Spring

Spring Boot Rest API 이메일 인증

mers 2021. 11. 10. 22:01

 이번 포스팅에서는 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에 저장하는 방식도 있다고 합니다. 

 해당 방식은 구현 시에 따로 포스팅 다루겠습니다.

 

 

 

 

참고링크


[1] Spring Boot 이메일 인증 구현하기