λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°

슀마일게이트 μ„œλ²„κ°œλ°œμΊ ν”„ 4κΈ°

[μ„œλ²„κ°œλ°œμΊ ν”„] 인증 μ„œλ²„ - 이메일 인증 νšŒμ›κ°€μž…

νšŒμ›κ°€μž…

 μ΄λ©”일 인증을 기반으둜 νšŒμ›κ°€μž… λ‘œμ§μ„ μž‘μ„±ν–ˆλ‹€. λ‘œμ§μ€ λ‹€μŒκ³Ό κ°™λ‹€.

1. μ‚¬μš©μžκ°€ 이메일과 λΉ„λ°€λ²ˆν˜Έ λ“± νšŒμ›μ •λ³΄λ₯Ό μž…λ ₯ν•œ ν›„, νšŒμ›κ°€μž… μš”μ²­μ„ 보낸닀.
2. μ„œλ²„μ—μ„œλŠ” μ‚¬μš©μžμ˜ μ΄λ©”μΌλ‘œ 인증메일을 μ „μ†‘ν•˜κ³ , μ‚¬μš©μžκ°€ μž…λ ₯ν•œ 정보λ₯Ό Redis에 μž„μ‹œμ €μž₯ν•œλ‹€.
3. μ‚¬μš©μžκ°€ μ „μ†‘λœ λ©”μΌμ˜ 링크λ₯Ό 톡해 인증확인 μš”μ²­μ„ 보낸닀.
4. μ„œλ²„μ—μ„œλŠ” 인증확인 ν›„, Redis에 μ €μž₯ 된 νšŒμ› 정보λ₯Ό DB에 μ €μž₯ν•˜λ©° νšŒμ›κ°€μž…μ„ λ§ˆμΉœλ‹€.

 μ°¨μž₯λ‹˜κ³Όμ˜ μ½”λ“œλ¦¬λ·°μ—μ„œ ν•΄λ‹Ή λ‘œμ§μ— λŒ€ν•΄ 쑰언을 λ°›μ•˜λ‹€. μ΅œκ·Όμ—λŠ” νšŒμ›κ°€μž…μ„ λ§ˆμ³€μ§€λ§Œ 아직 인증이 μ™„λ£Œλ˜μ§€ μ•Šμ€ μ‚¬μš©μžμ—κ²Œ 일단 μΌλΆ€μ˜ κΆŒν•œ(μ΅œμ†Œν•œμ˜ κΆŒν•œ)만 μ œκ³΅ν•œ ν›„, 인증이 μ™„λ£Œλ˜λ©΄ μΆ”κ°€ κΆŒν•œμ„ μ œκ³΅ν•˜λŠ” 것이 μΌλ°˜μ μ΄λΌλŠ” κ²ƒμ΄μ—ˆλ‹€. ν•˜μ§€λ§Œ ν˜„μž¬ 개발 쀑인 μ„œλΉ„μŠ€(μžμ†Œμ„€λ‹·μ»΄ 클둠 ν”„λ‘œμ νŠΈ)μ—μ„œλŠ” λ‘œκ·ΈμΈν•˜μ§€ μ•Šμ€ μ‚¬μš©μžλ„ μ±„μš©κ³΅κ³ λ₯Ό ν™•μΈν•˜λŠ” λ“±μ˜ 일뢀 κΈ°λŠ₯을 μ œκ³΅ν•˜κΈ° λ•Œλ¬Έμ—, 일단 λ‘œμ§μ„ μˆ˜μ •ν•˜μ§€λŠ” μ•ŠκΈ°λ‘œ κ²°μ •ν–ˆλ‹€.

 

Redis μ„€μ •

 μ‚¬μš©μžκ°€ 인증을 μ™„λ£Œν•˜κΈ°κΉŒμ§€ μž„μ‹œλ‘œ μ‚¬μš©μž 정보λ₯Ό μ €μž₯ν•  μ €μž₯μ†Œλ‘œ Redisλ₯Ό μ‚¬μš©ν–ˆλ‹€. λ‹€μŒμ€ λ‚΄κ°€ μž‘μ„±ν•œ Redis μ„€μ •κ³Ό μœ ν‹Έμ΄λ‹€.

RedisConfig.java

@Configuration
public class RedisConfig {

    @Bean
    public RedisConnectionFactory redisConnectionFactory(){
        LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory();
        return lettuceConnectionFactory;
    }

    @Bean
    public RedisTemplate<String,Object> redisTemplate(){
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }

}

 

RedisUtil.java

@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public void set(String key, Object o, int minutes) {
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(o.getClass()));
        redisTemplate.opsForValue().set(key, o, minutes, TimeUnit.MINUTES);
    }

    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    public boolean delete(String key) {
        return redisTemplate.delete(key);
    }

    public boolean hasKey(String key) { return redisTemplate.hasKey(key); }
}

 

인증 메일 전솑

UserController.java

 μ‚¬μš©μžκ°€ νšŒμ›μ •λ³΄λ₯Ό μž…λ ₯ ν›„, νšŒμ›κ°€μž… μš”μ²­/users/signup을 보낸닀. SignupRequestDtoμ—λŠ” μ‚¬μš©μžμ˜ emailκ³Ό password λ“±μ˜ 정보가 λ‹΄κ²¨μžˆλ‹€.

@RequiredArgsConstructor
@RestController
@RequestMapping("/users")
public class UserController {

    private final UserService userService;

