Auth

JJWT(Java Json Web Token) 예외 처리

kidmillionaire1998 2023. 7. 26. 12:21

Java환경에서 JJWT을 파싱하는 과정 속에서의 예외를 다뤄보고자 한다. 여러가지 파싱 함수가 있지만, 

JwtParser의 parseClaimsJws함수를 이용해 파싱해보고자 한다. 

 

parseClaimsJws 

Jws<Claims> parseClaimsJws(String claimsJws)
        throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException;

- 압축된 String 형식의 토큰을 인자로 받는다. 

- 파싱 후 토큰을 만들 때, 삽입했던 claims 값을 포함한 Jws 객체가 반환된다. 

- 파싱하는 과정 속에서 압축된 토큰의 형식이나 만료 여부등을 검증하며, 만약 이에 해당한다면, 예외를 발생시킨다. 

 

사용 방법 예시는 다음과 같다. 

  private Jws<Claims> parseJwt(String token) {
        Jws<Claims> claims;
        claims = Jwts.parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(token);
        return claims;
}

우선 JwtParser을 만들기 위해, JwtParserBuilder을 생성해야한다. 

 

- JwtParseBuilder 생성 후, 토큰 생성 시 서명하였던 키를 setSigningKey의 인자로 넣어, 적절한 키를 가지고 있는 사용자인지 인증한다. 

- 인증이 완료된 JwtParserBuilder을 build하여 JwtParser을 생성한다. 

 

이렇게 생성된 JwtParser에서 parseClaimsJws이라는 함수를 사용해, 문자열의 토큰을 파싱하면 된다. 

다만, 문자열 형식의 토큰이 잘못되거나 만료된 토큰일 경우에는 예외가 발생하는데, 다음과 같은 예외들이 발생한다. 

 

MalformedJwtException

  • 가장 많이 발생할 수 있는 예외인 것 같다. 말 그대로 잘못된 형식일때 발생한다.

https://jwt.io/ 에서 왼쪽에 있는 토큰 문자열을 조작하면 오른쪽에 나오는 형식이 잘못되었기 때문에 보통 이런 경우에 많이 발생한다.

 

SignatureException

  • 형식은 맞는데, 조작된 값이 전달되었을 때 발생하는 예외이다.

이 예외는, https://jwt.io/ 의 오른쪽 부분에 다른 값을 넣었을 때 보통 발생한다.

아래의 그림은 우측의 payload에서 userIdx값을 1에서 2로 변경하여 토큰을 넣어봤다.

UnsupportedJwtException

  • 잘못된 토큰이 들어왔구나.. 정도로만 생각하면 될 것 같다.

다음과 같이 두번째 .이 찍혀있고, 그 뒤에 값이 없으면 발생하는 경우에 발생하는데 구분할 필요가 있을까..? (이거 그냥 MalformedJwtException이랑 같은 거 아닌가?)

IllegalArgumentException

우리가 흔히 알고 있는 IllegalArgumentException이 맞다. 

 

예외처리 기준 고민 

 

해당 에러들을 보면 다양한 원인으로 발생하는데, 클라이언트 개발자 입장에서 위의 모든 에러에 대해 다른 처리 방법이 필요한 것이 아니니, 클라이언트 개발자 입장에서 처리해야 할 예외처리 방법에 따라 처리할 생각이다.

결국 크게 3가지인 것 같다.

 

1. 토큰 값이 Null일 때 

처리 예외 : NullPointerException

  • 토큰 값이 null이기 때문에 클라이언트 측에서 잘못된 요청을 보냈을 가능성이 크기 때문에 따로 분류했다.

2. 토큰 값이 잘못되었을 때 

처리 예외 : UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException

  • 토큰이 탈취되어 조작되었을 경우나 클라이언트 개발자에서 토큰 값을 잘못 입력했을 경우
  • 위에서 어떤 경우에 해당 에러들이 발생하는지 알아보고 싶어서, 나름대로 테스트를 해봤는데, 결국 클라이언트 개발자가 각 예외에 대한 다른 처리를 해야하는 것이 아니기 때문에 하나의 catch문에 같이 예외 처리를 해주면 된다고 생각했다. 
  • UnsupportedJwtException, MalformedJwtException, SignatureException는 모두 JwtException을 상속하지만, IllegalArgumentException은 그렇지 않기 때문에, 다음과 같은 형식으로 처리하는 것이 좋다고 생각했다. 
catch (JwtException | IllegalArgumentException e){
        log.error("Token tampered");
        //예외처리 
    }

3. 토큰이 만료되었을 때 

처리 예외 : ExpiredJwtException

  • 토큰이 만료되었기 때문에, 클라이언트 입장에선 토큰을 재발급 받아야한다.
  • 클라이언트 개발자가 제일 많이 마주칠 수 있는 예외라고 생각한다. 

 

구현 코드 

 

- 해당 코드는 UserException이라는 커스텀 예외 클래스를 통해 클라이언트에게 전달하는 방식으로 구현되어있다.

해당 부분은 자유롭게 백엔드 개발자 입장에서 처리하고자 하는 방식으로 처리하면된다. 

try {
  String jwtToken = "eydfajlk...";
  Jws<Claims> parsedToken = jwtParser.parseClaimsJws(jwtToken);           
}
catch (NullPointerException e){ //토큰 입력 값이 없을 때 
        log.error("Token is null.");
        throw new UserException(EMPTY_JWT);
        }
catch (ExpiredJwtException e) { //올바른 토큰이지만, 만료
 		log.error("Token is Expired") 
        throw new UserException(EXPIRED_JWT); 
      }
catch (JwtException | IllegalArgumentException e){ //부적절한 토큰 
        log.error("Token tampered");
        throw new UserException(INVALID_JWT); 
      }

* throw new UserException(...) 부분은 상황에 맞게 예외처리하면 된다. 

'Auth' 카테고리의 다른 글

세션 기반 인증 vs 토큰 기반 인증  (0) 2023.08.30