32. 패스워드 잊어버렸습니다
패스워드를 잊은 경우에는 "로그인 할 수 있는 링크"를 이메일로 전송한다.
이메일로 전송된 링크를 클릭하면 로그인한다.
GET /email-login
> 이메일을 입력할 수 있는 폼을 보여주고, 링크 전송 버튼을 제공한다
POST /email-login
> 입력받은 이메일에 해당하는 계정을 찾아보고 존재하는 계정이면 로그인 가능한 링크를 이메일로 전송한다.
GET /login-by-email
> 토큰과 이메일을 확인한 뒤 해당 계정으로 로그인한다.
로직은 다음과 같다
- 가입된 이메일을 체크한 후 해당 이메일이 존재할 시에 "비밀번호 없이 로그인 할 수 있는 링크"를 토큰과 함께 발급하여 보내준다
- 해당 메일에 로그인하여 링크를 타고 들어가면 바로 로그인이 가능하며 새로운 패스워드로 변경하여 로그인한다.
Controll단 API 설계하기
emailLoginForm()
이메일을 입력할 수 있는 폼을 보여주고, 링크 전송 버튼을 제공한다
sendEmailLoginLink()
1) 이메일이 존재하는가
2) 토큰 생성한지 특정 시간이 지났는가 - 무작위 토큰 생성 방지 ( 테스트용으로 10초에 한 번 요청가능하게 설정 )
return this.emailCheckTokenGenerateAt.isBefore(LocalDateTime.now().minusSeconds(10));
두가지 조건을 만족할 시에 로그인 링크를 이메일로 전송한다
AccountController.java
/**
* 패스워드없이 로그인하기
* & 패스워드 재설정
*/
@GetMapping("/email-login")
public String emailLoginForm() {
return "account/email-login";
}
@PostMapping("/email-login")
public String sendEmailLoginLink(String email, Model model, RedirectAttributes attributes) {
Account account = accountRepository.findByEmail(email);
if (account == null) {
model.addAttribute("error", "유효한 이메일 주소가 아닙니다.");
return "account/email-login";
}
if (!account.canSendConfirmEmail()) {
model.addAttribute("error", "이메일 로그인은 1시간 뒤에 사용할 수 있습니다.");
return "account/email-login";
}
accountService.sendLoginLink(account);
attributes.addFlashAttribute("message", "이메일 인증 메일을 발송했습니다.");
return "redirect:/email-login";
}
@GetMapping("/login-by-email")
public String loginByEmail(String token, String email, Model model) {
Account account = accountRepository.findByEmail(email);
String view = "account/logged-in-by-email";
if (account == null || !account.isValidToken(token)) {
model.addAttribute("error", "로그인할 수 없습니다.");
return view;
}
accountService.login(account);
return view;
}
Service단에는 토큰을 생성하여 이메일 로그인 링크를 보내주는 메소드를 생성한다
AccountService.java
// 로그인 링크 보내기
public void sendLoginLink(Account account) {
account.generateEmailCheckToken();
SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setTo(account.getEmail());
mailMessage.setSubject("닷 스터디, 로그인 링크");
mailMessage.setText("/login-by-email?token=" + account.getEmailCheckToken() +
"&email=" + account.getEmail());
javaMailSender.send(mailMessage);
}
/login-by-email 접근권한 설정
SecurityConfig.java
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.mvcMatchers( ... , "/login-by-email").permitAll()
}
}
뷰 만들기
뷰는 총 3개를 만들어야한다.
1. 패스워드를 잃어버렸을 때 이메일을 입력하는 폼
email-login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments.html :: head"></head>
<body class="bg-light">
<div th:replace="fragments.html :: main-nav"></div>
<div class="container">
<div class="py-5 text-center">
<p class="lead">닷 스터디</p>
<h2>패스워드 없이 로그인하기</h2>
</div>
<div class="row justify-content-center">
<div th:if="${error}" class="alert alert-danger alert-dismissible fade show mt-3" role="alert">
<span th:text="${error}">완료</span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div th:if="${message}" class="alert alert-info alert-dismissible fade show mt-3" role="alert">
<span th:text="${message}">완료</span>
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<form class="needs-validation col-sm-6" action="#" th:action="@{/email-login}" method="post" novalidate>
<div class="form-group">
<label for="email">가입 할 때 사용한 이메일</label>
<input id="email" type="email" name="email" class="form-control"
placeholder="your@email.com" aria-describedby="emailHelp" required>
<small id="emailHelp" class="form-text text-muted">
가입할 때 사용한 이메일을 입력하세요.
</small>
<small class="invalid-feedback">이메일을 입력하세요.</small>
</div>
<div class="form-group">
<button class="btn btn-success btn-block" type="submit"
aria-describedby="submitHelp">로그인 링크 보내기</button>
<small id="submitHelp" class="form-text text-muted">
닷 스터디에 처음 오신거라면 <a href="#" th:href="@{/findpassword}">계정을 먼저 만드세요.</a>
</small>
</div>
</form>
</div>
<div th:replace="fragments.html :: footer"></div>
</div>
<script th:replace="fragments.html :: form-validation"></script>
</body>
</html>
2. 패스워드없이 로그인하는 링크로, 패스워드 설정링크 제공해주는 폼
logged-in-by-email.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments.html :: head"></head>
<body class="bg-light">
<nav th:replace="fragments.html :: main-nav"></nav>
<div class="container">
<div class="py-5 text-center" th:if="${error}">
<p class="lead">닷 스터디 이메일 로그인</p>
<div class="alert alert-danger" role="alert" th:text="${error}">
로그인 할 수 없습니다.
</div>
</div>
<div class="py-5 text-center" th:if="${error == null}">
<p class="lead">닷 스터디 이메일 로그인</p>
<h2>이메일로 로그인 했습니다. <a th:href="@{/settings/password}">패스워드를 변경</a>하세요.</h2>
</div>
</div>
</body>
</html>
실행과정
1. 로그인 화면에서 '패스워드 없이 로그인하기' 클릭
2. 이메일 입력하기
3. 이메일이 존재할 시 이메일 인증 발송 문구와 함께 이메일로 링크 전송
이메일 전송할 링크 (토큰, 이메일)
특정 기간안에 재전송할시 에러문구
4. 이메일로 제공받은 로그인링크 클릭하면 해당 폼으로 이동
5. 패스워드 변경
참고
'Dot Programming > Spring Clone' 카테고리의 다른 글
[스프링 웹앱 프로젝트 #42] PostgreSQL 설치 및 Spring 연동 (0) | 2021.04.28 |
---|---|
[스프링 웹앱 프로젝트 #33~41] 관심주제/ 지역정보 (Tag) 기능 (0) | 2021.04.23 |
[스프링 웹앱 프로젝트 #31] 닉네임 수정 (0) | 2021.03.09 |
[스프링 웹앱 프로젝트 #30] ModelMapper적용 (0) | 2021.03.08 |
[스프링 웹앱 프로젝트 #29] 알림 설정 (0) | 2021.03.05 |