    @PostMapping("/signup")
    public ResponseEntity<ResultResponse> signup(@RequestBody SignupRequestDto signupRequestDto) {

        userService.sendSignupMail(signupRequestDto);

        return ResponseEntity.ok().body(
                ResultResponse.builder()
                        .success(true)
                        .status(HttpStatus.OK.value())
                        .message("인증 메일을 λ³΄λƒˆμŠ΅λ‹ˆλ‹€.")
                        .build()
        );
    }

}

 

UserService.java

 μ‘΄μž¬ν•˜λŠ” 이메일인지 확인 ν›„, ν•΄λ‹Ή μ΄λ©”μΌλ‘œ random UUIDλ₯Ό key κ°’μœΌλ‘œ κ°€μ§€λŠ” 링크λ₯Ό μ „μ†‘ν•œλ‹€. 메일 전솑이 μ„±κ³΅ν•˜λ©΄, ν•΄λ‹Ή UUIDλ₯Ό key κ°’μœΌλ‘œ Redis에 μ‚¬μš©μž 정보λ₯Ό 10λΆ„ λ™μ•ˆ μž„μ‹œλ‘œ μ €μž₯ν•œλ‹€.

@RequiredArgsConstructor
@Service
public class UserService {

    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;
    private final MailUtil mailUtil;
    private final RedisUtil redisUtil;

    public void sendSignupMail(SignupRequestDto signupRequestDto) {

        String email = signupRequestDto.getEmail();
        String passwd = passwordEncoder.encode(signupRequestDto.getPassword());

        if(userRepository.countUser(email) > 0) throw new ExistEmailException(email);

        String key = UUID.randomUUID().toString();
        User user = User.builder()
                        .email(email)
                        .nickname("temp")
                        .passwd(passwd)
                        .role(1)
                        .build();

        if(mailUtil.sendSignupMail(key, user)) redisUtil.set(key, user, 10);
    }

}

 

MailUtil.java

메일에 포함 된 λ§ν¬λŠ” 인증을 ν™•μΈν•˜λŠ” μš”μ²­ 링크가 걸렀있고, key κ°’μœΌλ‘œ UserServiceμ—μ„œ μƒμ„±ν•œ UUIDλ₯Ό 가지고 μžˆλ‹€. ν•΄λ‹Ή keyλŠ” Redis에 μ €μž₯된 μ‚¬μš©μž μ •λ³΄μ˜ key 값이기도 ν•˜λ‹€.

@Component
public class MailUtil {

    @Value("${spring.mail.username}")
    private String fromEmail;

    @Value("${spring.mail.password}")
    private String fromEmailpassword;

    private Session session;

    public MailUtil() {
    
        Properties prop = new Properties();
        prop.put("mail.smtp.host", "smtp.gmail.com");
        prop.put("mail.smtp.port", 465);
        prop.put("mail.smtp.auth", "true");
        prop.put("mail.smtp.ssl.enable", "true");
        prop.put("mail.smtp.ssl.trust", "smtp.gmail.com");
        prop.put("mail.smtp.starttls.enable", "true");

        this.session = Session.getDefaultInstance(prop, new javax.mail.Authenticator(){
            protected PasswordAuthentication getPasswordAuthentication(){
                return new PasswordAuthentication(fromEmail, fromEmailpassword);
            }
        });
        
    }

    public boolean sendSignupMail(String key, User user){
        try {
            
            MimeMessage message = new MimeMessage(session);
            message.setFrom(new InternetAddress(fromEmail));
            message.addRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));
            message.setSubject("[ν•©κ²©ν•˜μ†Œμ„œ] μΈμ¦μ½”λ“œ 전솑");

            message.setContent(
            		"<h1>[이메일 인증]</h1> <p>μ•„λž˜ 링크λ₯Ό ν΄λ¦­ν•˜μ‹œλ©΄ 이메일 인증이 μ™„λ£Œλ©λ‹ˆλ‹€.</p> " +
                    	"<a href='http://localhost:8080/users/signup/confirm?key="+key+"' target='_blenk'>이메일 인증 확인</a>"
                    	,"text/html;charset=euc-kr"
            );

            Transport.send(message);
            
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

        return true;
    }

}

 

인증 확인

 μΈμ¦ λ©”μΌμ˜ 링크λ₯Ό ν΄λ¦­ν•˜λ©΄, μ„œλ²„μ— 인증 확인 μš”μ²­μ„ λ³΄λ‚΄κ²Œ 되고 μ„œλ²„λŠ” νšŒμ›κ°€μž… 절차λ₯Ό μ™„λ£Œν•œ ν›„ λ‘œκ·ΈμΈν™”λ©΄μœΌλ‘œ λ¦¬λ‹€μ΄λ ‰νŠΈ μ‹œν‚¨λ‹€.

UserController.java

@RequiredArgsConstructor
@RestController
@RequestMapping("/users")
public class UserController {

    private final UserService userService;

    @GetMapping("/signup/confirm")
    public void signupConfirm(@RequestParam("key")String key, HttpServletResponse response) throws IOException {

        userService.registerUser(key);

        response.sendRedirect("http://localhost:8080/");
    }

}

 

UserService.java

@RequiredArgsConstructor
@Service
public class UserService {

    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;
    private final MailUtil mailUtil;
    private final RedisUtil redisUtil;

    public void registerUser(String key) {

        User user = (User)redisUtil.get(key);

        if(user==null) throw new TimeoutException();

        String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());

        user.setCreatedAt(now);
        user.setUpdatedAt(now);
        user.setAccessedAt(now);

        int userId = userRepository.registerUser(user);
        if(userId > 0) redisUtil.delete(key);

        String nickname = userRepository.getNickname(userId);

        userRepository.updateNickname(userId, nickname, now);
    }

}