이번엔 인증사이에 요청 저정하기.. 즉 인증하는 요청을 저장하는 것같습니다. 일단 내용을 확인해보겠습니다.
보안 예외처리에서 설명된 바와같이, 요청에 인증이 없고 인증이 필요한 리소스에 대한 요청을 할때, 인증이 성공한 후 인증된 리소스에 대한 요청을 다시 요청하기 위해 요청을 저장할 필요가 있습니다.(즉 같은 요청을 보내고 다시 요청할때 미리 저장하겠다는것 같네요) Spring Security 에서는 HttpServletRequest를 RequstCache 구현을 저장함으로써 이를 수행합니다.(캐시저장같네요)
RequstCache
HttpServletRequest는 RequestCache에 저장됩니다. 사용자가 성공적으로 인증할때. RequestCache는 원래 요청을 재생하는데 사용됩니다. (Request 재사용인듯합니다.) RequetsCacheAwarerFilter는 HttpServletRequest를 저장하기 위해 RequestCache를 사용하는것입니다.
기본적으로 HttpSessionRequestCache가 사용됩니다. 아래 코드는 continue라는 이름의 매개변수가 존재할 경우 HttpSession에서 저장된 요청을 확인하기 위해 사용되는 RequestCache 구현을 사용자 지정하는 방법을 보여줍니다.
아래 코드를 보죠.
@Bean
DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
// HttpSessionRequestCache의 인스턴스를 생성합니다. 이 클래스는 HTTP 요청을 세션에 저장하는 데 사용됩니다.
HttpSessionRequestCache requestCache = new HttpSessionRequestCache();
// 'continue'라는 매개변수가 있는 요청만을 HttpSessionRequestCache에 저장하도록 설정합니다.
requestCache.setMatchingRequestParameterName("continue");
http
// 다른 HttpSecurity 설정들...
.requestCache((cache) -> cache
// 사용자 정의 RequestCache를 HttpSecurity 설정에 적용합니다.
.requestCache(requestCache)
);
// 설정된 HttpSecurity 객체를 DefaultSecurityFilterChain 객체로 빌드하고 반환합니다.
return http.build();
}
Prevent the Request From Being Saved
요청 저장 방식이라는데요.
세션에서 사용자의 인증되지 않는 요청을 저장하지 않으려는 여러가지 이유가 잇을수 있습니다.
사용자의 브라우져에 그 저장소를 옮기거나 db에 저장하고 싶을수 있습니다. 또는 로그인 전에 방문하려고 했던 페이지 대신 항상 사용자를 홈페이지로 리다이렉트하고 싶어 이기능을 없애고 싶을수 있습니다.
Spring Security는 모든 보안 관련 이벤트를 Debug및 Trace레벨에서 포괄적으로 기록합니다.보안 조치로 인해 Spring Security는 요청이 거부된 이유를 응답 본문에 추가하지 않기 때문에, 애플리케이션 디버깅 시 이 기능이 매우 유용할 수 있습니다. 401 또는 403 오류를 만나면, 발생한 상황을 이해하는 데 도움이 될 로그 메시지를 찾을 가능성이 매우 높습니다.
예를 들어, 사용자가 CSRF 토큰 없이 CSRF 보호가 활성화된 리소스에 POST 요청을 시도하는 경우를 생각해봅시다. 로그가 없으면 사용자는 요청이 거부된 이유에 대한 설명 없이 403 오류를 보게 됩니다. 그러나 Spring Security의 로깅을 활성화하면 다음과 같은 로그 메시지를 볼 수 있습니다:"(기회되면 csrf에 대해서 간단히 정리해보겠습니다. 자주 언급되네요 생각보다 )
로깅:
2023-06-14T09:44:25.797-03:00 DEBUG 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Securing POST /hello
2023-06-14T09:44:25.797-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Invoking DisableEncodeUrlFilter (1/15)
2023-06-14T09:44:25.798-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Invoking WebAsyncManagerIntegrationFilter (2/15)
2023-06-14T09:44:25.800-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Invoking SecurityContextHolderFilter (3/15)
2023-06-14T09:44:25.801-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Invoking HeaderWriterFilter (4/15)
2023-06-14T09:44:25.802-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy : Invoking CsrfFilter (5/15)
2023-06-14T09:44:25.814-03:00 DEBUG 76975 --- [nio-8080-exec-1] o.s.security.web.csrf.CsrfFilter : Invalid CSRF token found for http://localhost:8080/hello
2023-06-14T09:44:25.814-03:00 DEBUG 76975 --- [nio-8080-exec-1] o.s.s.w.access.AccessDeniedHandlerImpl : Responding with 403 status code
2023-06-14T09:44:25.814-03:00 TRACE 76975 --- [nio-8080-exec-1] o.s.s.w.header.writers.HstsHeaderWriter : Not injecting HSTS header since it did not match request to [Is Secure]
(1) A review of Filter에서 설명한 바와 같이 'FilterChain.doFilter(request,response)'를 호출하는 것은 애플리케이션의 나머지 부분을 호출하는것과 같습니다. 이는 다른부분의 애플리케이션(예:FilterSecurityInterceptor 또는 메소드 보안)이 'AuthenticationException' 또는 'AccessDeniedException' 을 발생시킬 경우, 여기서 처리하는을 의미합니다.
(2) 사용자가 인증되지 않았거나 'AuthenticationException'인경우, 인증을 시작합니다.
FilterChainProxy는 결국 SecurityFilter Bean을 등록해주는 역할이라고 생각하면 좀 편할것 같습니다(맞겟죠? 제가 이해한게 ㅎㅎ 틀리면 지적 부탁드립니다 ㅠ )
첫번째는 Spring Security의 모든 서블릿 지원에 대한 시작점을 제공합니다.
그 이유로는 만약에 Spring Security 서블릿 지원시 트러블 슈트(문제해결) 시도 한다면 FilterChainProxy에 디버그 포인트를 추가하는것이 시작히기에는 좋은 방법이다.
라고 하는데요. 즉 문제 해결을 위해서 Spring Security 서블릿 지원에 관련해서는 시작점에서 제공한다는 얘기 같습니다.
두번째는 FilterChainProxy는 Spring Security 사용되는건 필수적이기 때문에 필수적으로 보이지 않는 작업들도 수행할수 있습니다.
즉 Spring Security는 사용되는건 무조건인데 별로 필요없는것들도 함께 작업이 수행될수 있다는 의미인듯합나다.
예를들어 메모리를 leak(부족)을 회피하기 위해 SecurityContext를 비웁니다. 또한 특정 유형의 공격으로부터 애플리케이션을 보호하기 위해서 HttpFirewall을 적용합니다.
(여기서 HttpFirewall은 Spring Security에서 웹 애플리케이션을 다양한 웹 기반 공격으로부터 보호하기 위해 요청을 필터링하는 보안 기능입니다. 나중에 HttpFirewall 에 대해서도 작성해보도록 하겠습니다 지금은 보안해주는 역할정도로만 이해하도록 하겠습니다.)
또한, SecurityFilterChain이 언제 호출될지 결정하는데 더 큰 유영성을 제공합니다. 서블릿 컨터이너에서, 필터 인스턴스는 URL에만 근거하여 호출이 됩니다. 그러나 FilterChaingProxy는 RequestMatchr 인터페이스를 사용하여 HttpServletRequet내의 어떤 것에 근거하여 호출을 결정할수 있습니다.
(여기서 RequestMatchr는 특정 조건에 따라 보안처리를 결정하는데 사용된다고 합니다. 저도 정확한건 찾아보고나서 따로 설명해도록 하겠습니다.)
매칭되는 첫번째는 SecurityFilterChain만 호출됩니다. 만약 api/message/url 요청딘다면, 이는 /api/ 패턴의 securityFilterChain0과 먼저 매칭되므로, SecurityFilterChain과도 매칭되더라도 오직 SecurityFilterChain(0)만 호출됩니다.
/message/url이 요청되면, 이는 /api/패턴의 SecurityFilterChain (0) 와 매칭되지 않으므로, FilterChainProxy는 다른 모든 SecurityFilterChain 을 시도합니다. 다른 어떤 SecurityFilterChain 인스턴스가 매칭되지 않는다면 SecurityFilterChain(n)이 호출됩니다.
그림을 보면
SecurityFilterChain(0)는 단 3개의 보안 필터 인스턴스만 설정되어 있음을 유의합니다. 그러나 SecurityFilterChain(n) 은 4개의 보안피러 인스턴스가 설정되어 있습니다. 각 SecurityFilterChain 은 고유할수 있으며, 독립적으로 구성할수 있습니다.
(즉 SecurityFilterChain 은 각각 역할로 url을 나눠서 필터링을 할수 있다는의미 같네요.)
실제로 어플리케이션이 특정 요청을 Spring Security가 무시하기를 원한다면 SecurityFilterChain 은 보안 필터 인스턴스 없이 구성할수 있습니다.
(음 보안 필터 없이 구성할수 있지만 왠만한 서비스는 전부 있어야는걸로 알고 잇어서 ㅎㅎ 악의적인 공격에 의해서 자칫하면 서버비 감당이 안되겟죠 ㅎㅎ)
대략 정리하면 DelegatingFilterProxy(FilterChainProxy)=>호출=> URL 매칭 SecurityFilterChain 선택-> SecurityFilterChain(0)..... SecurityFilterChain (N) 이런 느낌이네요.