Spring Security ‐ Servlet Application Architecture - taeyun-ham/andalos GitHub Wiki

Description

  • 공식 가이드 문서 기반으로 필요한 부분만 작성했습니다.

Version

Architecture

A Review of Filters

Spring Security의 서블릿 지원은 서블릿 필터를 기반으로 하므로 일반적으로 필터의 역할을 먼저 살펴보는 것이 도움이 됩니다. 다음 이미지는 단일 HTTP 요청에 대한 일반적인 핸들러 계층을 보여줍니다.

filterchain

클라이언트는 애플리케이션에 요청을 보내고 컨테이너는 요청 URI의 경로를 기반으로 HttpServletRequest를 처리해야 하는 필터 인스턴스와 서블릿을 포함하는 FilterChain을 생성합니다. Spring MVC 애플리케이션에서 서블릿은 DispatcherServlet의 인스턴스입니다. 최대 하나의 서블릿은 단일 HttpServletRequest 및 HttpServletResponse를 처리할 수 있습니다. 그러나 둘 이상의 필터를 사용하여 다음을 수행할 수 있습니다.

DelegatingFilterProxy

Spring은 서블릿 컨테이너의 라이프사이클과 Spring의 ApplicationContext 사이를 연결하는 DelegatingFilterProxy라는 필터 구현을 제공합니다. 서블릿 컨테이너는 자체 표준을 사용하여 필터 인스턴스를 등록할 수 있지만 Spring에서 정의한 Bean을 인식하지 못합니다. 표준 서블릿 컨테이너 메커니즘을 통해 DelegatingFilterProxy를 등록할 수 있지만 필터를 구현하는 Spring Bean에 모든 작업을 위임할 수 있습니다. 다음은 DelegatingFilterProxy가 필터 인스턴스와 FilterChain에 어떻게 적용되는지 보여주는 그림입니다.

delegatingfilterproxy

DelegatingFilterProxy는 ApplicationContext에서 Bean Filter0을 조회한 다음 Bean Filter0을 호출합니다. DelegatingFilterProxy의 또 다른 이점은 Filter Bean 인스턴스 검색을 지연시킬 수 있다는 것입니다. 컨테이너가 시작되기 전에 컨테이너가 필터 인스턴스를 등록해야 하기 때문에 이는 중요합니다. 그러나 Spring은 일반적으로 ContextLoaderListener를 사용하여 Spring Bean을 로드하는데, 이는 Filter 인스턴스를 등록해야 할 때까지 수행되지 않습니다.

FilterChainProxy

Spring Security의 Servlet 지원은 FilterChainProxy 내에 포함되어 있습니다. FilterChainProxy는 SecurityFilterChain을 통해 많은 필터 인스턴스에 위임을 허용하는 Spring Security에서 제공하는 특수 필터입니다. FilterChainProxy는 Bean이므로 일반적으로 DelegatingFilterProxy에 래핑됩니다. 다음 이미지는 FilterChainProxy의 역할을 보여줍니다.

filterchainproxy

SecurityFilterChain

SecurityFilterChain은 현재 요청에 대해 호출되어야 하는 Spring Security Filter 인스턴스를 결정하기 위해 FilterChainProxy에서 사용됩니다. 다음 이미지는 SecurityFilterChain의 역할을 보여줍니다.

securityfilterchain

SecurityFilterChain의 보안 필터는 일반적으로 Bean이지만 DelegatingFilterProxy 대신 FilterChainProxy로 등록됩니다. FilterChainProxy는 Servlet 컨테이너 또는 DelegatingFilterProxy에 직접 등록할 때 여러 가지 이점을 제공합니다.

첫째, Spring Security의 모든 서블릿 지원에 대한 시작점을 제공합니다. 이러한 이유로 Spring Security의 Servlet 지원 문제를 해결하려는 경우 FilterChainProxy에 디버그 지점을 추가하는 것이 좋은 시작점입니다.

