// parser을 이용해서, jwt token 검증 메서드
public boolean validToken(String token){
try{
makeParser()
.parseClaimsJws(token);
return true;
} catch (Exception e){
if (e instanceof SignatureException){
throw new TokenException(TokenErrorCode.INVALID_TOKEN);
}
else if (e instanceof ExpiredJwtException) {
// 만료된 토큰
throw new TokenException(TokenErrorCode.EXPIRED_AT,e);
}
else {
// 그 외 처리
throw new TokenException(TokenErrorCode.INVALID_TOKEN,e);
}
}
}
@RestControllerAdvic와 @ExceptionHandler을 통해서 예외를 처리하였다TokenExceptionHandler를 exceptionHandler경로에 만들어 주었다@Slf4j
@RestControllerAdvice
@Order(value = Integer.MIN_VALUE)
public class TokenExceptionHandler {
@ExceptionHandler(value = TokenException.class)
public ResponseEntity<Api<Object>> apiException(TokenException tokenException){
log.error("", tokenException);
ErrorCodeIfs errorCodeIfs = tokenException.getErrorCodeIfs();
return ResponseEntity
.status(errorCodeIfs.getHttpStatusCode())
.body(
Api.Error(errorCodeIfs, tokenException.getErrorDescription())
);
}
}

RestControllerAdvice가 눈에 보였다@RestControllerAdvice의 스펙을 확인하였다
DispatcherServlet에 의해 처리되는 경우 작동한다고 한다.✅Security Filter는 DispatcherServlet에 도달하기 전에 먼저 거쳐지는 곳이기 때문에 @ControllerAdvice에서 예외처리를 해줄 수 없는 것이다.즉 filter 측에서 예외처리를 한 후, 예외를 처리한 후 DispatcherServlet으로 넘겨줘야 된다고 판단하였다!
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// request에서 Authorization 헤더를 찾기
String authorization = request.getHeader(HEADER_AUTHORIZATION);
// Authorization 헤더 검증 -> jwt token인지
if (authorization == null || !authorization.startsWith("Bearer ")){
log.info("token null");
filterChain.doFilter(request,response);
// 다음 조건이 해당하면 -> 메서드 종료
return;
}
// 가져온 값에서 접두사 제거 -> 토큰 꺼내 오기
String token = getAccessToken(authorization);
if (jwtUtil.validToken(token)){
// 토큰에서 username, role 획득
String email = jwtUtil.getUsername(token);
String role = jwtUtil.getRole(token);
// userEntity를 생성해서 해당 값을 넣어준다
UserEntity userEntity = UserEntity.builder()
.email(email)
.role(role)
.password("temppassword")
.build();
//UserDetails에 회원 정보 객체 담기
CustomUserDetails customUserDetails = new CustomUserDetails(userEntity);
//스프링 시큐리티 인증 토큰 생성
Authentication authToken = new UsernamePasswordAuthenticationToken(customUserDetails, null, customUserDetails.getAuthorities());
//세션에 사용자 등록
SecurityContextHolder.getContext().setAuthentication(authToken);
}
filterChain.doFilter(request, response);
}

우리는 생각을 해보면, 여러 filter들이 연속적으로 doFilter을 통해서 계속 순차적으로 작동되는 사실을 알고 있다
결국 예외처리를 위한 Filter을 만들어 커스텀 할 수 있다는 소리였다!
그러므로 ✅Filter에서 발생하는 예외를 핸들링하려면, 예외 발생이 예상되는 Filter의 ✅상위에 예외를 핸들링하는 Filter를 만들어서 ✅Filter Chain에 추가해주면 된다
@Component
public class JwtExceptionHandler extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
try {
filterChain.doFilter(request,response);
} catch (TokenException e){
response.setStatus(e.getErrorCodeIfs().getHttpStatusCode());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding("UTF-8");
String error = objectMapper.writeValueAsString(Result.Error(e.getErrorCodeIfs()));
response.getWriter().write(error);
}
}
}
.authorizeHttpRequests((auth) -> auth
.requestMatchers("/login", "/", "/join").permitAll()
.requestMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated())
.addFilterBefore(new JwtFilter(jwtUtil), LoginFilter.class)
.addFilterAt(new LoginFilter(authenticationManager(authenticationConfiguration), jwtUtil), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new JwtExceptionHandler(), JwtFilter.class)
// jwt는 세션을 stateless하게 관리한다
.sessionManagement((session) -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.build();
addFilterBefore을 사용하였다!