web 개발/auth

naver mail을 이용한 회원가입

잼추 2024. 6. 23. 22:55

성공 화면!!

 

 

전체 회원가입 과정

  1. 정보 입력받기
  2. 중복 형식 테스트 → 실패 시 리턴
  3. 토큰 발행, 토큰도 객체에 넣어서 레디스에 임시저장
  4. 메일 전송(토큰 포함된 링크)
  5.  사용자 메일 입력

  6. 메일 회신에서 온 토큰과 레디스에서 뽑은 토큰 일치 확인 → 불일치 시 리턴
  7. 토큰 일치 시 레디스 정보 DB 저장
  8. 회원가입 완료!!


1. bulid.gradle 에 의존성 입력

    //naver mail
    implementation 'org.springframework.boot:spring-boot-starter-mail:3.0.5'
    implementation 'com.sun.mail:jakarta.mail:2.0.1'

 

2. 메일 config 파일 작성

@Configuration
@PropertySource("classpath:application.yml")
@ConfigurationProperties(prefix = "spring.mail")
@Getter
@Setter
@ToString
@Slf4j
public class MailConfig {
    private String username;
    private String password;
    private int port;
    private Properties properties;
    @Bean
    public JavaMailSender javaMailService() {
        JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
//
        javaMailSender.setHost("smtp.naver.com");
        javaMailSender.setUsername(username); // 네이버 아이디
        javaMailSender.setPassword(password); // 네이버 비밀번호


        javaMailSender.setPort(port); // 메일 인증서버 포트
        javaMailSender.setJavaMailProperties(properties);

        return javaMailSender;
    }
}

 

yml 파일도 작성

mail:
  host: smtp.naver.com
  port: 465
  username: ${SPRING_MAIL_USERNAME}
  password: ${SPRING_MAIL_PASSWORD}
  properties:
    mail:
      smtp:
        auth: true
        starttls:
          enable: true
        ssl:
          enable: true
          trust: smtp.naver.com
      debug: true

 

3. controller 작성

@Slf4j
@RestController
@RequestMapping("/api/auth/mail/naver")
@RequiredArgsConstructor
public class MailController {
    private final MailService mailService;
    private final RedisService redisService;
    private final MemberService memberService;

    //회원가입
    @PostMapping("/signup")
    public ResponseEntity<ApiResponse<?>> signup(@RequestBody MemberReqDtoByMail memberReqDtoByMail) throws Exception {
        //email 형식 인증 추가
        if(!memberService.checkEmailForm(memberReqDtoByMail.getEmail())){
            return ResponseEntity
                    .status(HttpStatus.UNAUTHORIZED)
                    .body(ApiResponse.error(ErrorType.INVALID_EMAIL_FORM));
        }

        if(!memberService.checkDuple(memberReqDtoByMail.getEmail())){
            return ResponseEntity
                    .status(HttpStatus.UNAUTHORIZED)
                    .body(ApiResponse.error(ErrorType.INVALID_EMAIL_DUPLE));
        }

        String token = UUID.randomUUID().toString();
        memberReqDtoByMail.setToken(token);

        //redis 저장
        redisService.setValues(memberReqDtoByMail.getEmail(), memberReqDtoByMail);

        //메일 전송
        mailService.sendMail(token, memberReqDtoByMail.getEmail());

        return ResponseEntity
                .status(HttpStatus.OK)
                .body(ApiResponse.success(SuccessType.SEND_EMAIL, SuccessType.SEND_EMAIL.getMessage()));
    }


    //토큰 검증 및 인증
    @GetMapping("/verify")
    public ResponseEntity<ApiResponse<Object>> verifyEmail( @RequestParam String email, String token){
        //토큰 일치시 DB 저장
        if(mailService.checkToken(email, token)){
            SimpleMemberRespDto response = memberService.addMember(redisService.getValues(email, MemberReqDtoByMail.class));
            return ResponseEntity
                    .status(HttpStatus.OK)
                    .body(ApiResponse.success(SuccessType.SUCCESS_CREATE, response));
        }else{
            return ResponseEntity
                    .status(HttpStatus.UNAUTHORIZED)
                    .body(ApiResponse.error(ErrorType.INVALID_TOKEN,  ErrorType.INVALID_TOKEN.getMessage()));
//
        }
    }
}

 

4. service 작성

@Service
@RequiredArgsConstructor
public class MailService {

    private final JavaMailSender javaMailSender;
    private final RedisService redisService;

    private MimeMessage createMessage(String token, String email) throws MessagingException, UnsupportedEncodingException {
        MimeMessage message = javaMailSender.createMimeMessage();
        message.addRecipients(MimeMessage.RecipientType.TO, email);// 보내는 대상
        String verificationUrl = "http://localhost:8080/api/auth/mail/naver/verify?email=" + email + "&token=" + token;

        message.setSubject("Artify 회원가입 이메일 인증");// 제목

        String body = "<div>"
                + "<h1> 안녕하세요. 인증메일 입니다</h1>"
                + "<br>"
                + "<p>아래 링크를 클릭하면 이메일 인증이 완료됩니다.<p>"
                + "<a href='"+ verificationUrl+ "'>인증 링크</a>"
                + "</div>";

        message.setText(body, "utf-8", "html");// 내용, charset 타입, subtype
        // 보내는 사람의 이메일 주소, 보내는 사람 이름
        message.setFrom(new InternetAddress("jjhhh6845@naver.com", "Artify_Admin"));// 보내는 사람
        return message;
    }

    public void sendMail(String token, String email) throws MessagingException, UnsupportedEncodingException {
        MimeMessage mimeMessage = createMessage(token, email);
        javaMailSender.send(mimeMessage);
    }


    public boolean checkToken(String email, String token) {
        MemberReqDtoByMail dto = redisService.getValues(email, MemberReqDtoByMail.class);
        String originToken = (dto.getToken());
        if(token.equals(originToken)){
            return true;
        }else{
            return false;
        }
    }


}

 

 

고민 사항들

  1. 토큰 발급 후 다시 돌아올 때 까지 어디에 저장 할 것 인가
    → 레디스에 임시 저장함
    → 토큰만 저장, 모든 내용 저장 
    → 직렬화, 역직렬화 필요(object Mapper)
        public void setValues(String key, Object data) throws JsonProcessingException {
            String json = objectMapper.writeValueAsString(data);
            ValueOperations<String, Object> values = redisTemplate.opsForValue();
            values.set(key, json);
        }
    


  2. 25 port  → 메일 port 및 설정 오류
  3. 아이디 비번 오류(실제로 일치함) → 해당 서비스 허용 안함
  4. java mail sender 가 인터페이스인데 생성자 주입이 되는가
    → impl 로 객체 주입 해주고 각자 세팅해준다음 @bean으로 등록 해주면 DI 완료

 

'web 개발 > auth' 카테고리의 다른 글

SpringSecurity 인증 방식  (0) 2024.06.29