skip to content

Filter Chain Architecture

The architecture everything else sits on: how the container reaches Spring's filters, how a request picks a chain, the lambda DSL, filter ordering, the SecurityContextHolder, and exception translation. Being able to draw this chain is the single most useful Spring Security answer.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

30

What is DelegatingFilterProxy and why does Spring Security need it?

level: juniorimportance: must knowfreq 70%

answer

  1. container Filter that holds no logic
  2. looks up Spring bean by name, delegates doFilter
  3. delegate = springSecurityFilterChain (FilterChainProxy)
  4. lazy lookup -> defers to ApplicationContext readiness
  5. bridge container lifecycle <-> Spring beans

basics

~20 s

DelegatingFilterProxy is a servlet Filter registered with the container that forwards requests to a Spring-managed filter bean (the security filter chain). It lets Spring beans act as filters even though the container created the proxy.

solid answer

~40 s

DelegatingFilterProxy is a standard jakarta.servlet.Filter that the servlet container registers and manages, but it does no filtering itself. On each request it looks up a Spring bean by name from the WebApplicationContext and delegates doFilter to it. Spring Security registers one named springSecurityFilterChain (a FilterChainProxy). This bridges two worlds: the container owns the filter lifecycle and knows about Filters at startup, while Spring Security's real logic lives in beans created and wired by the ApplicationContext. Without the proxy, the container would need to instantiate the security filter directly, losing dependency injection, AOP, and Spring lifecycle. It also defers bean lookup until the first request (or context readiness), so the Spring context can finish initializing before the filter is used.

go deeper

for a junior

Know it's a servlet filter that forwards to a Spring bean, and the bean is the security filter chain.

for a middle

Explain the name wiring (springSecurityFilterChain / FilterChainProxy) and that Boot auto-registers it.

for a senior

Articulate the two ownership models (container vs ApplicationContext) and why bridging is necessary for DI/AOP/lifecycle.

for a principal

Discuss deferred initialization, root-vs-child context lookup, targetFilterLifecycle, and the full delegation chain including FilterChainProxy selection.

## The problem it solves A servlet container (Tomcat, Jetty, etc.) manages the lifecycle of `jakarta.servlet.Filter` instances: it instantiates them, calls `init()`, `doFilter()`, and `destroy()`. Filters are typically declared in `web.xml` or via `ServletContext` registration at startup. But Spring Security's real filtering logic lives in **Spring beans** — objects created, dependency-injected, and lifecycle-managed by the Spring `ApplicationContext`, not by the container. These two ownership models don't naturally meet: the container doesn't know about Spring beans, and Spring beans aren't containers filters. ## What DelegatingFilterProxy is `org.springframework.web.filter.DelegatingFilterProxy` is itself a real `Filter` that the container registers and manages. But it contains **no security logic**. Its only job is to look up a Spring bean (by name) from the Spring `WebApplicationContext` and delegate `doFilter(...)` calls to that bean. So the container manages a lightweight proxy, and the proxy forwards to a fully Spring-managed delegate. ## How the name wiring works - The proxy has a **target bean name**. By default it's the filter's registered name; you can override it via the `targetBeanName` init-param. - Spring Security registers the proxy under the name **`springSecurityFilterChain`**, and the delegate bean is also named **`springSecurityFilterChain`** — an instance of `org.springframework.security.web.FilterChainProxy`. - In classic setups, `AbstractSecurityWebApplicationInitializer` (or `web.xml`) registers the `DelegatingFilterProxy` named `springSecurityFilterChain`, mapped to `/*`. - In Spring Boot, `SecurityFilterAutoConfiguration` registers a `DelegatingFilterProxyRegistrationBean` that wires the proxy to the `springSecurityFilterChain` bean automatically — you never write this by hand. ## Deferred initialization — the key mechanism At container startup the target Spring bean may not exist yet (the `ApplicationContext` may still be initializing). `DelegatingFilterProxy` therefore **lazily looks up the delegate on the first request** rather than in `init()`. It finds the `WebApplicationContext` (by default via `WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext)`, i.e., the root context), fetches the bean, and caches it. This deferral is why the proxy exists at all: it lets the servlet filter chain be assembled at startup while the actual security beans come online later, driven by the Spring context lifecycle. ## Delegate lifecycle By default the proxy does **not** invoke `init()`/`destroy()` on the delegate — Spring manages the bean's lifecycle. You can opt into invoking the servlet `Filter.init/destroy` on the delegate via the `targetFilterLifecycle` init-param (default `false`). ## Which context does it search? By default the **root** `WebApplicationContext` (the one created by `ContextLoaderListener`). You can point it at a specific context with the `contextAttribute` init-param. In Boot there is a single context, so this rarely matters. ## The full chain of delegation `Container filter chain` -> `DelegatingFilterProxy` -> `FilterChainProxy` (the `springSecurityFilterChain` bean) -> matched `SecurityFilterChain` -> ordered list of security filters (e.g., `SecurityContextHolderFilter`, `CsrfFilter`, `UsernamePasswordAuthenticationFilter`, `AuthorizationFilter`). Note the two-level indirection: the proxy bridges container->Spring, and `FilterChainProxy` then selects among possibly multiple `SecurityFilterChain`s by request matcher. ## Gotchas - Confusing `DelegatingFilterProxy` (the bridge) with `FilterChainProxy` (the actual dispatcher). Only the latter contains security filters. - Bean-name mismatch: if the `targetBeanName` doesn't resolve to a bean, you get a `NoSuchBeanDefinitionException` on first request. - Registering the proxy with the wrong URL mapping (must cover all secured paths, typically `/*`). - In multi-context apps, expecting the delegate in the child (DispatcherServlet) context when it lives in the root context. ## When you touch it Almost never directly in Boot. You encounter it when: reading stack traces, doing pure servlet/`web.xml` setups, adding custom filters that must be Spring beans, or debugging why a filter isn't being invoked.

  • What is the actual bean the proxy delegates to in Spring Security?
    A FilterChainProxy registered as the bean springSecurityFilterChain, which then dispatches to one of the SecurityFilterChain instances based on request matching.
  • In a Spring Boot app, who registers the DelegatingFilterProxy?
    SecurityFilterAutoConfiguration registers a DelegatingFilterProxyRegistrationBean that wires the proxy to the springSecurityFilterChain bean; you don't write it yourself.

