Skip to main content

Command Palette

Search for a command to run...

Spring security architecture

Updated
5 min read

Spring security architecture

بِسْمِ ٱللّٰهِ ٱلرَّحْمٰنِ ٱلرَّحِيمِ

I begin my journey of learning spring security 3 months ago. I copy-pasted from a tutorial, it worked, and I felt like a fraud. The code was running. I couldn't explain a single line of it. That feeling is what this series is about. This series tears open the black box. We start from the very beginning — filters — and build a mental model that actually sticks.

1) Filters: The First Line of Defense

Before any request reaches your controller, it travels through a Servlet FilterChain. Think of it as a pipeline of checkpoints. Each filter can inspect, modify, or block the request — and when the response comes back, it passes through the same filters in reverse. Order is the whole game in security.

Request/response flow (high level)

  1. Tomcat receives the HTTP request.

  2. The container runs the Servlet FilterChain.

  3. DelegatingFilterProxy forwards into Spring-managed security.

  4. FilterChainProxy selects a matching SecurityFilterChain.

  5. Spring Security filters run (authentication, authorization, etc).

  6. Request reaches DispatcherServlet and controllers.

  7. Response returns through filters in reverse order.

2) Spring Security inside the filter chain

Spring Security isn't special framework magic — it's just more filters, cleverly wired in. Two components make this work.

DelegatingFilterProxy

DelegatingFilterProxy is a Servlet Filter. It lives in the container’s filter chain. Its only job is to delegate to a Spring-managed bean named springSecurityFilterChain. It's the bridge between the raw Servlet world and the Spring context.

FilterChainProxy

The real entry point for Spring Security. It receives the delegated request and routes it to one or more SecurityFilterChain instances based on the request URL.

Spring’s reference doc covers this well:

SecurityFilterChain

Each SecurityFilterChain has two things:

  • A request matcher — which URLs it applies to.

  • An ordered list of filters to execute for matching requests.

You can define multiple chains. For example, one for /api/** with stateless JWT auth, and another for /admin/** with session-based login.

Mental model

Think of it like an “airport security”:

  • DelegatingFilterProxy: the gate that forwards you to airport security.

  • FilterChainProxy: security control that picks a lane.

  • SecurityFilterChain: one lane, with its own checks and order.

Most Spring Security filters are Spring-managed beans. They are assembled and ordered inside one or more SecurityFilterChain instances.

3) What's Inside a Security Filter Chain?

As we have seen every security filter chain is composed of many security filters. Those filters can be used for a number of different purposes authentication, authorization, exploit protection.... These filters are executed in a specific order, because in security order matters. For example the filter that performs the authentication should be placed before the filter that does authorization. Here as you can see we have 4 filters in our filterchain, and they will be executed in this order:

  1. First, the CsrfFilter is invoked to protect against CSRF attacks.

  2. Second, the authentication filters are invoked to authenticate the request.

  3. Third, the AuthorizationFilter is invoked to authorize the request.

For a particular request the list of filters that are invoked appears in the logs as follows. Here we can see all filters that the DefaultSecurityFilterChain will have:

2023-06-14T08:55:22.321-03:00 DEBUG 76975 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Will secure any request with [ DisableEncodeUrlFilter, WebAsyncManagerIntegrationFilter, SecurityContextHolderFilter, HeaderWriterFilter, CsrfFilter, LogoutFilter, UsernamePasswordAuthenticationFilter, DefaultLoginPageGeneratingFilter, DefaultLogoutPageGeneratingFilter, BasicAuthenticationFilter, RequestCacheAwareFilter, SecurityContextHolderAwareRequestFilter, AnonymousAuthenticationFilter, ExceptionTranslationFilter, AuthorizationFilter]

4) Adding Your Own Filters

The default filter chain covers most use cases, but sometimes you need custom behavior — API key validation, request tracing, rate limiting, and so on. Spring Security gives you three methods to insert a custom filter at precisely the right position:

  • #addFilterBefore(Filter, Class<?>) adds your filter before another filter

  • #addFilterAfter(Filter, Class<?>) adds your filter after another filter

  • #addFilterAt(Filter, Class<?>) replaces another filter with your filter

The key question is always: where does my filter belong? To answer that, consult Spring Security's official documentation — it lists the complete ordered set of built-in filters, which is your reference map for placing custom logic.

For example, a JWT filter should run before UsernamePasswordAuthenticationFilter, because it performs its own form of authentication:

@Bean SecurityFilterChain filterChain(HttpSecurity http) throws Exception { 
    http // ... 
    .addFilterAfter(new JwtFilter(), UsernamePasswordAuthenticationFilter.class); 
    return http.build(); 
}

Because we should check if the user has a jwt token before checking the username and password auth. If you ask why i did not give the code implementation of the JwtFilter. Don't worry it will be in the next article.

If you want to have detailed logs to debug your spring security configuration you can add this line to your application properties:

logging.level.org.springframework.security=TRACE

What's Next

Now you understand how requests flow through filters and how Spring Security assembles its filter chain. Next, we go deeper:

  • How does SecurityContext get populated and cleared between requests?

  • What does an AuthenticationFilter actually do internally?

  • How does AuthorizationFilter make its allow/deny decision?

Next up: The Authentication Architecture — AuthenticationManager, AuthenticationProvider, and UserDetailsService.

Sources:

Spring security documentation: https://docs.spring.io/spring-security/reference/servlet/architecture.html#

// this is a code snippet from the spring security documentation 
@Configuration 
@EnableWebSecurity 
public class SecurityConfig { 
    @Bean 
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { 
        http .csrf(Customizer.withDefaults()) 
             .httpBasic(Customizer.withDefaults()) 
             .formLogin(Customizer.withDefaults()) 
             .authorizeHttpRequests((authorize) -> authorize .anyRequest().authenticated() ); 
        return http.build(); 
    } 
}