보통 일반적인 예외를 발생시키면 아래처럼 명확한 JSON 응답을 받을 수 있다.
하지만 Spring Security를 사용하게 되면, 예외가 다음과 같이
내부적으로 숨겨진 채 처리되며,
우리가 정의한 예외 메시지가 노출되지 않는 현상이 발생한다.도대체 무엇 때문에 이렇게 되는 걸까?
Spring Security 내부 동작 구조를 따라가며 그 원인을 찾아보자.
위와 같이, 나는 "이미 가입된 이메일입니다."라는 상황을 나타내기 위해
ResponseStatusException을 이용해 명시적으로 예외를 던졌다.
하지만 실제 응답은…

403 응답 코드만 전달될 뿐,
내가 지정한 메시지("이미 가입된 이메일입니다.")는 응답 본문에 전혀 포함되지 않았다.
Spring Security는 인증, 인가, 그리고 보안 관련 정책을 처리하기 위해 HTTP 요청이 DispatcherServlet에 도달하기 전에 모든 것을 검사해야 한다.
이를 위해 Spring Security는 자체적으로 FilterChainProxy라는 서블릿 필터를 만들어 등록한다.
하지만 공식 자료에서 보여주는 그림은 Spring Security 필터 다른 필터 사이에 있는걸 확인할 수 있다.
하지만, 이거는 다른 필터들 사이에 존재할 수 있다는 가능성을 보여주는 구조도다.
즉, 이 그림은 필터 순서를 커스터마이징할 수 있다는 의미지,
항상 이 순서대로 실행된다는 걸 보장하지는 않는다.
기본적으로는 DelegatingFilterProxy 가 가장 먼저 등록되어 실행된다.
Filter 예외는 왜 500 만 떴던 걸까? + 인천국제공항에서 배운 보안 이 글들을 참고해보자.
우리는 이전 글에서, Spring Security는 애플리케이션이 시작될 때
사용자가 작성한 Security 설정 클래스(@Configuration) 를 읽고,
그 안의 설정대로 필터 체인을 구성하여 등록한다는 것을 확인했다.즉, 서버가 켜질 때
SecurityFilterChain이 초기화되며,
우리가 정의한 보안 규칙(인증, 인가, 예외 처리 등)은 모두 이 필터 체인을 통해 동작하게 된다.
하, 이것도 결국 서블릿 레벨에서 발생하는 예외라서 403이 뜨는 거구나 즉, 컨트롤러 레벨(@Controller, @RestController) 에 도달하기 전에 Spring Security의 필터 체인에서 예외가 발생하고, 그 과정에서 내가 의도한 메시지는 응답 본문에 포함되지 않고 버려지는 것이구나.
하지만, 조금만 생각해도 이상하지 않은가?
어째서 403 에러인거지?

디버그를 실행시켜보니, 예외까지는 잘 온다.
즉, 서블릿 레벨에서 걸러지지 않는다.
어렵지 않았다. 예외를 글로벌로 잡게하면 된다.


매우 잘 해결된다
보통 403 에러는 권한 쪽 문제이다.
내가 예상하는 건.., Spring security 쪽에서 예외를 가로 챈다음에 권한문제 넘기는 느낌이다.
답답해서 디버그 모드로 타고 들어갔다.

SecurityContextHolder어디서 많이 봤던거다.
10번에서 걸린다.
확실한 사안은 아니지만, 생각해보면.. 해보면 다음과 같다.
ResponseStatusException -> SecurityContextHolder ->
FilterSecurityInterceptor(여기서 401 인지 403인지 던지고) -> ExceptionTranslationFilter 가 잡음
그래서 결국 로직에는 FilterSecurityInterceptor가 잡은 예외가 나오는 것이다.
하지만, 전역예외로 잡은건 SecurityContextHolder로 가지않기에 처리가 되는 것.
나름 디버그를 활용해서 어떤 문제인지 조금씩 실마리를 잡아나갈 수 있었다.
처음엔 ResponseStatusException을 던졌는데 403이 뜨는 게 이상하다고만 느꼈지만,
디버깅을 하면서 SecurityContextHolder나 ExceptionTranslationFilter 같은
Spring Security 내부 흐름에서 뭔가 개입이 있었던 걸 확인할 수 있었다.
정확히 확신할 순 없지만,
지금으로선 Spring Security가 컨트롤러에 도달하기 전 예외를 가로채서,
인가 실패처럼 처리하고 403을 응답한 게 아닐까? 라는 추측이 든다.
응답이 언제 커밋되고, 예외가 어디서 터졌는지를 따지는 게
Spring Security 환경에선 특히 중요한 것 같다.
Problem with Costume Exception handler for 403 Forbidden in Spring Boot
Why is Spring ResponseStatusException 400 translated into 403
Spring Security in response to registration I get a 403 error