아이디(이메일인증 통한) 찾기 및 비밀번호 찾기
들어가며
이번에는 회원가입을 했을때 아이디나 비밀번호가 기억이 나지않았을때 찾을수있는 기능을 구현해볼 예정입니다. 아이디는 보인인증을 통해서 찾으며 비밀번호는 찾은 아이디와 이름을 통해서 찾아볼 계횝입니다. 또한 찾은 비밀번호를 url에서는 암호화해서 표시하도록 해보겠습니다.
Reposiotry
Repository를 작성해야합니다. 저번에 LoginRepositoryImpl를 구현했지만 이번에는 LoginFindRepositoryImpl를 구현해볼 예정입니다. 그래서 LoginRepository 를 상속받아서 새로 메소드를 정의해서 만들어보겠습니다.
LoginFindRepositoryImpl
@Repository
@RequiredArgsConstructor
public class LoginFindRepositoryImpl implements LoginRepository{
private final EntityManager em;
@Override
public List<Member> findById(String email) {
String jpql = "select m from Member m where m.email=:email";
List<Member> loginId = em.createQuery(jpql, Member.class).setParameter("email", email).getResultList();
return loginId;
}
@Override//사용안함
public List<Member> findBPwd(String pwd) {
return null;
}
public List<Member> findPwd(String username, String loginId) { //재정의
String jpql = "select m from Member m where m.username=:username and m.loginId=:loginId";
List<Member> password = em.createQuery(jpql, Member.class)
.setParameter("username", username)
.setParameter("loginId", loginId).getResultList();
return password;
}
}
- findById(String email) : 이메일을 통해서 아이디를 찾기
- findPwd(String username, String loginId) : 이름과 로그인 아이디를 통해서 비밀번호를 찾기
Service
LoginService
public List<String> findById(String email){
List<String> loginIds = new ArrayList<>();
try{
List<Member> ids = loginFindRepository.findById(email);
for (Member id : ids) {
loginIds.add(id.getLoginId());
}
}catch (IndexOutOfBoundsException e){
e.printStackTrace();
}
return loginIds;
}
public String findPassword(String username, String loginId){
List<Member> pwd = loginFindRepository.findPwd(username, loginId);
if(pwd.isEmpty()){
return null;
}
return pwd.get(0).getPassword1();
}
기존에 있던 LoginService에 아이디찾기와 비밀번호 찾기 기능을 추가해줍니다. 아이디는 한 이메일로 중복 가입이 가능하다는 전재를 두기때문에 반환을 List로 했습니다. 하지만 비밀번호는 한 아이디에 비밀번호는 하나이기 때문에 반환을 String타입으로 반환하게 했습니다.
Test
이제는 앞서 만들었던 기능이 정상적으로 작동되는지 테스트를 해보겠습니다. 테스트를 할때는 test폴더에도 main과 동일한 경로로 패키지를 구성하거나 아니면 테스트하고자하는 클래스에서 Ctrl+Shift+T 를 통해서 테스트를 쉽게 만들수있습니다.
LoginServiceTest
@SpringBootTest
@Transactional
class LoginServiceTest {
@Autowired MemberRepository memberRepository;
@Autowired LoginRepositoryImpl loginRepository;
@Autowired LoginService loginService;
@Test
void find(){
Member member = Member.builder()
.loginId("test")
.username("testAdmin")
.password1("1234")
.password2("1234")
.email("test@naver.com")
.role(Role.admin)
.build();
memberRepository.save(member);
List<String> byId1 = loginService.findById("test@naver.com");
String password = loginService.findPassword("testAdmin", "test");
assertThat(byId1.get(0)).isEqualTo("test");
assertThat(password).isEqualTo("1234");
}
}
- assertThat : import static org.assertj.core.api.Assertions.*; 를사용했다.
실행을 하게되면 아래와 같이 초록색불이 들어와 작성한 기능이 정상적으로 실행되는것을 볼수있다.
Controller
이메일 인증을 통한 아이디찾기
LoginController
@GetMapping("/findId")
public String findId(@ModelAttribute("member") MemberDto memberDto){
return "login/help/findId";
}
@PostMapping("/findId")
public String findIdPost(MemberDto memberDto , BindingResult result, RedirectAttributes redirectAttributes){
if(result.hasErrors()){
return "login/help/findId";
}
List<String> findIds = loginService.findById(memberDto.getEmail());
redirectAttributes.addAttribute("findIds",findIds);
return "redirect:/findId/findIdCom";
}
@GetMapping("/findId/findIdCom")
public String findIdCom(@RequestParam("findIds") List<String> findIds, Model model){
model.addAttribute("findIds",findIds);
return "login/help/findIdCom";
}
- RedirectAttributes : 찾은 아이디 값을 findIdCom으로 넘겨주기 위해서 사용한다. RedirectAttributes 을 사용할때는 맞는 쪽에서 @ResquestParam을 사용해야한다.
Mapping를 통해서 Controller을 설정해준다.
이메일 인증 API
@PostMapping("/login/mailConfirm")
@ResponseBody
public String mailConfirm(@RequestParam("email") String email, HttpSession session) throws Exception {
String code = mailService.sendSimpleMessage(email);
System.out.println("인증코드 : " +code);
session.setAttribute("code", code);
return code;
}
이메일 인증을 통해서 아아디를 찾을수있기때문에 API를 만들어야한다. 이전시간에도 회원가입 페이지에 사용했던 API를 사용했습니다.
비밀번호 찾기
비밀번호 찾기는 아이디와 이름을 통해서 비밀번호 찾을수 있으면 URL에는 암호화하여 비밀번호를 표시하게 할수 있게 할 예정입니다.
LoginController
@GetMapping("/findPwd")
public String findPwdGet(@ModelAttribute("member")MemberDto memberDto){
return "login/help/findPwd";
}
@PostMapping("/findPwd")
public String findPwdPost(MemberDto memberDto,RedirectAttributes redirectAttributes){
String password = loginService.findPassword(memberDto.getUsername(), memberDto.getLoginId());
if(password != null) {
String encryptedPassword = Base64.getEncoder().encodeToString(password.getBytes(StandardCharsets.UTF_8));
redirectAttributes.addAttribute("password",encryptedPassword);
}else if (password==null) {
redirectAttributes.addAttribute("password",null);
}
return "redirect:/findPwd/findPwdCom";
}
@GetMapping("/findPwd/findPwdCom")
public String findIdCom(@RequestParam(value = "password", required = false) String encryptedPassword, Model model){
if(encryptedPassword != null){
String originalPassword = new String(Base64.getDecoder().decode(encryptedPassword), StandardCharsets.UTF_8);
model.addAttribute("password",originalPassword);
}else if (encryptedPassword==null){
model.addAttribute("password",null);
}
return "login/help/findPwdCom";
}
- Base64 : 8비트 바이너르 데이터를 7비트의 ASCII문자로 표현한다.
- .getDecoder().encodeToString(password.getBytes(StandardCarsets.UTF_8) : 입력한 비밀번호를 URL표시할때 암호화 한다.
- .getDecoder().decode(encryptedPassword), StandardCarsets.UTF_8 : 암호화된 비밀번호를 복호화한다.
HTML
아이디 찾기 html
findId.html
<body>
<h2>
아이디 찾기
</h2>
<form role="form" action="/findId" th:object="${member}" method="post" >
<div class="form-group">
<label th:for="email" id="mailTxt">이메일 입력해주세요</label>
<input type="text" th:field="*{email}" class="form-control" required>
<button class="btn btn-outline-primary" type="button" id="checkEmail">인증번호</button>
</div>
<div class="form-group">
<label for="memailconfirm" id="memailconfirmTxt">인증번호를 입력해 주세요</label>
<input type="text" class="form-control" id="memailconfirm">
</div>
<button type="submit" id="findButton">찾기</button>
</form>
</body>
html을 작성해준다 이제 script를 작성해야하는데 찾기버튼을 눌렀을때 인증을 해야 넘어가며, 인증번호 전송을 위한 스크립트를 작성한다.
스크립트
$(document).ready(function() {
var $checkEmail = $("#checkEmail");
var $email = $("#email");
var $memailconfirm = $("#memailconfirm");
var $memailconfirmTxt = $("#memailconfirmTxt");
var $findButton = $("#findButton");
var isCodeSent= "";
$checkEmail.click(function () {
$.ajax({
type: "POST",
url: "/login/mailConfirm",
data: {
"email": $email.val()
},
success: function (data) {
alert("해당 이메일로 인증번호 발송이 완료되었습니다. 확인 해주시기 바랍니다.");
console.log("data: " + data);
isCodeSent=data;
chkEmailConfirm(isCodeSent, $memailconfirm, $memailconfirmTxt);
}
});
});
function chkEmailConfirm(data, $memailconfirm, $memailconfirmTxt) {
$memailconfirm.on("keyup", function () {
if (data === "") {
emconfirmchk = false;
alert("인증번호를 먼저 전송해주세요.");
$("#emconfirmchk").css({
"color": "#FA3E3E",
"font-weight": "bold",
"font-size": "10px"
});
} else if(data !== $memailconfirm.val()) {
emconfirmchk = false;
$memailconfirmTxt.html("<span id ='emconfirmchk'>인증번호가 잘못되었습니다.</span>");
$("#emconfirmchk").css({
"color": "#FA3E3E",
"font-weight": "bold",
"font-size": "10px"
});
}else {
emconformchk =true;
$memailconfirmTxt.html("<span id = 'emconfirmchk'>인증완료</span>");
$("#emconfirmchk").css({
"color": "#0D6EFD",
"font-weight": "bold",
"font-size": "10px"
});
}
});
}
$findButton.on("click", function (event) {
var hasError = false;
if ($email.val().trim() === "") {
$("#mailTxt").css("color", "red");
hasError = true;
} else {
$("#mailTxt").css("color", "");
}
if(isCodeSent === ""){
$("#memailconfirmTxt").css("color", "red");
hasError=true;
$memailconfirmTxt.html("<span id ='emconfirmchk'>인증번호를 먼저 전송해주세요.</span>");
}else if($memailconfirm.val().trim()===""){
$("#memailconfirmTxt").css("color","red");
$memailconfirmTxt.html("<span id ='emconfirmchk'>인증번호를 입력해주세요</span>");
hasError=true;
}else if($memailconfirm.val() !== isCodeSent){
$("#memailconfirmTxt").css("color", "red");
hasError=true;
}
if (hasError) {
event.preventDefault();
}
});
});
이제는 아이디를 찾으면 보여주는 페이지를 작성한다.
findIdCom.html
<div>
<h1>아이디 찾기 결과</h1>
<p>찾은 아이디는 다음과 같습니다.</p>
<li th:each="findId: ${findIds}" th:text="${findId}" text="일치하는 아이디가 없습니다."> </li>
<li th:if="${findIds.isEmpty()}">일치하는 아이디가 없습니다.</li>
<button class="w-100 btn btn-secondary btn-lg" onclick="location.href='/members/help/findPwd'" th:onclick="|location.href='@{/findPwd}'|" type="button">비밀번호찾기</button>
<button class="w-100 btn btn-secondary btn-lg" onclick="location.href='/home.html'" th:onclick="|location.href='@{/}'|" type="button">메인페이지로</button>
<button class="btn" onclick="location.href='/members/login.html'" th:onclick="|location.href='@{/login}'|" type="button">로그인 하러가기</button>
</div>
여러개의 아이디를 보여줄수있어서 each문으로 돌려서 아이디를 표시하게 해줍니다.
비밀번호 찾기 html
findPwd.html
<body>
<h2>
비밀번호 찾기
</h2>
<form role="form" action="/findPwd" th:object="${member}" method="post">
<div class="form-group">
<label th:for="username">이름</label>
<input type="text" th:field="*{username}" class="form-control">
</div>
<div class="form-group">
<label th:for="loginId">아이디</label>
<input type="text" th:field="*{loginId}">
</div>
<button type="submit" >찾기</button>
</form>
</body>
비밀번호를 보여주는 페이지 작성
findPwdCom.html
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>비밀번호 찾기 결과</title>
</head>
<body>
<div>
<h1>비밀번호 찾기 결과</h1>
<p>찾은 비밀번호는 다음과 같습니다.</p>
<p th:if="${password != null}">
찾은 비밀번호는 <span style="color: #dc3545; font-weight: bold" th:text="${password}"></span> 입니다.
</p>
<p th:if="${password == null}" th:text="'일치하는 비밀번호가 없습니다.'" style="color: #dc3545; font-weight: bold"></p>
<button class="w-100 btn btn-secondary btn-lg" onclick="location.href='/members/help/findPwd'" th:onclick="|location.href='@{/findPwd}'|" type="button">비밀번호찾기</button>
<button class="w-100 btn btn-secondary btn-lg" onclick="location.href='/home.html'" th:onclick="|location.href='@{/}'|" type="button">메인페이지로</button>
<button class="btn" onclick="location.href='/members/login.html'" th:onclick="|location.href='@{/login}'|" type="button">로그인 하러가기</button>
</div>
</body>
</html>
실행
아이디 찾기
인증을 하지 않으면 오류메시지로 표시하며 인증을 성공해야지만 아이디를 찾을수있다.
비밀번호 찾기
비밀번호을 찾으면 URL에 암호화된 상태로 표시되는 것을 볼수있다.
다음으로
다음 포스팅부터는 상품등록을 하는 것을 개발해보겠습니다.
'ToyProject쇼핑몰' 카테고리의 다른 글
[스프링 부트] JPA 쇼핑몰 # 11. 상품등록 (0) | 2023.11.05 |
---|---|
[스프링 부트] JPA 쇼핑몰 # 9.세션을 이용한 로그인 및 로그아웃 (9) | 2023.10.28 |
[스프링 부트] JPA 쇼핑몰 # 8.로그인 레이아웃 (0) | 2023.10.27 |
[스프링 부트] JPA 쇼핑몰 # 7.회원가입 실행해보기 (0) | 2023.10.27 |
[스프링 부트] JPA 쇼핑몰 # 6.회원가입 주소 (0) | 2023.10.27 |