둘째, FilterChainProxy는 Spring Security 사용의 핵심이므로 선택 사항으로 간주되지 않는 작업을 수행할 수 있습니다. 예를 들어 메모리 누수를 방지하기 위해 SecurityContext를 지웁니다. 또한 특정 유형의 공격으로부터 애플리케이션을 보호하기 위해 Spring Security의 HttpFirewall을 적용합니다.

또한 SecurityFilterChain을 호출해야 하는 시기를 결정하는 데 더 많은 유연성을 제공합니다. 서블릿 컨테이너에서 필터 인스턴스는 URL만을 기반으로 호출됩니다. 그러나 FilterChainProxy는 RequestMatcher 인터페이스를 사용하여 HttpServletRequest의 모든 항목을 기반으로 호출을 결정할 수 있습니다.

다음 이미지는 여러 SecurityFilterChain 인스턴스를 보여줍니다.

multi-securityfilterchain

Multiple SecurityFilterChain 그림에서 FilterChainProxy는 어떤 SecurityFilterChain을 사용해야 하는지 결정합니다. 일치하는 첫 번째 SecurityFilterChain만 호출됩니다. /api/messages/의 URL이 요청되면 먼저 /api/**의 SecurityFilterChain0 패턴과 일치하므로 SecurityFilterChain0에서도 일치하더라도 SecurityFilterChain0만 호출됩니다. /messages/의 URL이 요청되면 /api/**의 SecurityFilterChain0 패턴과 일치하지 않으므로 FilterChainProxy는 계속해서 각 SecurityFilterChain을 시도합니다. 일치하는 다른 SecurityFilterChain 인스턴스가 없다고 가정하면 SecurityFilterChainn이 호출됩니다.

SecurityFilterChain0에는 보안 필터 인스턴스가 3개만 구성되어 있습니다. 그러나 SecurityFilterChainn에는 4개의 보안 필터 인스턴스가 구성되어 있습니다. 각 SecurityFilterChain은 고유할 수 있으며 별도로 구성될 수 있다는 점에 유의하는 것이 중요합니다. 실제로, 애플리케이션이 Spring Security가 특정 요청을 무시하도록 하려는 경우 SecurityFilterChain에는 보안 필터 인스턴스가 없을 수 있습니다.

URL 별로 다른 보안전략을 세우면 될거 같습니다. 예를 들어 SecurityFilterChain0 는 OAuth2 의 인가 서버 구성, SecurityFilterChain1 는 OAuth2 의 리소스 서버 구성, SecurityFilterChain2 는 자체 서비스 인증 (OAuth2 의 Client) 등

Security Filters

보안 필터는 SecurityFilterChain API를 사용하여 FilterChainProxy에 삽입됩니다. 이러한 필터는 인증, 권한 부여, 악용 방지 등과 같은 다양한 목적으로 사용될 수 있습니다. 필터는 적시에 호출되도록 특정 순서로 실행됩니다. 예를 들어 인증을 수행하는 필터는 인증을 수행하는 필터보다 먼저 호출되어야 합니다. 일반적으로 Spring Security 필터의 순서를 알 필요는 없습니다. 하지만 순서를 아는 것이 도움이 될 때가 있는데, 알고 싶다면 FilterOrderRegistration 코드를 확인하면 됩니다.

총 35개의 Filters

  • DisableEncodeUrlFilter
  • ForceEagerSessionCreationFilter
  • ChannelProcessingFilter
  • WebAsyncManagerIntegrationFilter
  • SecurityContextHolderFilter
  • SecurityContextPersistenceFilter
  • HeaderWriterFilter
  • CorsFilter
  • CsrfFilter
  • LogoutFilter
  • OAuth2AuthorizationRequestRedirectFilter
  • Saml2WebSsoAuthenticationRequestFilter
  • X509AuthenticationFilter
  • AbstractPreAuthenticatedProcessingFilter
  • CasAuthenticationFilter
  • OAuth2LoginAuthenticationFilter
  • Saml2WebSsoAuthenticationFilter
  • UsernamePasswordAuthenticationFilter
  • DefaultLoginPageGeneratingFilter
  • DefaultLogoutPageGeneratingFilter
  • ConcurrentSessionFilter
  • DigestAuthenticationFilter
  • BearerTokenAuthenticationFilter
  • BasicAuthenticationFilter
  • RequestCacheAwareFilter
  • SecurityContextHolderAwareRequestFilter
  • JaasApiIntegrationFilter
  • RememberMeAuthenticationFilter
  • AnonymousAuthenticationFilter
  • OAuth2AuthorizationCodeGrantFilter
  • SessionManagementFilter
  • ExceptionTranslationFilter
  • FilterSecurityInterceptor
  • AuthorizationFilter
  • SwitchUserFilter

Handling Security Exceptions

ExceptionTranslationFilter를 사용하면 AccessDeniedException 및 AuthenticationException을 HTTP 응답으로 변환할 수 있습니다. ExceptionTranslationFilter는 보안 필터 중 하나로 FilterChainProxy에 삽입됩니다. 다음 이미지는 ExceptionTranslationFilter와 다른 구성 요소의 관계를 보여줍니다.

exceptiontranslationfilter

  1. 첫번째 ExceptionTranslationFilter는 FilterChain.doFilter(request, response)를 호출하여 애플리케이션의 나머지 부분을 호출합니다.
  2. AuthenticationException인 경우 인증을 시작합니다.
    1. SecurityContextHolder가 지워집니다.
    1. HttpServletRequest는 인증이 성공하면 원래 요청을 재생하는 데 사용할 수 있도록 저장됩니다.
    1. AuthenticationEntryPoint는 클라이언트로부터 자격 증명을 요청하는 데 사용됩니다. 예를 들어 로그인 페이지로 리디렉션되거나 WWW-Authenticate 헤더를 보낼 수 있습니다.
  1. 그렇지 않고 AccessDeniedException이면 액세스가 거부됩니다. AccessDeniedHandler는 거부된 액세스를 처리하기 위해 호출됩니다.

애플리케이션이 AccessDeniedException 또는 AuthenticationException을 발생시키지 않는 경우 ExceptionTranslationFilter는 아무 작업도 수행하지 않습니다.

Saving Requests Between Authentication

보안 예외 처리에 설명된 대로 요청에 인증이 없고 인증이 필요한 리소스에 대한 요청인 경우 인증 성공 후 다시 요청하려면 인증된 리소스에 대한 요청을 저장해야 합니다. Spring Security에서는 RequestCache 구현을 사용하여 HttpServletRequest를 저장함으로써 이를 수행합니다.

RequestCache

HttpServletRequest는 RequestCache에 저장됩니다. 사용자가 성공적으로 인증되면 RequestCache를 사용하여 원래 요청을 재생합니다. RequestCacheAwareFilter는 RequestCache를 사용하여 HttpServletRequest를 저장하는 것입니다.기본적으로 HttpSessionRequestCache가 사용됩니다.

요청이 저장되지 않도록 방지 할 수 있습니다.

사용자의 인증되지 않은 요청을 세션에 저장하지 않으려는 데에는 여러 가지 이유가 있습니다. 해당 스토리지를 사용자의 브라우저로 오프로드하거나 데이터베이스에 저장할 수 있습니다. 또는 사용자가 로그인하기 전에 방문하려고 시도한 페이지 대신 항상 홈 페이지로 사용자를 리디렉션하기를 원하므로 이 기능을 종료할 수도 있습니다.

@Bean
SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
    RequestCache nullRequestCache = new NullRequestCache();
    http
        // ...
        .requestCache((cache) -> cache
            .requestCache(nullRequestCache)
        );
    return http.build();
}

RequestCacheAwareFilter

RequestCacheAwareFilter는 RequestCache를 사용하여 HttpServletRequest를 저장합니다.