saying these in an interview costs you the question

  • Saying DelegatingFilterProxy contains the security filters
  • Thinking it does the authentication/authorization itself
  • Believing the servlet container instantiates the security beans directly

context

open as a page

What is the ExceptionTranslationFilter and what job does it do in the Spring Security filter chain?

level: juniorimportance: must knowfreq 55%

basics

~20 s

It's a filter that catches two security exceptions thrown further down the chain — AuthenticationException (not logged in) and AccessDeniedException (logged in but not allowed) — and turns them into an HTTP response instead of an error page.

open as a page

What is FilterChainProxy in Spring Security, and what is its job?

level: juniorimportance: must knowfreq 60%

basics

~10 s

FilterChainProxy is the single servlet filter Spring Security registers. For each request it picks the first matching SecurityFilterChain and runs that chain's security filters in order.

open as a page

What is the Spring Security filter chain, and why does the ORDER of filters in it matter?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Spring Security is a chain of servlet filters run in a fixed order. Each filter does one job (load context, check CSRF, authenticate, authorize). Order matters because later filters depend on what earlier ones set up — e.g. you must load the user before checking permissions.

open as a page

What is a SecurityFilterChain @Bean and how do you declare one in modern Spring Security?

level: juniorimportance: must knowfreq 80%

basics

~10 s

You write a method annotated with @Bean that takes an HttpSecurity object, configures the rules on it, and returns http.build(), which produces a SecurityFilterChain. Spring registers it to secure your HTTP requests.

open as a page

What is SecurityContextHolder, and how do you read the currently authenticated user from it?

level: juniorimportance: must knowfreq 80%

basics

~10 s

SecurityContextHolder is a static holder that stores the SecurityContext for the current request/thread. Inside it lives an Authentication object describing who is logged in. You read it via SecurityContextHolder.getContext().getAuthentication().

open as a page

Explain the difference between DelegatingFilterProxy and FilterChainProxy.

level: middleimportance: must knowfreq 60%

basics

~10 s

DelegatingFilterProxy is the container-registered bridge that forwards to a Spring bean. FilterChainProxy is that bean — the Spring-managed filter that holds the security filter chains and runs the ordered security filters.

open as a page

What is an AuthenticationEntryPoint, and how does it differ between a browser app and a REST API?

level: middleimportance: must knowfreq 60%

basics

~10 s

An AuthenticationEntryPoint decides how to ask an unauthenticated user to log in. A browser app usually redirects to a login page (302); a REST API usually returns 401 Unauthorized instead of redirecting.

open as a page

How does FilterChainProxy decide which SecurityFilterChain handles a given request?

level: middleimportance: must knowfreq 55%

basics

~10 s

It iterates its ordered list of SecurityFilterChains and calls each chain's matches(request), which uses a RequestMatcher. The first chain that matches is used; the rest are ignored.

open as a page

