Been_DevStep
Spring Boot - Recover Password (email certification) 본문
먼저 기본이 되는 HTML 소스 중 main 부분입니다.
<main class="--main main">
<form class="form" id="form">
<div class="title-container">
<h1 class="title">
비밀번호 재설정
</h1>
</div>
<table class="table">
<tbody>
<tr>
<th rowspan="2">이메일</th>
<td>
<label class="label email">
<span hidden>이메일</span>
<input class="--object-input input" maxlength="50" name="email"
placeholder="이메일 주소를 입력해 주세요." type="email">
</label>
</td>
</tr>
<tr>
<td>
<input class="--object-button" type="button"
value="인증하기" name="emailSend">
</td>
</tr>
<tr class="message-row" rel="messageRow">
<th></th>
<td>
<span class="message">
<i class="fa-solid fa-hourglass icon"></i>
<span class="text">입력하신 이메일로 인증을 진행할 수 있는 링크를 전송하였습니다. 해당 링크 확인 후 해당 페이지로 돌아와 주세요.</span>
</span>
</td>
</tr>
<!--querySelector(rel) 은 반드시 하나만 선택한다!-->
<tr class="password-row" rel="passwordRow">
<th>새로운 비밀번호</th>
<td>
<label class="label password">
<span hidden>비밀번호</span>
<input class="--object-input input" maxlength="50" name="password"
placeholder="새로운 비밀번호를 입력해 주세요." type="password">
</label>
<label class="label password">
<span hidden>비밀번호 재입력</span>
<input class="--object-input input" maxlength="50" name="passwordCheck"
placeholder="비밀번호를 한번더 입력해 주세요." type="password">
</label>
<input class="--object-button" type="button"
value="비밀번호 재설정하기" name="passwordUpdate">
</td>
</tr>
<tr class="warning-row" rel="warningRow">
<th></th>
<td>
<span class="warning">
<i class="fa-solid fa-triangle-exclamation icon"></i>
<span class="text"></span>
</span>
</td>
</tr>
</tbody>
</table>
<input name="code" type="hidden">
<input name="salt" type="hidden">
</form>
</main>
비밀번호 찾기는 이메일 인증될 경우 js를 통해서 보여지게 됩니다.
<input name="code" type="hidden">
<input name="salt" type="hidden">
이 코드는 이메일이 인증되었다면 해당 이메일을 통해 email_auths 테이블에서 code와 salt를 가져와서 입력해줍니다.
하지만 사용자가 임의로 입력하는것이 아니기 때문에 hidden으로 숨겨줍니다.
먼저 이메일 인증과정입니다.
js에서 인증하기 버튼을 누를 경우에 적용는 동작입니다.
form['emailSend'].addEventListener('click', () => {
Warning.hide();
if (form['email'].value === '') {
Warning.show('이메일을 입력해주세요.');
form['email'].focus();
return;
}
Cover.show('계정을 확인하고 있습니다. \n 잠시만 기다려주세요.');
const xhr = new XMLHttpRequest();
const formData = new FormData();
formData.append('email', form['email'].value);
xhr.open('POST', './recoverPassword');
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
Cover.hide();
if (xhr.status >= 200 && xhr.status < 300) {
const responseObject = JSON.parse(xhr.responseText);
switch (responseObject['result']) {
case 'success' :
emailAuthIndex = responseObject['index'];
form['email'].setAttribute('disabled', 'disabled');
form['emailSend'].setAttribute('disabled', 'disabled');
form.querySelector('[rel="messageRow"]').classList.add('visible');
break;
default:
Warning.show("해당 이메일을 사용하는 계정을 찾을수 없습니다.");
form['email'].focus();
form['email'].select();
}
} else {
Warning.show('서버와 통신하지 못하였습니다. 잠시 후 다시 시도해 주세요.');
}
}
};
xhr.send(formData);
});
이에 해당하는 Controller
@RequestMapping(value = "recoverPassword",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public String postRecoverPassword(EmailAuthEntity emailAuth) throws MessagingException {
Enum<?> result = this.memberService.recoverPasswordSend(emailAuth);
JSONObject responseObject = new JSONObject();
responseObject.put("result", result.name().toLowerCase());
if (result == CommonResult.SUCCESS)
responseObject.put("index", emailAuth.getIndex());
return responseObject.toString();
}
Service
@Transactional // 이메일 보내기 등등... 중간에 작업이 취소된다면 insert 철회를 위한 구문
public Enum<? extends IResult> recoverPasswordSend(EmailAuthEntity emailAuth) throws MessagingException {
UserEntity existingUser = this.memberMapper.selectUserByEmail(emailAuth.getEmail());
if (existingUser == null) return CommonResult.FAILURE;
String authCode = RandomStringUtils.randomNumeric(6);
String authSalt = String.format("%s%s%f%f",
emailAuth.getEmail(), authCode, Math.random(), Math.random());
authSalt = CryptoUtils.hashSha512(authSalt);
Date createdOn = new Date();
Date expiresOn = DateUtils.addMinutes(createdOn, 5);
emailAuth.setEmail(emailAuth.getEmail());
emailAuth.setCode(authCode);
emailAuth.setSalt(authSalt);
emailAuth.setCreatedOn(createdOn);
emailAuth.setExpiresOn(expiresOn);
emailAuth.setExpired(false);
if(this.memberMapper.insertEmailAuth(emailAuth) == 0)
return CommonResult.FAILURE;
Context context = new Context();
//Service에서 html파일에 접근하기 위해서 Context를 사용한다.
context.setVariable("code", emailAuth.getCode());
context.setVariable("email", emailAuth.getEmail());
context.setVariable("salt", emailAuth.getSalt());
//template에 만들어둔 email html파일 경로
String text = this.templateEngine.process("member/recoverPasswordEmailAuth", context);
MimeMessage mail = this.mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mail, "UTF-8");
helper.setFrom("wjscdksqls@gmail.com");
helper.setTo(emailAuth.getEmail());
helper.setSubject("[스터디] 비밀번호 재설정 인증 링크");
helper.setText(text, true);
this.mailSender.send(mail);
return CommonResult.SUCCESS;
}
위코드에서
String text = this.templateEngine.process("member/recoverPasswordEmailAuth", context);
는 새롭게 html을 만들어서 연결시켜준 코드입니다.
UserEntity existingUser = this.memberMapper.selectUserByEmail(emailAuth.getEmail());
입력한 이메일이 존재하는지 DB에 검색해서 existingUser에 담아줍니다.
만약 email이 없다면
if (existingUser == null) return CommonResult.FAILURE;
을 통해서 구문을 끝내줍니다.
존재한다면 email-auths에 맞게 setting을 한 뒤
emailAuth.setEmail(emailAuth.getEmail());
emailAuth.setCode(authCode);
emailAuth.setSalt(authSalt);
emailAuth.setCreatedOn(createdOn);
emailAuth.setExpiresOn(expiresOn);
emailAuth.setExpired(false);
if(this.memberMapper.insertEmailAuth(emailAuth) == 0)
return CommonResult.FAILURE;
DB에 담아줍니다.
Context context = new Context();
//Service에서 html파일에 접근하기 위해서 Context를 사용한다.
context.setVariable("code", emailAuth.getCode());
context.setVariable("email", emailAuth.getEmail());
context.setVariable("salt", emailAuth.getSalt());
//template에 만들어둔 email html파일 경로
String text = this.templateEngine.process("member/recoverPasswordEmailAuth", context);
MimeMessage mail = this.mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mail, "UTF-8");
helper.setFrom("wjscdksqls@gmail.com");
helper.setTo(emailAuth.getEmail());
helper.setSubject("[스터디] 비밀번호 재설정 인증 링크");
helper.setText(text, true);
this.mailSender.send(mail);
구문을 통해서 정해진 메일을 발송해줍니다.
인증 이메일을 보내고 인증 결과를 기다리기 위해서 js에서 SetInterval을 사용해줍니다.
let emailAuthIndex = null;
setInterval(() => {
if (emailAuthIndex == null) return;
const xhr = new XMLHttpRequest();
const formData = new FormData();
formData.append('index', emailAuthIndex);
xhr.open('POST', './recoverPasswordEmail');
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status >= 200 && xhr.status < 300) {
const responseObject = JSON.parse(xhr.responseText);
console.log(responseObject);
switch (responseObject['result']) {
case 'success':
form['code'].value = responseObject['code'];
form['salt'].value = responseObject['salt'];
form.querySelector('[rel="messageRow"]').classList.remove('visible');
emailAuthIndex = null;
form.querySelector('[rel="passwordRow"]').classList.add('visible');
break;
default:
}
}
}
};
xhr.send(formData);
}, 1000);
setInterval(f, t)은 t밀리초마다 f함수를 실행하게 된다.
인증을 할 때까지 위 화면상태를 유지합니다.
이메일에서 인증을 하게되면 아래 구문을 통해 index값을 받아와서
emailAuthIndex = responseObject['index'];
emailAuthIndex 가 가르키는 값이 null이 아니라 받아온 값으로 셋팅되기 때문에 함수가 작동하고 함수 내에서
emailAuthIndex = null;
통해서 더이상 함수가 작동하지 않고 return 되게 되면서 이메일 인증 작업이 완료됩니다.
'공부 > SpringBoot' 카테고리의 다른 글
SpringBoot - 사용한 Maven 목록 (0) | 2022.11.11 |
---|---|
Spring Boot - Recover Password (0) | 2022.11.10 |
Spring Boot 회원 가입 (0) | 2022.11.08 |
Spring Boot 이메일 인증(3) - 인증 확인 (0) | 2022.11.08 |
Spring Boot 이메일 인증(2) - 실제 메일 보내기 (0) | 2022.11.07 |
Comments