Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 49

Spring Security Filter Chain & SecurityContextHolder

Recommended resources

Hello! Welcome to the first lesson in our module on "Spring Security & REST API Design."

In our last lesson, we explored how Spring uses AOP proxies to intercept method calls and apply cross-cutting concerns like transaction management. This "interception" model is a powerful pattern you'll see throughout the Spring ecosystem.

Today, we'll see that same pattern applied at a different level. Your learning outcome is to explain Spring Security's filter chain architecture and the role of the SecurityContextHolder. We will move from intercepting Java method calls to intercepting incoming HTTP requests. A solid grasp of this architecture is non-negotiable for senior-level interviews, as it forms the bedrock of how security is enforced in virtually all Spring web applications.

1. The Foundation: Servlet Filters

Before diving into Spring Security itself, we must start with the technology it's built upon: Servlet Filters. A Servlet Filter is a standard Java EE component that intercepts incoming HTTP requests and outgoing responses. Filters are chained together, forming a FilterChain. Each filter in the chain can inspect or modify the request before passing it to the next filter, and eventually to the target servlet (in Spring MVC, this is the DispatcherServlet).

This provides a perfect mechanism for handling cross-cutting concerns at the web layer, like logging, compression, and, most importantly, security.

Architecture :: Spring Security

To understand this foundational concept, let's review the official Spring Security documentation, which provides a concise explanation of Servlet Filters.

Please read the section titled 'A Review of Filters'. Focus on understanding how a FilterChain processes a request and the role of the chain.doFilter(request, response) call.

The key takeaway is that filters form a pipeline. A request enters the pipeline and passes through each filter in order. A filter can decide to:

  1. Perform some action and then pass the request down the chain.
  2. Block the request entirely (for example, if a security check fails).
  3. Modify the request or response before passing it along.

2. The Bridge: DelegatingFilterProxy and FilterChainProxy

Now, a challenge arises. Servlet Filters are managed by the Servlet Container (like Tomcat), but the rich components needed for security (services, repositories, etc.) are managed by Spring's ApplicationContext. How do we connect these two worlds?

Spring's solution is a special filter called DelegatingFilterProxy.

  • DelegatingFilterProxy: This is a standard Servlet Filter registered with the container. Its only job is to find a Spring bean in the ApplicationContext that is also a Filter and delegate the request to it. It acts as a bridge.

The Spring bean it delegates to is typically the FilterChainProxy.

  • FilterChainProxy: This is the powerhouse of Spring Security. It's a single, special filter that contains the entire security logic. Internally, it manages a list of one or more SecurityFilterChain instances. It decides which security chain to apply based on the request URI.
Spring Security Filter Chain Architecture
This diagram illustrates how a request first hits the `DelegatingFilterProxy` in the standard servlet container, which then passes control to the `FilterChainProxy` (a Spring bean). The `FilterChainProxy` then manages the actual `SecurityFilterChain`, which is composed of many individual security filters.

🔒 Spring Security Architecture: The Bigger Picture Explained! 🚀

To see how these components connect, let's watch a video that walks through the architecture, starting from the filter chain concept we just discussed.

Watch the segment from 07:59 to 12:18. The presenter does an excellent job of explaining the need for the DelegatingFilterProxy to bridge the Servlet API and Spring context, and then introduces the FilterChainProxy as the container for the actual security filters.

In summary, the flow is:
Client Request -> Servlet Container -> DelegatingFilterProxy -> Spring ApplicationContext -> FilterChainProxy -> SecurityFilterChain


3. Inside the SecurityFilterChain

The FilterChainProxy determines which SecurityFilterChain should handle a request. A SecurityFilterChain is an ordered list of security-specific filters, each with a distinct responsibility. By default, Spring Boot auto-configures a standard chain that includes several important filters.

When you add spring-boot-starter-security to your project, you get a secure-by-default setup. Every request must be authenticated. This is enforced by a default chain of filters.

Here are some of the most important filters you'll find in a typical chain:

  • CsrfFilter: Protects against Cross-Site Request Forgery attacks.
  • UsernamePasswordAuthenticationFilter: The default filter for processing username/password from a login form.
  • BasicAuthenticationFilter: Handles HTTP Basic authentication.
  • ExceptionTranslationFilter: Catches security-related exceptions (like AccessDeniedException) thrown further down the chain and translates them into appropriate HTTP responses (like a 403 Forbidden or a redirect to a login page).
  • AuthorizationFilter: Responsible for determining if an authenticated user is authorized to access a specific resource.