Walk through the roles and relative order of SecurityContextHolderFilter, CsrfFilter, UsernamePasswordAuthenticationFilter, ExceptionTranslationFilter, and AuthorizationFilter.

level: middleimportance: must knowfreq 65%

basics

~10 s

Order: SecurityContextHolderFilter loads the current user; CsrfFilter checks the CSRF token; UsernamePasswordAuthenticationFilter authenticates form logins; ExceptionTranslationFilter catches security errors from below it; AuthorizationFilter (last) decides if the user may access the resource.

open as a page

What is the Lambda DSL in Spring Security and why is it preferred over the old chained-and method style?

level: middleimportance: must knowfreq 70%

basics

~20 s

The Lambda DSL configures each area by passing a lambda (a Customizer) into methods like authorizeHttpRequests(auth -> ...). It groups related settings clearly and avoids the ambiguous .and() chaining of the old style, which is now deprecated.

open as a page

Compare MODE_THREADLOCAL and MODE_INHERITABLETHREADLOCAL. When would you switch strategy, and what's the risk?

level: middleimportance: must knowfreq 65%

basics

~20 s

MODE_THREADLOCAL (default) stores the SecurityContext per-thread, so child threads don't see it. MODE_INHERITABLETHREADLOCAL uses an InheritableThreadLocal so a thread you spawn inherits the parent's context. The risk: with pooled threads inheritance is unreliable and can leak stale identities.

open as a page

When an AccessDeniedException is thrown, how does ExceptionTranslationFilter decide between commencing the AuthenticationEntryPoint and delegating to the AccessDeniedHandler?

level: seniorimportance: must knowfreq 50%

basics

~20 s

It checks the current authentication. If the user is only anonymous or remembered (remember-me), it treats the denial as 'not really logged in' and calls the AuthenticationEntryPoint (prompt to log in). If the user is fully authenticated, it delegates to the AccessDeniedHandler (403).

open as a page

How do you propagate the SecurityContext to code running on another thread (@Async, an ExecutorService, or MVC async)?

level: seniorimportance: must knowfreq 55%

basics

~20 s

Wrap the task or executor with Spring's delegating helpers: DelegatingSecurityContextRunnable / Callable for a single task, or DelegatingSecurityContextExecutor / DelegatingSecurityContextAsyncTaskExecutor for @Async. They capture the caller's context at submit time and install it on the worker thread for that task.

open as a page

How does the DelegatingFilterProxy get registered in a Spring Boot application, and how would you do it without Boot?

level: middleimportance: should knowfreq 45%

basics

~10 s

