[SPRING] JwtUtil

야부엉·2023년 11월 13일
0

1. Util

1. Util 클래스란?

  • 특정 매개변수(파라미터)에 대한 작업을 수행하는 메서드들의 집합인 클래스
  • 다른 객체에 의존 X ,하나의 모듈로 작동

2. JwtUtil 클래스 만들어 보기

1. 토큰 생성에 필요한 데이터

// 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
private String secretKey;
private Key key;
private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

// 로그 설정
public static final Logger logger = LoggerFactory.getLogger("JWT 관련 로그");

@PostConstruct
public void init() {
    byte[] bytes = Base64.getDecoder().decode(secretKey);
    key = Keys.hmacShaKeyFor(bytes);
}
  • @Value : application.properties에 있는 미리 Base64로 Encode된 Secret key를 가져온다.
  • @PostConstruct : 딱 한번만 받아오면 되는 값을 위해 사용
  • Key는 Decode한 secret key를 담는 객체
  • SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256 HS256 알고리즘을 사용한다고 선언

    "Bearer"?
    토큰은 요청 헤더의 Authorization: type credentials와 같이 Authorization 필드에 담아져 보내진다. 이때 Bearer은 type에 해당한다

    • Baic : 사용자 아이디와 암호를 Base64로 인코딩한 값을 토큰으로 사용
    • Bearer : JWT 혹은 Oauth에 대한 토큰을 사용
    • Digest : 서버에서 난수 데이터 문자열을 클라이언트에 보낸다. 클라이언트는 사용자 정보와 nonce를 포함하는 해시값을 사용하여 응답한다
    • HOBA : 전자 서명 기반 인증
    • Mutual : 암호를 이용한 클라이언트 - 서버 상호 인증
    • AWS4-HMAC-SHA256 : AWS 전저 서명 기반 인증

2. JWT 생성

// 토큰 생성
public String createToken(String username, UserRoleEnum role) {
    Date date = new Date();

    return BEARER_PREFIX +
            Jwts.builder()
                    .setSubject(username) // 사용자 식별자값(ID)
                    .claim(AUTHORIZATION_KEY, role) // 사용자 권한
                    .setExpiration(new Date(date.getTime() + TOKEN_TIME)) // 만료 시간
                    .setIssuedAt(date) // 발급일
                    .signWith(key, signatureAlgorithm) // 암호화 알고리즘
                    .compact();
}
  • JWT에 사용자의 권한 정보를 넣습니다. key-value 형식으로 key 값을 통해 확인할 수 있다.

3. JWT Cookie에 저장

// 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) {
        logger.error(e.getMessage());
    }
}

4. 받아온 Cookie의 Value인 JWT 토큰 substring

// JWT 토큰 substring
public String substringToken(String tokenValue) {
    if (StringUtils.hasText(tokenValue) && tokenValue.startsWith(BEARER_PREFIX)) {
        return tokenValue.substring(7);
    }
    logger.error("Not Found Token");
    throw new NullPointerException("Not Found Token");
}
  • StringUtils.hasText를 사용하여 공백, null을 확인하고 startsWith을 사용하여 토큰의 시작값이 Bearer이 맞는지 확인
  • 맞다면, Bearer를 잘라낸다.

5. JWT 검증

// 토큰 검증
public boolean validateToken(String token) {
    try {
        Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
        return true;
    } catch (SecurityException | MalformedJwtException | SignatureException e) {
        logger.error("Invalid JWT signature, 유효하지 않는 JWT 서명 입니다.");
    } catch (ExpiredJwtException e) {
        logger.error("Expired JWT token, 만료된 JWT token 입니다.");
    } catch (UnsupportedJwtException e) {
        logger.error("Unsupported JWT token, 지원되지 않는 JWT 토큰 입니다.");
    } catch (IllegalArgumentException e) {
        logger.error("JWT claims is empty, 잘못된 JWT 토큰 입니다.");
    }
    return false;
}
  • Jwts.parserBuilder() 를 사용하여 JWT를 파싱한다.
  • JWT가 위변조되지 않았는지 secretKey(key)값을 넣어 확인

6. JWT에서 사용자 정보 가져오기

// 토큰에서 사용자 정보 가져오기
public Claims getUserInfoFromToken(String token) {
    return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
}
  • JWT Payload부분에 토큰에 담긴 정보가 있다.
  • Payload에 담긴 정보의 한 조각을 claim이라고 부르고, key-value의 한쌍으로 이뤄져 있다. 여러개 클레임이 존재할 수 있다.
  • Jwts.parserBuilder() 와 secretKey를 사용하여 JWT의 Claims를 가져와 담겨 있는 사용자의 정보를 사용한다.

출처

Bearer이란?
내일배움캠프 Spring Master

profile
밤낮없는개발자

0개의 댓글