JWT 토큰을 검증해서 인증된 사용자 객체를 저장하는 스프링 시큐리티를 적용했다. 시큐리티 필터 내에서 인증 과정을 진행하던 중에 exception이 발생한 경우, 상황에 따라 exception 처리를 할 수 있도록
AuthenticationEntryPoint
를 구현하여 예외처리를 해보자!
try-catch
구문을 사용해서, @ExceptionHandler
로 처리할 수 있다.ExceptionTranslationFilter
를 통해 Security Filter 내 예외처리를 한다. 이 필터는 보호된 리소스에 접근할 때 발생하는 예외를 처리하며, AuthenticationEntryPoint
를 사용하여 인증되지 않은 사용자를 처리할 수 있다.request.setAttribute("exception", ErrorType.NOT_TOKEN);
AuthenticationEntryPoint
를 구현한 클래스에서 request의 속성 중 “exception” 값을 보고 해당하는 예외에 맞게 처리해준다.@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
ErrorType exception = (ErrorType) request.getAttribute("exception");
if (exception.equals(ErrorType.NOT_TOKEN)) {
exceptionHandler(response, ErrorType.NOT_TOKEN);
return;
}
}
public void exceptionHandler(HttpServletResponse response, ErrorType error) {
response.setStatus(error.getCode());
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
try {
String json = new ObjectMapper().writeValueAsString(ResponseUtils.error(ErrorResponse.of(error)));
response.getWriter().write(json);
log.error(error.getMessage());
} catch (Exception e) {
log.error(e.getMessage());
}
}
@Slf4j
@RequiredArgsConstructor
public class JwtAuthFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// request 에 담긴 토큰을 가져온다.
String token = jwtUtil.resolveToken(request);
// 토큰이 null 이면 다음 필터로 넘어간다
if (token == null) {
request.setAttribute("exception", ErrorType.NOT_TOKEN);
filterChain.doFilter(request, response);
return;
}
// 토큰이 유효하지 않으면 다음 필터로 넘어간다
if (!jwtUtil.validateToken(token)) {
request.setAttribute("exception", ErrorType.NOT_VALID_TOKEN);
filterChain.doFilter(request, response);
return;
}
// 유효한 토큰이라면, 토큰으로부터 사용자 정보를 가져온다.
Claims info = jwtUtil.getUserInfoFromToken(token);
try {
setAuthentication(info.getSubject()); // 사용자 정보로 인증 객체 만들기
} catch (UsernameNotFoundException e) {
request.setAttribute("exception", ErrorType.NOT_FOUND_USER);
}
// 다음 필터로 넘어간다.
filterChain.doFilter(request, response);
}
private void setAuthentication(String loginId) {
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication = jwtUtil.createAuthentication(loginId); // 인증 객체 만들기
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
}
}
@Slf4j
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
ErrorType exception = (ErrorType) request.getAttribute("exception");
if (exception.equals(ErrorType.NOT_TOKEN)) {
exceptionHandler(response, ErrorType.NOT_TOKEN);
return;
}
if (exception.equals(ErrorType.NOT_VALID_TOKEN)) {
exceptionHandler(response, ErrorType.NOT_VALID_TOKEN);
return;
}
if (exception.equals(ErrorType.NOT_FOUND_USER)) {
exceptionHandler(response, ErrorType.NOT_FOUND_USER);
}
}
public void exceptionHandler(HttpServletResponse response, ErrorType error) {
response.setStatus(error.getCode());
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
try {
String json = new ObjectMapper().writeValueAsString(ResponseUtils.error(ErrorResponse.of(error)));
response.getWriter().write(json);
log.error(error.getMessage());
} catch (Exception e) {
log.error(e.getMessage());
}
}
}