기술과 산업/언어 및 프레임워크

Spring Boot 시리즈 11편 – 이메일 인증 및 비밀번호 재설정: 실전 보안 흐름 구현 전략

B컷개발자 2025. 4. 25. 16:30
728x90

Spring Boot에서 이메일 인증과 비밀번호 재설정 흐름을 안전하게 구현하는 방법을 소개합니다. 인증 토큰 발급, 만료 처리, 보안 설계 전략까지 포함된 실전 가이드입니다.


Spring Boot 시리즈 11편 – 이메일 인증 및 비밀번호 재설정: 실전 보안 흐름 구현 전략

대부분의 서비스에서 이메일 인증은 신뢰 기반 사용자 확보를 위해,
비밀번호 재설정은 보안 사고 예방을 위해 반드시 필요한 기능입니다.

이번 글에서는 Spring Boot 기반으로 이메일 인증 및 비밀번호 재설정 기능을 보안성, 유지보수성, 사용자 경험까지 고려하여
기획부터 구현까지 실무 수준으로 구성해보겠습니다.


📌 1. 이메일 인증이 필요한 이유

목적 설명

가입 계정 유효성 검증 실사용자 확인 (가짜/스팸 방지)
계정 활성화 조건 설정 이메일 인증 전 로그인 차단 가능
비밀번호 재설정 시 본인 확인 이메일을 통해 본인만 접근 가능하도록 설정
보안 사고 대응 인증 링크는 1회성 & 시간 제한으로 제한 필요

🔄 2. 이메일 인증 흐름 설계

[1] 회원가입 요청 → 이메일 발송
[2] 사용자 메일함 → 인증 링크 클릭
[3] 백엔드에서 토큰 검증 → 계정 활성화 처리

✅ 3. 인증 토큰 Entity 설계

@Entity
public class VerificationToken {

    @Id
    @GeneratedValue
    private Long id;

    private String token;

    private String userEmail;

    private LocalDateTime expiryDate;

    public static VerificationToken generate(String email) {
        return new VerificationToken(UUID.randomUUID().toString(), email, LocalDateTime.now().plusHours(1));
    }

    public boolean isExpired() {
        return LocalDateTime.now().isAfter(expiryDate);
    }
}

✉️ 4. 이메일 발송 서비스

1️⃣ 토큰 생성 + 이메일 전송

@Service
@RequiredArgsConstructor
public class EmailVerificationService {

    private final VerificationTokenRepository tokenRepository;
    private final EmailSender emailSender;

    public void sendVerificationEmail(String email) {
        VerificationToken token = VerificationToken.generate(email);
        tokenRepository.save(token);

        String link = "https://myapp.com/api/auth/verify?token=" + token.getToken();
        String body = "이메일 인증을 완료하려면 아래 링크를 클릭하세요.\n" + link;

        emailSender.send(email, "회원가입 이메일 인증", body);
    }
}

2️⃣ 인증 완료 처리

public void verifyToken(String token) {
    VerificationToken vt = tokenRepository.findByToken(token)
        .orElseThrow(() -> new BusinessException(ErrorCode.INVALID_TOKEN));

    if (vt.isExpired()) {
        throw new BusinessException(ErrorCode.EXPIRED_TOKEN);
    }

    User user = userRepository.findByEmail(vt.getUserEmail())
        .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));

    user.activate();  // 계정 활성화
    userRepository.save(user);
    tokenRepository.delete(vt);  // 1회성 토큰 폐기
}

🔐 5. 비밀번호 재설정 흐름 설계

[1] 사용자 이메일 입력 → 임시 토큰 생성 및 메일 발송
[2] 메일의 링크 클릭 → 새로운 비밀번호 입력 폼
[3] 토큰 검증 → 비밀번호 변경 처리

🧱 6. 비밀번호 재설정 토큰 구조 (재사용 가능)

@Entity
public class PasswordResetToken {

    @Id @GeneratedValue
    private Long id;

    private String token;

    private String email;

    private LocalDateTime expiresAt;

    public static PasswordResetToken create(String email) {
        return new PasswordResetToken(UUID.randomUUID().toString(), email, LocalDateTime.now().plusMinutes(30));
    }

    public boolean isExpired() {
        return LocalDateTime.now().isAfter(expiresAt);
    }
}

🔁 7. 재설정 API 흐름 예시

1️⃣ 메일 발송 API

@PostMapping("/api/auth/reset-password-request")
public ApiResponse<Void> requestPasswordReset(@RequestBody EmailRequest request) {
    passwordResetService.sendResetLink(request.getEmail());
    return ApiResponse.success();
}

2️⃣ 비밀번호 변경 API

@PostMapping("/api/auth/reset-password")
public ApiResponse<Void> resetPassword(@RequestBody ResetPasswordRequest request) {
    passwordResetService.resetPassword(request.getToken(), request.getNewPassword());
    return ApiResponse.success();
}

🧠 보안 고려 사항

항목 권장 설계

토큰 만료시간 30분 ~ 1시간 이내 제한
링크 유효성 단일 사용 후 삭제 or 만료
사용자 피드백 "링크가 만료되었습니다" 등의 UX 메시지 중요
토큰 식별자 UUID + 이메일 등 혼합 전략 사용 가능
이메일 전송 비밀번호는 절대 메일에 포함하지 말 것

✅ 마무리 요약

항목 요약

인증 흐름 이메일 → 링크 클릭 → 토큰 검증 → 계정 활성화
토큰 설계 VerificationToken, PasswordResetToken 등 Entity 기반 관리
메일 발송 이메일 전송은 비동기 or 별도 서비스로 분리 권장
보안 설계 만료 시간, 1회성 제한, 예외 처리 통일
UX 강화 사용자에게 명확한 안내 메시지와 흐름 제공

📌 다음 편 예고

Spring Boot 시리즈 12편: 파일 업로드 처리 전략 – 이미지, 문서 업로드부터 S3 연동까지 실전 가이드

 

728x90