This section discusses the high-level architecture of Spring Security in servlet-based applications.

A brief description of filters

Spring Security's servlet support is based on Filter servlet instances, so it's useful to first look at the role of Filter in general. The figure below shows a typical layered structure of handlers for a single HTTP request.

The client sends a request to the application, and the container creates a FilterChain, which contains instances of a Filter and a Servlet that should process a HttpServletRequest based on the request URI path. In a Spring MVC application, Servlet is an instance of DispatcherServlet. Only one Servlet can handle one HttpServletRequest and HttpServletResponse. However, you can use more than one Filter to:

  • Prevent calls to downstream Filter or Servlet . In this case, Filter typically records HttpServletResponse.

  • Changes to HttpServletRequest or HttpServletResponse used by subsequent Filter and Servlet

The power of Filter depends on FilterChain, which is passed into it.

Example of using FilterChain
Java

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
    // do something before executing the rest of the application
    chain.doFilter(request, response) ;// invoke the rest of the application
    // do something after the rest of the application is executed
}
Kotlin

fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
    // do something before executing the rest of the application
    chain.doFilter(request, response)// invoke the rest of the application
    // do something after the rest of the application has executed
}

Because the Filter only affects the underlying Filter and Servlet, the order in which each Filter is called is extremely important.

DelegatingFilterProxy

Spring provides an implementation of Filter titled DelegatingFilterProxy, which allows you to establish a bridge between the lifecycle of a servlet container and ApplicationContext from Spring. The servlet container allows you to register Filter instances using its own standards, but it does not know about the beans defined by Spring. DelegatingFilterProxy can be registered through standard servlet container mechanisms, but delegates all the work to the Spring bean that implements Filter.

Here is how DelegatingFilterProxy is used fits into a schema with Filter and FilterChain instances.

DelegatingFilterProxy looks up Bean Filter0 in ApplicationContext and then calls Bean Filter0. The pseudo code for DelegatingFilterProxy is shown below.

Pseudo code for DelegatingFilterProxy
Java

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
    // In deferred mode, we get the filter that was registered as a Spring bean
    // For the example in DelegatingFilterProxy, the delegate is instance Bean Filter0
    Filter delegate = getFilterBean(someBeanName);
    // delegate the work to the Spring bean delegate.doFilter(request, response);
}
Kotlin

fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
    // In deferred mode, we get the filter that was registered as a Spring bean
    // For the example in DelegatingFilterProxy, the delegate is an instance of Bean Filter0
    val delegate: Filter = getFilterBean(someBeanName)
    // delegate work with the Spring bean delegate.doFilter(request, response)
}

Another advantage of DelegatingFilterProxy is that it allows you to defer the search for instances of Filter beans. This is important because the container needs to register Filter instances before running the container. However, Spring typically uses ContextLoaderListener to load Spring beans, which only happens after Filter instances have been registered.

FilterChainProxy

Servlet Facilities in Spring Security they are contained in FilterChainProxy. FilterChainProxy is a specialized Filter provided by Spring Security that allows you to delegate authority to multiple Filter instances via <SecurityFilterChain. Since FilterChainProxy is a bean, it is usually wrapped in DelegatingFilterProxy.

SecurityFilterChain

SecurityFilterChain is used by FilterChainProxy to determine which Filter from Spring Security should be called for a given request.

Security filters in SecurityFilterChain are usually beans, but they are registered in FilterChainProxy instead of DelegatingFilterProxy. FilterChainProxy provides several advantages over registering directly with the servlet container or DelegatingFilterProxy. First, it provides the starting point for all servlet support in Spring Security. For this reason, if you are trying to troubleshoot Spring Security's servlet support, a good place to start is by adding a debug point to FilterChainProxy.

Secondly, because FilterChainProxy is central to the use of Spring Security, it can perform tasks that are not considered optional. For example, it cleans up SecurityContext to avoid memory leaks. It also uses HttpFirewall from Spring Security to protect applications from certain types of attacks.

It also provides greater flexibility in determining when to call SecurityFilterChain. In a servlet container, Filter instances are called based on the URL only. However, FilterChainProxy can determine the call based on anything in HttpServletRequest using the RequestMatcher interface.

In fact, FilterChainProxy can be used to determine which SecurityFilterChain should be used. This allows you to provide completely separate configuration for different parts of your application.