In Boot, SecurityFilterAutoConfiguration registers a DelegatingFilterProxyRegistrationBean pointing at the springSecurityFilterChain bean. Without Boot you register it via web.xml or AbstractSecurityWebApplicationInitializer with the same bean name and a /* mapping.

open as a page

What RequestMatcher does a SecurityFilterChain use if you never call securityMatcher, and why does that matter?

level: middleimportance: should knowfreq 30%

basics

~10 s

It uses AnyRequestMatcher, so the chain matches every request. That makes it a catch-all, which must be ordered last or it will shadow more specific chains.

open as a page

How does authorizeHttpRequests work, and why does the order of its matcher rules matter?

level: middleimportance: should knowfreq 65%

basics

~10 s

authorizeHttpRequests defines authorization rules by matching request paths to access rules like permitAll() or authenticated(). Rules are evaluated top-to-bottom and the first match wins, so specific rules must come before broad ones like anyRequest().

open as a page

Explain the deferred initialization of DelegatingFilterProxy and which ApplicationContext it resolves its delegate from. What can go wrong?

level: seniorimportance: should knowfreq 35%

basics

~10 s

The proxy is created by the container at startup but doesn't fetch its delegate bean until the first request, giving the Spring context time to initialize. By default it looks in the root WebApplicationContext.

open as a page

Where does ExceptionTranslationFilter sit relative to AuthorizationFilter, and why does its position matter?

level: seniorimportance: should knowfreq 35%

basics

~20 s

ExceptionTranslationFilter is placed just before AuthorizationFilter. Because it only wraps the filters after it in a try/catch, the authorization filter's AccessDeniedException is thrown downstream and caught by it. If it came after, it couldn't catch that exception.

open as a page

Once a SecurityFilterChain is selected, how are its filters actually invoked? Explain VirtualFilterChain.

level: seniorimportance: should knowfreq 35%

basics

~10 s

FilterChainProxy wraps the selected chain's filters in a VirtualFilterChain. Each filter calls chain.doFilter(...) to advance; after the last security filter, it delegates back to the container's original FilterChain to reach the controller.

open as a page

How do addFilterBefore, addFilterAfter, and addFilterAt work, and when would you use each to insert a custom filter?

level: seniorimportance: should knowfreq 55%

basics

~20 s

They insert your custom filter relative to a known Spring Security filter. addFilterBefore(f, X) runs f before filter X; addFilterAfter(f, X) after X; addFilterAt(f, X) at X's position (order among same-position filters is undefined). Use them because you can't hand-number Spring's built-in filters.

open as a page

Why does CsrfFilter run before the authentication filters, and what breaks if you place a state-changing custom filter before it?

level: seniorimportance: should knowfreq 40%

basics

~20 s

CsrfFilter runs early so a forged, state-changing request is rejected before any authentication or business logic acts on it. If a custom filter that mutates state runs before CsrfFilter, it executes on requests that CSRF validation would have blocked — reintroducing the CSRF vulnerability.

open as a page

How do you define multiple SecurityFilterChain beans, and how does Spring decide which one handles a request?

level: seniorimportance: should knowfreq 55%

basics

~20 s

Declare several SecurityFilterChain beans, each scoped with http.securityMatcher(...) to a URL subset, and order them with @Order. The FilterChainProxy tries them in order and the first chain whose matcher matches handles the request — only one chain runs per request.

open as a page

What is SecurityContextHolderStrategy, and why does modern Spring Security recommend reading it via getContextHolderStrategy() instead of the static SecurityContextHolder methods?

level: seniorimportance: should knowfreq 35%

basics

~10 s

SecurityContextHolderStrategy is the interface behind SecurityContextHolder that actually stores the context (ThreadLocal, inheritable, or global). Modern code injects/obtains it via SecurityContextHolder.getContextHolderStrategy() so the strategy can be swapped and mocked, instead of hardcoding static calls.

open as a page

How does ExceptionTranslationFilter enable 'redirect back to the originally requested page after login', and how would you tune this in a mixed API/browser system?

level: principalimportance: should knowfreq 25%

basics

~20 s

Before commencing the entry point, ExceptionTranslationFilter saves the current request in a RequestCache. After the user logs in, a SavedRequestAware success handler pulls that saved request out and redirects them back to where they were originally going.

open as a page

You need a stateless JWT API and a session-based server-rendered UI in the same app. How do you structure the SecurityFilterChains, and what ordering pitfalls apply?

level: principalimportance: should knowfreq 30%

basics

~10 s

Define two SecurityFilterChain beans: one with securityMatcher("/api/**"), stateless + JWT + CSRF off, ordered first; one catch-all with sessions + form login + CSRF, ordered last. Order matters because the first matching chain wins.

open as a page

When you define multiple SecurityFilterChain beans, how does FilterChainProxy decide which chain handles a request, and how does that interact with filter ordering?

level: principalimportance: should knowfreq 35%

basics

~20 s

FilterChainProxy tries each SecurityFilterChain in bean order and uses the FIRST one whose request matcher matches — only that chain's filters run. So a broad or unordered chain declared first can shadow a more specific one. Order the beans with @Order and give each a securityMatcher.

open as a page

Your service leaks one user's identity into another user's request under load. Walk through how the SecurityContextHolder storage model could cause this and how you'd design propagation to prevent it (including virtual threads).

level: principalimportance: should knowfreq 25%

basics

~20 s

Identity leaks come from stale ThreadLocal state on reused threads: forgetting to clear the context, or using MODE_INHERITABLETHREADLOCAL with a pool so a worker keeps a context from an earlier request. Fix it with per-task propagation (DelegatingSecurityContext*) and ensuring the context is always cleared after each unit of work.

open as a page

You need a stateful, dependency-injected custom servlet filter that runs across the whole app. How does DelegatingFilterProxy help, and what design trade-offs apply?

level: principalimportance: nice to knowfreq 20%

basics

~20 s

Register a DelegatingFilterProxy pointing at your Spring-managed filter bean. The container manages the proxy while Spring injects and lifecycle-manages your real filter, so it gets dependencies and AOP even though the container owns filter registration.

open as a page

When should you use authorizeHttpRequests().permitAll() versus WebSecurityCustomizer web.ignoring(), and how are HttpSecurity, WebSecurity, and FilterChainProxy related?

level: principalimportance: nice to knowfreq 35%

basics

~20 s

permitAll() lets a request through but still runs the security filter chain (CSRF, headers, etc.). web.ignoring() (WebSecurityCustomizer) tells Spring to skip the entire chain for those paths. Prefer permitAll(); reserve ignoring() for truly static, security-irrelevant resources.

open as a page