Spring Security in the web tier (for UIs and HTTP back ends) is based on Servlet Filters , so it is helpful to look at the role of Filters generally first. The picture below shows the typical layering of the handlers for a single HTTP request.

The client sends a request to the app, and the container decides which filters and which servlet apply to it based on the path of the request URI. At most one servlet can handle a single request, but filters form a chain, so they are ordered, and in fact a filter can veto the rest of the chain if it wants to handle the request itself. A filter can also modify the request and/or the response used in the downstream filters and servlet. The order of the filter chain is very important, and Spring Boot manages it through 2 mechanisms: one is that @Beans of type Filter can have an @Order or implement Ordered , and the other is that they can be part of a FilterRegistrationBean that itself has an order as part of its API. Some off-the-shelf filters define their own constants to help signal what order they like to be in relative to each other (e.g. the SessionRepositoryFilter from Spring Session has a DEFAULT_ORDER of Integer.MIN_VALUE + 50 , which tells us it likes to be early in the chain, but it doesn’t rule out other filters coming before it).

Spring Security is installed as a single Filter in the chain, and its concrete type is FilterChainProxy , for reasons that will become apparent soon. In a Spring Boot app the security filter is a @Bean in the ApplicationContext , and it is installed by default so that it is applied to every request. It is installed at a position defined by SecurityProperties.DEFAULT_FILTER_ORDER , which in turn is anchored by FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER (the maximum order that a Spring Boot app expects filters to have if they wrap the request, modifying its behaviour). There’s more to it than that though: from the point of view of the container Spring Security is a single filter, but inside it there are additional filters, each playing a special role. Here’s a picture:

Figure 2. Spring Security is a single physical Filter but delegates processing to a chain of internal filters

In fact there is even one more layer of indirection in the security filter: it is usually installed in the container as a DelegatingFilterProxy , which does not have to be a Spring @Bean . The proxy delegates to a FilterChainProxy which is always a @Bean , usually with a fixed name of springSecurityFilterChain . It is the FilterChainProxy which contains all the security logic arranged internally as a chain (or chains) of filters. All the filters have the same API (they all implement the Filter interface from the Servlet Spec) and they all have the opportunity to veto the rest of the chain.

There can be multiple filter chains all managed by Spring Security in the same top level FilterChainProxy and all unknown to the container. The Spring Security filter contains a list of filter chains, and dispatches a request to the first chain that matches it. The picture below shows the dispatch happening based on matching the request path ( /foo/** matches before /** ). This is very common but not the only way to match a request. The most important feature of this dispatch process is that only one chain ever handles a request.

Figure 3. The Spring Security FilterChainProxy dispatches requests to the first chain that matches.

A vanilla Spring Boot application with no custom security configuration has a several (call it n) filter chains, where usually n=6. The first (n-1) chains are there just to ignore static resource patterns, like /css/** and /images/** , and the error view /error (the paths can be controlled by the user with security.ignored from the SecurityProperties configuration bean). The last chain matches the catch all path /** and is more active, containing logic for authentication, authorization, exception handling, session handling, header writing, etc. There are a total of 11 filters in this chain by default, but normally it is not necessary for users to concern themselves with which filters are used and when.

Note The fact that all filters internal to Spring Security are unknown to the container is important, especially in a Spring Boot application, where all @Beans of type Filter are registered automatically with the container by default. So if you want to add a custom filter to the security chain, you need to either not make it a @Bean or wrap it in a FilterRegistrationBean that explicitly disables the container registration.

Creating and Customizing Filter Chains The default fallback filter chain in a Spring Boot app (the one with the /** request matcher) has a predefined order of SecurityProperties.BASIC_AUTH_ORDER . You can switch it off completely by setting security.basic.enabled=false , or you can use it as a fallback and just define other rules with a lower order. To do that just add a @Bean of type WebSecurityConfigurerAdapter (or WebSecurityConfigurer ) and decorate the class with @Order . Example: @Configuration @Order(SecurityProperties.BASIC_AUTH_ORDER - 10) public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.antMatcher("/foo/**") ...; } } This bean will cause Spring Security to add a new filter chain and order it before the fallback. Many applications have completely different access rules for one set of resources compared to another. For example an application that hosts a UI and a backing API might support cookie-based authentication with a redirect to a login page for the UI parts, and token-based authentication with a 401 response to unauthenticated requests for the API parts. Each set of resources has its own WebSecurityConfigurerAdapter with a unique order and a its own request matcher. If the matching rules overlap the earliest ordered filter chain will win.

Request Matching for Dispatch and Authorization A security filter chain (or equivalently a WebSecurityConfigurerAdapter ) has a request matcher that is used for deciding whether to apply it to an HTTP request. Once the decision is made to apply a particular filter chain, no others are applied. But within a filter chain you can have more fine grained control of authorization by setting additional matchers in the HttpSecurity configurer. Example: @Configuration @Order(SecurityProperties.BASIC_AUTH_ORDER - 10) public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.antMatcher("/foo/**") .authorizeRequests() .antMatchers("/foo/bar").hasRole("BAR") .antMatchers("/foo/spam").hasRole("SPAM") .anyRequest().isAuthenticated(); } } One of the easiest mistakes to make with configuring Spring Security is to forget that these matchers apply to different processes, one is a request matcher for the whole filter chain, and the other is only to choose the access rule to apply.