In the picture with several SecurityFilterChain, the FilterChainProxy instance decides which SecurityFilterChain should be used. Only the first matching SecurityFilterChain will be called. If the URL /api/messages/ is requested, it will first be matched against the pattern SecurityFilterChain0 via /api/**, so only SecurityFilterChain0 will be called, even if it also matches SecurityFilterChainn. If the /messages/ URL is requested, it will not be matched against the SecurityFilterChain0 pattern via /api/**, so FilterChainProxy will continue to iterate over each SecurityFilterChain. It is assumed that other instances of SecurityFilterChain that match SecurityFilterChainn will not be called.

Note that in SecurityFilterChain0 only three instances of Filter are configured. However, there are four Filters configured in the SecurityFilterChainn. It is important to note that each SecurityFilterChain can be unique and configured in isolation. In fact, SecurityFilterChain can have zero Filter instances if the application wants Spring Security to ignore certain requests.

Security Filters

Spring Security filters are added to FilterChainProxy via the SecurityFilterChain API. The order of Filter instances matters. Typically there is no need to know the order of Filter in Spring Security. However, in some cases it is useful to know what order they are in.

The following is a complete ordered list of Spring Security filters:

  • ForceEagerSessionCreationFilter

  • ChannelProcessingFilter

  • WebAsyncManagerIntegrationFilter

  • SecurityContextPersistenceFilter

  • HeaderWriterFilter

  • CorsFilter

  • CsrfFilter

  • LogoutFilter

  • OAuth2AuthorizationRequestRedirectFilter

  • Saml2WebSsoAuthenticationRequestFilter

  • X509AuthenticationFilter

  • AbstractPreAuthenticatedProcessingFilter

  • CasAuthenticationFilter

  • OAuth2LoginAuthenticationFilter

  • Saml2WebSsoAuthenticationFilter

  • UsernamePasswordAuthenticationFilter

  • OpenIDAuthenticationFilter

  • DefaultLoginPageGeneratingFilter

  • DefaultLogoutPageGeneratingFilter

  • ConcurrentSessionFilter

  • DigestAuthenticationFilter

  • BearerTokenAuthenticationFilter

  • BasicAuthenticationFilter

  • RequestCacheAwareFilter

  • SecurityContextHolderAwareRequestFilter

  • JaasApiIntegrationFilter

  • RememberMeAuthenticationFilter

  • AnonymousAuthenticationFilter

  • OAuth2AuthorizationCodeGrantFilter

  • SessionManagementFilter

  • ExceptionTranslationFilter

  • FilterSecurityInterceptor

  • SwitchUserFilter

Security exception handling

ExceptionTranslationFilter allows conversion AccessDeniedException and AuthenticationException in HTTP responses.

ExceptionTranslationFilter is added to FilterChainProxy in quality of one of the security filters.

  • 1 First ExceptionTranslationFilter accesses FilterChain.doFilter(request, response) to call the rest of the application.

  • 2 If the user is not authenticated or an AuthenticationException occurs, then run authentication procedure.

    • TheSecurityContextHolder is cleared

    • HttpServletRequest is saved in RequestCache. Once the user is successfully authenticated, RequestCache will be used to replay the original request.

    • AuthenticationEntryPoint is used to request credentials client data. For example, it could redirect to a login page or send a WWW-Authenticate header.

  • 3 Otherwise, if AccessDeniedException occurs, access will be denied. AccessDeniedHandler is called to handle access denial.

If the application does not throw an AccessDeniedException or AuthenticationException, then the ExceptionTranslationFilter does nothing.

The pseudocode for the ExceptionTranslationFilter looks something like this:

ExceptionTranslationFilter pseudocode

try {
    filterChain.doFilter(request, response); 
} catch (AccessDeniedException | AuthenticationException ex) {
    if (!authenticated || ex instanceof AuthenticationException) {
        startAuthentication();
    } else {
        accessDenied(); 
    }
}
  1. From the brief description of Filter instances, you may recall that calling FilterChain.doFilter(request, response) is equivalent to calling the rest of the application. This means that if another part of the application (e.g. FilterSecurityInterceptor or security method) throws an AuthenticationException or AccessDeniedException, then those exceptions will be caught and handled at this stage.
  2. If the user is not authenticated or an AuthenticationException is thrown, then run the authentication procedure.
  3. Otherwise access will be denied