본문 바로가기
컴퓨터 프로그래밍/Spring

[Spring] JwtUtil 예시 코드 분석

by 한33 2024. 9. 21.

package com.sparta.sweethoney.util;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.security.Key;
import java.security.SignatureException;
import java.util.Base64;
import java.util.Date;

@Component
public class JwtUtil {
    //JWT 데이터-Secret Key
    // Header KEY 값
    public static final String AUTHORIZATION_HEADER = "Authorization";
    // 사용자 권한 값의 KEY
    public static final String AUTHORIZATION_KEY = "auth";
    // Token 식별자
    public static final String BEARER_PREFIX = "Bearer ";
    // 토큰 만료시간
    private final long TOKEN_TIME = 60 * 60 * 1000L; // 60분
    //encoding decoding algorithm 선언
    private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
     @Value("${jwt.secret.key}")
    // Base64 Encode 한 SecretKey
    //application.properties 에서 선언한 jwt.secret.key 를 String으로 저장
    private String secretKey;
    //키를 저장할 키클래스 필드선언
    private Key key;
    @PostConstruct
    public void init() {
        //byte array 에 secretkey 를 base 64로 디코딩 한값을 저장
        System.out.println(secretKey);
        byte[] bytes = Base64.getDecoder().decode(secretKey);
        //디코딩한 값을 가진 byte array 를 key 필드에 다시 저장
        key = Keys.hmacShaKeyFor(bytes);
    }
    //JWT 생성
    // 토큰 생성
    public String createToken(Long userId) {
        Date date = new Date();
        return BEARER_PREFIX +
                //Jwts.builder 를 사용하여 토큰을 생성
                Jwts.builder()
                        .setSubject(Long.toString(userId)) // 사용자 식별자값(ID)
                        .setExpiration(new Date(date.getTime() + TOKEN_TIME)) // 만료 시간 = 현제시간 date.getTIME() + 만료시간 TOKEN_TIME
                        .setIssuedAt(date) // 발급일
                        .signWith(key, signatureAlgorithm) // 암호화 알고리즘 (키 , 선택한 알고리즘)
                        .compact();
    }



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

    public boolean validateToken(String token) {
        try {//암호화할떄 사용한 키를 .setSigningkey , parseClaimsJws(token)은 받아와서 검증할 토큰
            Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
            return true;
        } catch (SecurityException | MalformedJwtException e) {
            System.out.println("Invalid JWT signature, 유효하지 않는 JWT 서명 입니다.");
        } catch (ExpiredJwtException e) {
            System.out.println("Expired JWT token, 만료된 JWT token 입니다.");
        } catch (UnsupportedJwtException e) {
            System.out.println("Unsupported JWT token, 지원되지 않는 JWT 토큰 입니다.");
        } catch (IllegalArgumentException e) {
            System.out.println("JWT claims is empty, 잘못된 JWT 토큰 입니다.");
        }
        return false;
    }

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

// Header KEY 값
public static final String AUTHORIZATION_HEADER = "Authorization";
// 사용자 권한 값의 KEY
public static final String AUTHORIZATION_KEY = "auth";
// Token 식별자
public static final String BEARER_PREFIX = "Bearer ";
// 토큰 만료시간
private final long TOKEN_TIME = 60 * 60 * 1000L; // 60분

 

문자열 등을 상수화시켜서 시각적으로 보이기 좋게 설정


 @Value("${jwt.secret.key}")
// Base64 Encode 한 SecretKey
//application.properties 에서 선언한 jwt.secret.key 를 String으로 저장
private String secretKey;
//키를 저장할 키클래스 필드선언
private Key key;
@PostConstruct
public void init() {
    //byte array 에 secretkey 를 base 64로 디코딩 한값을 저장
    System.out.println(secretKey);
    byte[] bytes = Base64.getDecoder().decode(secretKey);
    //디코딩한 값을 가진 byte array 를 key 필드에 다시 저장
    key = Keys.hmacShaKeyFor(bytes);
}

 

application.properties 에서 지정된 jwt.secret.key 를 불러와서 디코딩 한 값을 key 에 저장


public String createToken(Long userId) {
    Date date = new Date();
    return BEARER_PREFIX +
            //Jwts.builder 를 사용하여 토큰을 생성
            Jwts.builder()
                    .setSubject(Long.toString(userId)) // 사용자 식별자값(ID)
                    .setExpiration(new Date(date.getTime() + TOKEN_TIME)) // 만료 시간 = 현제시간 date.getTIME() + 만료시간 TOKEN_TIME
                    .setIssuedAt(date) // 발급일
                    .signWith(key, signatureAlgorithm) // 암호화 알고리즘 (키 , 선택한 알고리즘)
                    .compact();
}

 

jwt.secret.key 와 조합해서 토큰을 생성


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

 

생성된 토큰의 접두에 BEARER 을 걸러주는 코드


public boolean validateToken(String token) {
    try {//암호화할떄 사용한 키를 .setSigningkey , parseClaimsJws(token)은 받아와서 검증할 토큰
        Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
        return true;
    } catch (SecurityException | MalformedJwtException e) {
        System.out.println("Invalid JWT signature, 유효하지 않는 JWT 서명 입니다.");
    } catch (ExpiredJwtException e) {
        System.out.println("Expired JWT token, 만료된 JWT token 입니다.");
    } catch (UnsupportedJwtException e) {
        System.out.println("Unsupported JWT token, 지원되지 않는 JWT 토큰 입니다.");
    } catch (IllegalArgumentException e) {
        System.out.println("JWT claims is empty, 잘못된 JWT 토큰 입니다.");
    }
    return false;
}

 

토큰 유효성 검사


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

 

  • JWT 토큰을 입력받고, 해당 토큰이 서명이 유효한지 검증한 뒤, 토큰 내에 담겨있는 Claims (JWT의 페이로드 정보)를 반환.
  • 반환된 Claims 객체는 JWT에 포함된 데이터(사용자 정보, 발급 시간, 만료 시간 등)를 담고 있으며, 이를 통해 인증/인가 과정에서 사용자 정보를 활용할 수 있음.


// JWT Cookie 에 저장
public void addJwtToCookie(String token, HttpServletResponse res) {
    try {
        token = URLEncoder.encode(token, "utf-8").replaceAll("\\+", "%20"); // Cookie Value 에는 공백이 불가능해서 encoding 진행
        Cookie cookie = new Cookie(AUTHORIZATION_HEADER, token); // Name-Value
        cookie.setPath("/");
        // Response 객체에 Cookie 추가
        res.addCookie(cookie);
    } catch (UnsupportedEncodingException e) {
        System.out.println(e);
    }
}

 

 

JWT 를 쿠키에 저장하는 방법도 추가로 알아두자.

'컴퓨터 프로그래밍 > Spring' 카테고리의 다른 글

[Spring] AuthUser 예시 코드 분석  (0) 2024.09.21
[Spring] AuthFilter 예시 코드 분석  (1) 2024.09.21
[Spring] Naver Open API  (0) 2024.09.18
[Spring] RestTemplate  (2) 2024.09.17
[Spring] Service Test Code 작성  (0) 2024.09.14