[Spring Boot] 비밀번호 재설정 링크를 이메일로 전송하기 (Redis 토큰)

손은실·2024년 6월 21일
0

Spring Boot

목록 보기
5/11
post-thumbnail

들어가며

지금까지의 프로젝트에서 사용자의 비밀번호 변경을 서버에서 처리해 보안성이 매우 낮았습니다.

개발자조차 사용자의 개인 정보를 열람할 수 없도록 하기 위해서 비밀번호를 변경할 수 있는 링크를 사용자의 이메일로 전송하는 방법을 채택했습니다.

해당 포스팅에는 코드 부분만 나와있으며, import와 의존성 주입 등 자세한 전체 코드는 북마키에서 보실 수 있습니다.

개발 환경
Spring Boot 3 / Java 17 / Spring Security 6 / MySQL / Redis / JavaMailSender




설계 (구조와 실행 흐름)

일단 필요한 기능을 생각해봤습니다.

  1. 메일 생성+전송 → JavaMailSender
  2. 요청 들어온 이메일 주소가 존재하는 사용자인지 확인 → 토큰 발급 전 확인
  3. 링크에 접근 가능한지 확인 → 일회용 토큰
  4. 토큰 관리(저장 · 발급 · 유효성 검증 · 무효화) → Redis

Token

  • 이메일로 전송된 링크에 무분별하게 비밀번호 변경 API 요청이 들어오는 것을 방지하기 위해 토큰을 사용합니다.
  • 24시간 동안 1회 사용 가능한 일회용 토큰을 발급합니다.

Redis

  • 사용자마다 개인의 토큰을 저장하기 위해 Key-Value 구조의 NoSQL DB를 사용합니다.
  • 토큰 유효성 검증과 무효화를 간단히 수행할 수 있습니다.

전체적인 흐름은 아래 그림과 같습니다.



토큰 발급 + 메일 전송

🟢 build.gradle

필요한 의존성을 추가합니다.


🟢 application.yml

  • RedisSMTP 정보들을 작성합니다.
    • 저는 실제 프로젝트에서는 민감한 정보들을 숨기기 위해 application-private.yml 파일을 따로 만들어 application.yml에 연결시키고 private 파일은 gitignore에 등록했습니다.
    • SMTP의 비밀번호는 Gmail 앱 비밀번호를 사용하시길 추천합니다!
      (Google 계정에서 2차 인증 활성화 후 생성 가능)
  • props메일로 전송될 주소를 환경 변수로 설정합니다.
    • 코드 내에 주소를 기재에 하드코딩하는 것은 추천하지 않습니다.
    • 보안, 유지보수, 확장성, 테스트 등의 문제


🟢 UserController

메일 요청 API

  • 로그인에 사용한 사용자의 이메일 주소를 전달하며 변경 메일 요청



🟢 UserService

메일 생성 + 전송

  1. 존재하는 사용자인지 확인
  2. 메일 생성+전송 메서드 실행
    - 존재하는 사용자: 토큰 반환
    - 존재하지 않는 사용자: HTTP 상태 코드 404 반환


🟢 PasswordResetRes

  • 메일 요청 API의 응답 VO


🟢 MailService

환경 변수 설정 (하드코딩 X)

  • yml에서 변수 변경 시 코드 내에 사용된 모든 곳에서 자동으로 변경

메일 생성

  1. UUID 토큰 발급
  2. 메일 작성
  3. 메일 전송 메서드 호출

메일 전송

  1. JavaMailSender를 이용해 전송에 필요한 정보 생성
    • 데이터 형식, 보내는 사람, 받는 사람, 제목, 내용
  2. 전송


🟢 ResetTokenService

토큰 생성 + Redis 저장

  1. UUID 생성
  2. 식별자를 앞에 붙여 비밀번호 변경 토큰임을 표시
  3. 24시간 제한과 함께 토큰 저장

    왜 토큰을 Key로 했나요?
    토큰 유효성 확인, 무효화할 때 간단하게 Redis에서 해당 key의 존재 여부를 확인하면 되므로 O(1) 시간 복잡도로 조회 가능합니다.


토큰 유효성 검증


토큰 무효화



테스트

SMTP LOG

application.yml에서 debug: true 를 하면 로그를 볼 수 있습니다.

  • Gmail에 정상적으로 로그인

  • 메일이 작성되는 과정



클라이언트가 받는 값

  • 클라이언트는 토큰을 가지고 비밀번호 변경 요청을 할 수 있습니다.


정상적으로 메일이 전송됐을 때

  • 임시로 작성해 하이퍼링크가 활성화되지 않은 상태입니다.


존재하지 않는 이메일로 전달됐을 때

  • 코드 상에서 존재하지 않는 이메일은 API 요청을 거부하기에 이메일이 반송되는 경우는 없습니다.
  • 구현 과정에서 반송된다는 것을 알게돼서 해당 내용도 추가했습니다.


토큰 검증 + 비밀번호 변경

🟢 UserController

비밀번호 변경 API



🟢 UserService

비밀번호 변경

  1. 토큰 유효성 검증
  2. 비밀번호 변경 (24시간 내, 1회 가능)
  3. 토큰 무효화

🟢 PasswordResetReq

  • 비밀번호 변경 API의 요청 VO


테스트

변경 성공 (1회만 가능)

변경 실패

  • 1회 변경 후에 동일한 토큰으로 변경 요청 시 변경에 실패하게 됩니다.

변경 Log

0개의 댓글