<aside> 1️⃣ 토큰 에러 발생시 error.html로 오류페이지 이동을 안함
</aside>
{
"message": "OTHER TOKEN ERROR",
"code": "9999",
"status": 401
}
// doFilterInternal 메서드에서 예외 처리하는 부분
catch (Exception e) {
// Token 내에 Exception이 발생 하였을 경우 => 클라이언트에 응답값을 반환하고 종료합니다.
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
PrintWriter printWriter = response.getWriter();
JSONObject jsonObject = jsonResponseWrapper(e);
e.printStackTrace();
printWriter.print(jsonObject);
printWriter.flush();
printWriter.close();
}
/**
* 토큰 관련 Exception 발생 시 예외 응답값을 구성하는 부분
*/
private JSONObject jsonResponseWrapper(Exception e) {
String resultMessage = "";
// JWT 토큰 만료
if (e instanceof ExpiredJwtException) {
resultMessage = "TOKEN Expired";
}
// JWT 허용된 토큰이 아님
else if (e instanceof SignatureException) {
resultMessage = "TOKEN SignatureException Login";
}
// JWT 토큰내에서 오류 발생 시
else if (e instanceof JwtException) {
resultMessage = "TOKEN Parsing JwtException";
}
// 이외 JTW 토큰내에서 오류 발생
else {
resultMessage = "OTHER TOKEN ERROR";
}
HashMap<String, Object> jsonMap = new HashMap<>();
jsonMap.put("status", 401);
jsonMap.put("code", "9999");
jsonMap.put("message", resultMessage);
jsonMap.put("reason", e.getMessage());
JSONObject jsonObject = new JSONObject(jsonMap);
logger.error(resultMessage, e);
return jsonObject;
}
ProfileApplicationException.class
로 토큰에러를 던졌기에 여기서 잡힐줄 알았다.package com.jinan.profile.exception;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
/**
* 중앙 집중 예외처리를 위한 GlobalControllerAdvice 작성
*/
@Slf4j
@ControllerAdvice
public class GlobalControllerAdvice {
/**
* ProfileApplicationException 발생했을 때 호출되는 메서드
* 에러 페이지를 보여주고, 에러 메시지를 모델에 추가한다.
* <p th:text="${errorMessage}"></p> 이런식으로 타임리프에 에러페이지를 만들고 가져다 사용한다.
*/
@ExceptionHandler(ProfileApplicationException.class)
public ModelAndView handleProfileApplicationException(ProfileApplicationException e) {
log.error("Error occurs {}", e.toString());
// Spring MVC는 알아서 'error'라는 이름의 뷰를 찾아서 렌더링한다.
ModelAndView mav = new ModelAndView("error"); // 에러 페이지의 뷰 이름
mav.addObject("errorMessage", e.getErrorCode().getMessage()); // 에러 메시지를 모델에 추가
return mav;
}
/**
* RuntimeException이 발생했을 때 호출되는 메서드
* 에러 페이지를 보여주고, 에러 메시지를 모델에 추가한다.
*/
@ExceptionHandler(RuntimeException.class)
public ModelAndView handleRuntimeException(RuntimeException e) {
log.error("Error occurs {}", e.toString());
// Spring MVC는 알아서 'error'라는 이름의 뷰를 찾아서 렌더링한다.
ModelAndView mav = new ModelAndView("error"); // 에러 페이지의 뷰 이름
mav.addObject("errorMessage", e.getMessage()); // 에러 메시지를 모델에 추가
return mav;
}
}
<aside> 2️⃣ 문제발생 이유
</aside>
JwtAuthorizationFilter
**의 doFilterInternal
메서드에서 예외를 처리하는 방식은 HTTP 응답에 직접 JSON 형태의 에러 메시지를 작성하고 반환하는 방식이다. 따라서, 이 예외는 Spring의 **@ExceptionHandler
**로 전파되지 않는다.@ExceptionHandler
는 Spring MVC의 컨트롤러에서 발생하는 예외를 처리하기 위한 메커니즘이며, 필터나 인터셉터에서 발생하는 예외는 이 메커니즘의 범위 밖에 있다.JwtAuthorizationFilter
**에서 발생하는 예외를 **@ExceptionHandler
**로 처리하려면, 필터에서 예외를 던지고 이를 Spring의 **DispatcherServlet
**이 처리하도록 해야한다. 그러나 이 방식은 보안 필터에서 일반적으로 사용되지 않는다. 필터에서 직접 응답을 처리하는 것이 일반적이다.@ExceptionHandler
**를 사용하여 ProfileApplicationException
예외를 처리하려면 다음과 같은 방법을 시도해볼 수 있다.
JwtAuthorizationFilter
**의 doFilterInternal
메서드에서 예외를 catch하지 않고 던지도록 변경한다.ProfileApplicationException
예외를 Spring의 RuntimeException
또는 **AuthenticationException
**으로 변경하거나 확장한다.@ExceptionHandler
**로 처리하도록 한다.error.html
페이지를 보여주고 싶다면, **JwtAuthorizationFilter
**에서 직접 에러 페이지를 렌더링하거나 리다이렉트하는 방식을 사용해야 한다.<aside> 3️⃣ 1차 해결시도
</aside>
catch (Exception e) {
// Token 내에 Exception이 발생 하였을 경우 => 클라이언트에 응답값을 반환하고 종료합니다.
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
PrintWriter printWriter = response.getWriter();
JSONObject jsonObject = jsonResponseWrapper(e);
e.printStackTrace();
printWriter.print(jsonObject);
printWriter.flush();
printWriter.close();
}