웹개발/SpringBoot

보안과 권한 시스템의 핵심 - 7. Provider/JwtProvider

bergerac 2025. 6. 8. 09:11

📁 프로젝트 구조

com.example.korea_sleepTech_springboot
├── provider
│   └── JwtProvider.java             // ✅ JWT 생성·검증 유틸
├── filter
│   └── JwtAuthenticationFilter.java // JWT 필터
├── config
│   └── WebSecurityConfig.java       // SecurityFilterChain 구성
├── service
│   └── CustomUserDetailsService.java (선택)

JwtProvider 클래스의 역할

 

JwtProvider 전체 흐름 요약

[1] 로그인 성공 시
   → generateJwtToken(username, roles) → 클라이언트에 반환

[2] 인증이 필요한 요청
   → Authorization: Bearer <token>
   → JwtAuthenticationFilter → removeBearer → isValidToken → getUsernameFromJwt
   → SecurityContextHolder 인증 주입 → Controller에서 @AuthenticationPrincipal로 사용 가능

 

JwtProvider의 구현

주요 필드 및 초기화

private final Key key;
private final int jwtExpirationMs;
private final long jwtEmailExpirationMs;

 

  • key: 서명을 위한 HMAC 암호화 키
  • jwtExpirationMs: 기본 토큰 만료 시간
  • jwtEmailExpirationMs: 이메일 인증용 토큰 만료 시간
this.key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secret));

 

Base64로 인코딩된 시크릿 키를 디코딩하여 HMAC 키 객체 생성 (HS256용)

 

JWT 생성: generateJwtToken()

public String generateJwtToken(String username, Set<String> roles) {
    return Jwts.builder()
            .claim("username", username)
            .claim("roles", roles)
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + jwtExpirationMs))
            .signWith(key, SignatureAlgorithm.HS256)
            .compact();
}

 

 

  • claim(): JWT에 사용자 정보나 권한 등 커스텀 데이터를 넣을 수 있음
  • setIssuedAt(): 토큰 발급 시각
  • setExpiration(): 만료 시간 설정
  • signWith(): HMAC-SHA256 알고리즘으로 서명
// JWT Payload 예시
{
  "username": "bergerac@example.com",
  "roles": ["USER", "ADMIN"],
  "iat": 1717065600000,
  "exp": 1717108800000
}

 

이메일 인증용 토큰 생성: generateEmailValidToken()

public String generateEmailValidToken(String username)

 

  • 이메일 인증 링크에 사용하는 짧은 수명 토큰
  • jwtEmailExpirationMs를 이용해 생성

Bearer 제거: removeBearer()

public String removeBearer(String bearerToken)

 

 

  • "Bearer <토큰>" 형식으로 들어온 Authorization 헤더를 처리
  • "Bearer " 접두사 제거

토큰 유효성 검증: isValidToken()

public boolean isValidToken(String token) {
    try {
        getClaims(token);
        return true;
    } catch (Exception e) {
        return false;
    }
}

 

 

  • 파싱이 되면 유효한 토큰
  • 만료됐거나 서명이 틀리면 예외 발생 → false 반환

Claims 파싱: getClaims()

public Claims getClaims(String token) {
    JwtParser jwtParser = Jwts.parserBuilder()
        .setSigningKey(key)
        .build();

    return jwtParser.parseClaimsJws(token).getBody();
}

 

 

  • JWT를 파싱해 Claims 객체 반환
  • 클레임은 JWT 본문(payload)에 포함된 사용자 정의 데이터

사용자 정보 추출

public String getUsernameFromJwt(String token)

 

클레임에서 "username" 키 값을 꺼냄

public Set<String> getRolesFromJwt(String token)
  • List<String> → HashSet<String> 변환하여 반환