요약
클라이언트가 액세스 토큰 재발급 요청을 제때 할 수 있도록, 만료된 토큰이 들어오면 약속된 에러 코드를 반환한다.
이슈
클라이언트(프론트엔드)가 액세스 토큰을 포함하여 요청을 보내면 401에러를 반환하고, 해당 에러를 받은 클라이언트는 액세스 토큰 재발급 요청을 보낸 후, 이전의 요청을 다시 하기로 약속이 되어 있었지만, 401에러가 반환되지 않는 상황이다.
현상황 테스트
현재 JWT Filter와 JWT Util에서 토큰을 검증하는 코드는 다음과 같다.
if (jwtUtil.isExpired(accessToken)) { System.out.println("token expired"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 401 에러 반환 response.setContentType("application/json"); response.getWriter().write("{\"error\": \"Access token expired\"}"); //응답 json에 error : access token expired메시지 작성 filterChain.doFilter(request, response); return; }
public Boolean isExpired(String token) { return Jwts.parser() .verifyWith(secretKey) .build() .parseSignedClaims(token) .getPayload() .getExpiration() .before(new Date()); }
토큰이 필요한 요청이 들어오면 토큰을 JWT Filter에서 JWT Util을 통해 검증하고, 만료된 토큰이라면 401에러를 반환하는 로직이다.
해당 코드로 스택 트레이스를 분석해보니, FWTFilter에서 토큰 만료시 "token expired"가 출력되지 않고, JWT Filter만 빙글빙글 돌았다.
문제 발생지
JWT Util에서 Exception(예외)를 던지지만, 해당 예외를 핸들링하지 않기 때문에 계속하여 문제가 생기는 것이였다.
Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token)
해당 검증 로직은 토큰이 유효하지 않은 형식을 가지고 있거나, 만료됐거나, 파싱중 문제가 발생하면 예외가 발생한다.
하지만 나는 토큰의 유효기간에 문제가 있으면 401에러를 반환해야 하므로 JWT Util에서 토큰 만료 예외가 발생하면 true를 반환하여 JWT Filter에서 401을 반환할 수 있도록 코드를 수정하였다.public boolean isExpired(String token) { try { // JWT 파서 생성 및 비밀키 설정 var jwtParser = Jwts.parser().verifyWith(secretKey).build(); // 토큰 파싱 및 클레임 추출 var claims = jwtParser.parseSignedClaims(token).getPayload(); // 만료 시간 추출 var expiration = claims.getExpiration(); // 만료 여부 확인 return expiration.before(new Date()); } catch (ExpiredJwtException e) { System.out.println("Token is expired: " + e.getMessage()); return true; } catch (MalformedJwtException e) { System.out.println("Malformed token: " + e.getMessage()); return false; } catch (UnsupportedJwtException e) { System.out.println("Unsupported token: " + e.getMessage()); return false; } catch (IllegalArgumentException e) { System.out.println("Illegal argument token: " + e.getMessage()); return false; } catch (Exception e) { System.out.println("Invalid token: " + e.getMessage()); return false; } }
결과
이제 401에러로 잘 반환된다.