이번 포스팅에서는 REST API로 스프링 부트 이메일 인증을 해보겠습니다.
인증 번호를 받아서 입력하는 것이 아닌 URL 클릭 방식으로 하겠습니다.
1. https://www.google.com/settings/security/lesssecureapps 보안 수준 낮은 앱의 액세스 허용
2. 의존성 추가
// 이메일 인증
implementation 'org.springframework.boot:spring-boot-starter-mail'
3. application.yml 설정
spring:
mail:
host: smtp.gmail.com
port: 587
username: <gmail 계정>
password: <gmail 패스워드>
properties:
mail:
smtp:
auth: true
starttls:
enable: true
required: true
connectiontimeout: 5000
timeout: 5000
writetimeout: 5000
4. EmailToken 테이블 생성
- id는 primary key로 설정합니다.
5. EmailToken.java
- JPA에 사용될 엔티티 입니다.
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class EmailToken {
private static final long EMAIL_TOKEN_EXPIRATION_TIME_VALUE = 5L; // 이메일 토큰 만료 시간
@Id
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Column(length = 36)
private String id;
private LocalDateTime expirationDate;
private boolean expired;
private Long memberId;
// 이메일 인증 토큰 생성
public static EmailToken createEmailToken(Long memberId) {
EmailToken emailToken = new EmailToken();
emailToken.expirationDate = LocalDateTime.now().plusMinutes(EMAIL_TOKEN_EXPIRATION_TIME_VALUE); // 5분 후 만료
emailToken.expired = false;
emailToken.memberId = memberId;
return emailToken;
}
// 토큰 만료
public void setTokenToUsed() {
this.expired = true;
}
}
6. EmailTokenRepository.java
- 이메일 토큰, 유효정보를 저장할 Repository 입니다.
- 추가적으로 쿼리함수를 하나 만들어줍니다.
( 토큰 ID를 찾는데, 만료되지 않았으며 현재보다 이후에 만료되는 토큰정보를 가져옵니다. )
public interface EmailTokenRepository extends JpaRepository<EmailToken, String> {
Optional<EmailToken> findByIdAndExpirationDateAfterAndExpired(String emailTokenId, LocalDateTime now, boolean expired);
}
7. EmailSenderService.java
- 이메일을 전송하는 서비스입니다. 이 경우 비동기로 구현합니다.
@Service
@RequiredArgsConstructor
public class EmailSenderService {
private final JavaMailSender javaMailSender;
@Async
public void sendEmail(SimpleMailMessage email) {
javaMailSender.send(email);
}
}
8. EmailTokenService.java
- 토큰과 관련된 서비스입니다, 토큰 생성 및 유효토큰을 가져오는 메소드가 있습니다.
@Slf4j
@RequiredArgsConstructor
@Service
public class EmailTokenService {
private final EmailSenderService emailSenderService;
private final EmailTokenRepository emailTokenRepository;
// 이메일 인증 토큰 생성
public String createEmailToken(Long memberId, String receiverEmail) {
Assert.notNull(memberId, "memberId는 필수입니다");
Assert.hasText(receiverEmail, "receiverEmail은 필수입니다.");
// 이메일 토큰 저장
EmailToken emailToken = EmailToken.createEmailToken(memberId);
emailTokenRepository.save(emailToken);
// 이메일 전송
SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setTo(receiverEmail);
mailMessage.setSubject("회원가입 이메일 인증");
mailMessage.setText("http://[서버주소]/confirm-email?token="+emailToken.getId());
emailSenderService.sendEmail(mailMessage);
return emailToken.getId(); // 인증메일 전송 시 토큰 반환
}
// 유효한 토큰 가져오기
public EmailToken findByIdAndExpirationDateAfterAndExpired(String emailTokenId) throws BaseException {
Optional<EmailToken> emailToken = emailTokenRepository
.findByIdAndExpirationDateAfterAndExpired(emailTokenId, LocalDateTime.now(), false);
// 토큰이 없다면 예외 발생
return emailToken.orElseThrow(() -> new BaseException(BaseResponseStatus.DATABASE_ERROR));
}
}
9. EmailController.java
- 인증 URL이 담긴 이메일을 전송받은 사용자가 URL 클릭 시 해당 URI로 매핑됩니다. (confirm-email)
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping
public class EmailController {
private final EmailService emailService;
@GetMapping("/confirm-email")
public BaseResponse<Boolean> viewConfirmEmail(@Valid @RequestParam String token) {
try {
boolean result = emailService.verifyEmail(token);
return new BaseResponse<>(result);
} catch (BaseException exception) {
return new BaseResponse<>(exception.getStatus());
}
}
}
10. EmailService.java
- 이메일 토큰 검증하는 메소드입니다.
- TODO 아래 부분운 작성한 예시코드입니다.
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class EmailService {
private final EmailTokenService emailTokenService;
@Override
@Transactional
public boolean verifyEmail(String token) throws BaseException {
// 이메일 토큰을 찾아옴
EmailToken findEmailToken = emailTokenService.findByIdAndExpirationDateAfterAndExpired(token);
// TODO: 여기서부터는 이메일 성공 인증 로직을 구현합니다.
// 지금 예시는 유저의 인증내용 변경하는 방법입니다.
// 토큰의 유저 ID를 이용하여 유저 인증 정보를 가져온다.
Optional<Member> findMember = memberRepository.findById(findEmailToken.getMemberId());
findEmailToken.setTokenToUsed(); // 사용 완료
if (findMember.isPresent()) {
Member member = findMember.get();
member.setVerified();
return true;
} else {
throw new BaseException(DATABASE_ERROR); // TODO: 토큰 에러
}
}
}
11. 결과
- 이메일 인증 메일은 아래와 같이 표시됩니다.
- token값이 DB에 저장이 됩니다.
현재는 토큰값을 DB에 저장하는 형태였는데, 이를 개선하여 redis에 저장하는 방식도 있다고 합니다.
해당 방식은 구현 시에 따로 포스팅 다루겠습니다.
참고링크
'Spring' 카테고리의 다른 글
Spring Boot + Nuxt.js 환경에서의 FCM 웹 푸시 구현 (1/2) - Spring Boot편 (0) | 2023.04.15 |
---|---|
[Mybatis] Mybatis에서 DTO로 분리하기 (0) | 2023.03.16 |
@Transactional Annotation 정리 (0) | 2022.09.13 |
빈 등록 어노테이션 @Configuration, @Component, @Bean에 대해서 (2) | 2022.08.27 |
세션 vs JWT (0) | 2022.06.21 |