Spring

[Spring] JwtUtil 클래스란? | JwtUtil 역할 | JwtUtil 구현

kimslab01 2024. 11. 22. 16:21

 

JwtUtil의 주요 역할

  1. JWT 생성
    • 사용자의 ID, 이메일, 권한 등의 정보를 포함하는 JWT를 생성
    • JWT는 클라이언트로 전달, 이후 요청에서 사용됨
  2. JWT 파싱 및 검증
    • 클라이언트가 제공한 JWT를 파싱하여 토큰에 포함된 정보 추출
    • 서명을 검증해 토큰이 변조되지 않았는지 검증
    • 토큰의 유효 시간과 만료 시간을 확인
  3. JWT 관리
    • HTTP 요청 헤더에 Bearer 접두사를 제거하여 순수한 JWT를 추출
    • JWT를 기반으로 사용자 정보를 확인하여 인증 및 권한 처리

 

JwtUtil 코드

@Component
public class JwtUtil {
    private static final String BEARER_PREFIX = "Bearer ";
    private static final long TOKEN_TIME = 60 * 60 * 1000L; // 60분
    private static final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

    @Value("${jwt.secret.key}")
    private String secretKey;
    private Key key;

    @PostConstruct
    public void init() {
        byte[] bytes = Base64.getDecoder().decode(secretKey);
        key = Keys.hmacShaKeyFor(bytes);
    }

    public String createToken(Long userId, String email, UserRole userRole) {
        Date date = new Date();

        return BEARER_PREFIX +
                Jwts.builder()
                        .setSubject(String.valueOf(userId))
                        .claim("email", email)
                        .claim("userRole", userRole)
                        .setExpiration(new Date(date.getTime() + TOKEN_TIME))
                        .setIssuedAt(date)
                        .signWith(key,signatureAlgorithm)
                        .compact();
    }

    public String substringToken(String tokenValue) throws ServerException {
        if (StringUtils.hasText(tokenValue) && tokenValue.startsWith(BEARER_PREFIX)) {
            return tokenValue.substring(7);
        }
        throw new ServerException("Not Found Token");
    }

    public Claims extractToken(String token) {
        return Jwts.parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(token)
                .getBody();
    }
}

 

 

코드 세부 분석

1. 상수 선언

private static final String BEARER_PREFIX = "Bearer ";
private static final long TOKEN_TIME = 60 * 60 * 1000L; // 60분
private static final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
  • BEARER_PREFIX : JWT의 접두사로 사용되는 문자열 “Bearer “로, 헤더의 Authorization 필드에서 JWT를 구분
  • TOKEN_TIME : JWT의 유효 시간을 60분(60 * 60 * 1000ms)으로 설정
  • signatureAlgorithm : JWT 서명을 할 때 사용할 알고리즘을 HS256으로 설

2. 필드

@Value("${jwt.secret.key}")
    private String secretKey;
    private Key key;
  • secretKey : JWT 서명 생성에 사용할 비밀 키로, application.properties와 같은 설정 파일이나 환경 변수로 주입
  • key : JWT 서명 생성 및 검증에 사용할 HMAC 키 객체 저장

3. @PostConstruct 메서드 (init)

@PostConstruct
    public void init() {
        byte[] bytes = Base64.getDecoder().decode(secretKey);
        key = Keys.hmacShaKeyFor(bytes);
    }
  • 클래스가 생성된 후 초기화 단계에서 호출
  • secretKey를 Base64로 디코딩하여 HMAC 키를 생성. 이 키는 JWT를 생성하거나 검증할 때 사용

4. createToken 메서드

public String createToken(Long userId, String email, UserRole userRole) {
        Date date = new Date(); // 현재 시간을 기준으로 Date 객체를 생성합니다. (토큰 생성 시간 및 만료 시간 계산에 사용)

        return BEARER_PREFIX +
                Jwts.builder() // JWT 생성 빌더 시작
                        .setSubject(String.valueOf(userId)) // subject 필드에 userId를 저장 (주로 사용자 식별자)
                        .claim("email", email) // 클레임에 "email" 키와 값을 저장
                        .claim("userRole", userRole) // 클레임에 "userRole" 키와 값을 저장
                        .setExpiration(new Date(date.getTime() + TOKEN_TIME)) // 현재 시간(date)에서 토큰 유효 시간(TOKEN_TIME)을 더하여 만료 시간 설정
                        .setIssuedAt(date)  // 토큰 발급 시간 설정
                        .signWith(key,signatureAlgorithm) // 서명 생성: secretKey로 만든 키와 지정된 알고리즘(HS256)을 사용
                        .compact();  // JWT를 문자열 형태로 직렬화
    }
  • 순서
    • JWT 생성
    • 매개변수로 userId, email, UserRole을 받아 클레임에 저장 (매개변수는 프로젝트에 따라 변경될 수 있음)
    • subject에 사용자 ID, email, userrole, expiration(토큰 만료 시간), issuedAt(토큰 생성 시간)이 JWT에 정보로 포함됨
    • HMAC 알고리즘 키를 사용해 서명된 JWT를 반환하여 BEARER_PREFIX를 앞에 붙임

5. substringToken 메서드

public String substringToken(String tokenValue) throws ServerException {
        if (StringUtils.hasText(tokenValue) && tokenValue.startsWith(BEARER_PREFIX)) { // 조건: tokenValue가 null 또는 공백이 아니고 "Bearer "로 시작해야 함
            return tokenValue.substring(7); // "Bearer " 이후의 문자열(JWT 본문)만 반환
        }
        throw new ServerException("Not Found Token"); // 조건에 맞지 않는 경우 예외를 발생
    }
  • 순서
    • 클라이언트에서 전달받은 JWT에서 Bearer 접두사를 제거
    • 토큰이 유효하지 않으면 ServerException을 던짐

6. extractToken 메서드

public Claims extractToken(String token) {
        return Jwts.parserBuilder() // JWT 파싱을 위한 빌더 생성
                .setSigningKey(key) // 서명 검증을 위한 HMAC 키 설정
                .build() // JWT 파서 빌드
                .parseClaimsJws(token) // JWT 문자열을 파싱하고 클레임(Claims) 추출
                .getBody(); // 파싱된 JWT의 본문(Claims) 반환
    }
  • 순서
    • 전달받은 JWT를 파싱하여 클레임(Claims) 추출
    • JWT의 서명을 검증하기 위해 key를 사용하며, 서명이 올바르지 않으면 예외 발생아이콘 추가