🔒 Spring Security Architecture: The Bigger Picture Explained! 🚀

Seeing this in action is the best way to understand it. The same video includes a debugging session that steps through the filter chain.

Watch from 13:43 to 17:07. Pay close attention as the debugger inspects the FilterChainProxy and you see the list of actual filter beans. The presenter specifically highlights the UsernamePasswordAuthenticationFilter, which is active in the default form-login configuration.

Understanding this ordered chain is critical. For example, an authentication filter must run before an authorization filter. It makes no sense to check if a user is authorized before you even know who they are.


4. Storing the Authenticated User: SecurityContextHolder

Once a filter (like UsernamePasswordAuthenticationFilter) successfully authenticates a user, where does it store this information so that the rest of the application can access it?

This is the job of the SecurityContextHolder.

  1. Authentication Object: After successful authentication, an Authentication object is created. This object contains the user's identity (principal), their credentials (which are usually cleared immediately after authentication), and a list of their granted authorities (roles or permissions).

  2. SecurityContext: This is a simple container that holds one Authentication object.

  3. SecurityContextHolder: This is the central component for accessing the SecurityContext. By default, it uses a ThreadLocal strategy. This means the SecurityContext (and thus the Authentication object) is bound to the current execution thread. As a single request is processed by a single thread, this makes the security information available everywhere in your code during that request's lifecycle—from your services to your controllers—without needing to pass it as a method parameter.

At the end of the request, a filter (SecurityContextHolderFilter) ensures the ThreadLocal is cleared to prevent memory leaks and issues with thread reuse.

Spring Security Filter Chain and Authentication Flow Diagram
This diagram provides a comprehensive overview, tying everything together. You can see the request entering the Security Filter Chain, being processed by authentication filters, and upon success, the resulting `Authentication` object being stored in the `SecurityContextHolder` for application-wide access.

Servlet Authentication Architecture :: Spring Security

The official documentation provides the most precise definition of these components.

Read the sections titled 'SecurityContextHolder', 'SecurityContext', and 'Authentication'. Focus on the ThreadLocal storage mechanism of the SecurityContextHolder and the purpose of the three main parts of the Authentication object: principal, credentials, and authorities.

Test your understanding!

Given that the SecurityContextHolder uses a ThreadLocal by default, what potential problem could arise if you spawn a new, unmanaged thread from within a secured method (e.g., using new Thread(() -> { ... }).start();) and try to access security information from that new thread?

Show answer

The SecurityContext would not be propagated to the new thread. Since the SecurityContextHolder stores its context in a ThreadLocal, the information is only available on the original request-processing thread. The new thread would have an empty SecurityContext, and calls to SecurityContextHolder.getContext().getAuthentication() would likely return null. This means any security checks or attempts to retrieve the current user's identity within that new thread would fail.

This is why for asynchronous processing, it's crucial to use Spring-managed mechanisms like @Async, which can be configured to propagate the security context correctly.


Conclusion

In this lesson, we've unpacked the core architecture of Spring Security. You've learned how it uses a sophisticated, interception-based filter model to apply security concerns to web requests. This is a topic that frequently appears in interviews for senior roles, and being able to articulate it clearly demonstrates a deep understanding of the framework.

Key Takeaways:

  • Spring Security is fundamentally built on the standard Servlet Filter mechanism.
  • The DelegatingFilterProxy acts as a bridge, connecting the servlet container's lifecycle to the Spring ApplicationContext.
  • The FilterChainProxy is the main Spring Security bean that manages one or more SecurityFilterChains.
  • A SecurityFilterChain is an ordered list of filters, each performing a specific security task (authentication, authorization, CSRF protection, etc.).
  • After successful authentication, the user's details are stored in an Authentication object within the SecurityContext.
  • The SecurityContextHolder provides application-wide access to the SecurityContext by storing it in a ThreadLocal for the duration of the request.

Preview of the Next Lesson:
We've seen the architectural "slots" where authentication happens. But how do we tell Spring Security to check usernames and passwords against our own database? In the next lesson, we will implement authentication against a database by creating a custom UserDetailsService. This will be our first step in customizing the default behavior to fit a real-world application.