-
[이메일 인증] 이메일 인증 구현 & 코드 검증!Health-Genie 2023. 11. 18. 14:55728x90
배경
해당 프로젝트에서 이메일 인증 기능이 필요해졌다!
그래서 나는 Google SMTP 서버를 이용해서 이메일 인증을 진행하도록 했다.
개발 환경
- Java 17
- Spring 3.x
- Gradle
- MySQL
- IntelliJ
흐름
- 사용자는 이메일을 입력 후 이메일 인증 버튼 클릭
- 클라이언트 서버에게 사용자의 이메일로 인증 번호 전송 요청
- 서버는 랜덤 인증 번호 생성. 인증 번호를 auth code table에 저장 후 사용자의 이메일로 인증 번호 전송
- 사용자는 인증 번호 확인 후 인증 번호 입력 후 확인 버튼 클릭
- 클라이언트는 서버에게 인증 번호 검증 요청
- 서버는 전달받은 인증 번호가 auth code table에 저장된 인증 번호와 동일한지 확인 후 동일하면 true 반환
SMTP에서 사용할 구글계정을 등록하고, application.yml에 적어준다. [ 생략 ]
spring: mail: host: smtp.gmail.com port: 587 username: ENC(mN89mzWmJjg24UA4+WAfpRWd3ouJi+ncg0L6b9vQLCM=) password: ENC(OWlYfU6Qj8xrpbXmFILpeMG6/uaoOZR4fiZNb9Coov8=) properties: mail: smtp: auth: true starttls: enable: true required: true connectiontimeout: 5000 timeout: 5000 writetimeout: 5000 auth-code-expiration-millis: 1800000 # 30 * 60 * 1000 == 30분
가물가물해져서 헷갈릴 수 있는 부부은 가령 mysql에서는 빈번히 username을 root로 설정해서 여기서도 root를 작성하는 경우가 있는데, username 은 [아이디]@gmail.com 이런식으로 해줘야 한다.
참고로 ENC는 자바의 jasypt를 사용하여 암호화를 진행했다.
https://techj9972.tistory.com/174
build.gradle
implementation 'org.springframework.boot:spring-boot-starter-data-redis' implementation 'org.springframework.boot:spring-boot-starter-mail'
config
@Configuration public class EmailConfig { @Value("${spring.mail.host}") private String host; @Value("${spring.mail.port}") private int port; @Value("${spring.mail.username}") private String username; @Value("${spring.mail.password}") private String password; @Value("${spring.mail.properties.mail.smtp.auth}") private boolean auth; @Value("${spring.mail.properties.mail.smtp.starttls.enable}") private boolean starttlsEnable; @Value("${spring.mail.properties.mail.smtp.starttls.required}") private boolean starttlsRequired; @Value("${spring.mail.properties.mail.smtp.connectiontimeout}") private int connectionTimeout; @Value("${spring.mail.properties.mail.smtp.timeout}") private int timeout; @Value("${spring.mail.properties.mail.smtp.writetimeout}") private int writeTimeout; @Bean public JavaMailSender javaMailSender() { JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); mailSender.setHost(host); mailSender.setPort(port); mailSender.setUsername(username); mailSender.setPassword(password); mailSender.setDefaultEncoding("UTF-8"); mailSender.setJavaMailProperties(getMailProperties()); return mailSender; } private Properties getMailProperties() { Properties properties = new Properties(); properties.put("mail.smtp.auth", auth); properties.put("mail.smtp.starttls.enable", starttlsEnable); properties.put("mail.smtp.starttls.required", starttlsRequired); properties.put("mail.smtp.connectiontimeout", connectionTimeout); properties.put("mail.smtp.timeout", timeout); properties.put("mail.smtp.writetimeout", writeTimeout); return properties; } }
- JavaMailSender 인터페이스를 구현하는 클래스로, yml 파일에 설정한 환경 변수들을 사용해서 객체를 만들고, 해당 객체를 통해서 이메일을 보낼 수 있다 ( 따로 이메일 송신자를 체크 안해줘도 된다 )
controller
// 이메일 코드전송,이메일유효성검사 @PostMapping("/mail/send") public ResponseEntity authMail(@RequestBody String email) throws MessagingException { userMailService.sendCode(email); return new ResponseEntity("이메일이 성공적으로 보내쟜습니다", HttpStatus.OK); } //이메일 코드검증 @GetMapping("/mail/verify") public ResponseEntity validMailCode(@RequestParam("email") String email, @RequestParam("authCode") String authCode){ boolean result = userMailService.verify(email, authCode); if (result) { return new ResponseEntity("authcode is correct",HttpStatus.OK); } return new ResponseEntity("authcode is wrong ",HttpStatus.BAD_REQUEST); }
- sendCode() : 이메일 수신자에서 코드를 보내는 메소드
- verify() : 수신자에게 보낸 코드와 수신자가 작성한 코드가 맞는지 검증하는 메소드
service
UserMailService
@Transactional public EmailAuthResponseDto sendCode(String toEmail) throws MessagingException { String title = "Health Genie 이메일 인증 번호"; String authCode = this.createCode(); JsonObject jsonObject = new Gson().fromJson(toEmail, JsonObject.class); String email = jsonObject.get("email").getAsString(); mailService.sendEmail(email, title, authCode); // 이메일 인증 요청 시 인증 번호 DTO에 저장 EmailAuthCode code = EmailAuthCode.builder() .code(authCode) .email(email) .build(); EmailAuthCode savedCode = emailAuthRepository.save(code); return EmailAuthResponseDto.builder().id(savedCode.getId()).build(); } private String createCode() { int lenth = 8; try { Random random = SecureRandom.getInstanceStrong(); StringBuilder builder = new StringBuilder(); for (int i = 0; i < lenth; i++) { builder.append(random.nextInt(10)); } return builder.toString(); } catch (NoSuchAlgorithmException e) { log.debug("MemberService.createCode() exception occur"); throw new CommonException(CommonErrorResult.NO_SUCH_ALGORITHM); } } @Transactional public boolean verify(String email, String authCode) { EmailAuthCode code = getCodeByEmail(email); // email을 이용해 코드 가져오기 (예시 메서드) // 코드가 없으면 false 반환하거나 예외처리 if (code == null) { return false; } // DB에서 가져온 코드와 파라미터로 받은 코드(authCode)를 비교 return code.getCode().equals(authCode); } @Transactional(readOnly = true) public EmailAuthCode getCodeByEmail(String email) { return emailAuthRepository.findByEmail(email).orElseThrow(); }
- sendCode() : JSON 형태로 이메일을 받아서 Gson() 라이브러리를 통해서 email값을 string으로 얻고, authcode와 email 을 값을 DB에 저장해준다.
- createCode() : 8자리 랜덤 숫자를 생성 해준다
- verify() : DB에 있는 authcode와 사용자가 작성하는 code를 비교해서 boolean 값을 반환해준다
- EmailAuthCode() : 이메일을 통해서 해당 code값을 찾아준다
MailService
public void sendEmail(String toEmail, String title, String text) throws MessagingException { SimpleMailMessage emailForm = createEmailForm(toEmail, title, text); try { emailSender.send(emailForm); } catch (RuntimeException e) { log.debug("MailService.sendEmail exception occur toEmail: {}, " + "title: {}, text: {}", toEmail, title, text); throw new CommonException(CommonErrorResult.UNABLE_TO_SEND_EMAIL); } } // 발신할 이메일 데이터 세팅 private SimpleMailMessage createEmailForm(String toEmail, String title, String text) { SimpleMailMessage message = new SimpleMailMessage(); message.setTo(toEmail); message.setSubject(title); message.setText(text); return message; }
- createEmailForm() : 이메일 데이터를 세팅하고, SimpleMailMessage 객체를 생성해서 반환한다
- sendEmail() : 이메일 양식을 전달해준다
repository
Optional<EmailAuthCode> findByEmail(String email);
결과
인증 코드 보내기
코드 확인 인증 코드 검사하기
인증 코드 검사하기 'Health-Genie' 카테고리의 다른 글
[error] No enum constant enum명.값 해결방법 (2) 2023.11.21 [error] Could not write JSON: Infinite recursion (stackOverflowError) (0) 2023.11.19 [symbol 에러] error: cannot find symbol log.info (1) 2023.06.12 [UserDetail] InternalAuthenticationServiceException 에러 (0) 2023.06.04 [mail] Authentication Fail 문제 (0) 2023.06.04