Spring Security filter chain explained with custom filters — Complete Guide
A practical, in-depth guide to Spring Security filter chain explained with custom filters with examples.
INTRO
When you spin up a Spring Boot application and slap @EnableWebSecurity on a config class, you instantly get a wall of default filters protecting every endpoint. It feels great until you need to insert your own authentication step, log request metadata, or short‑circuit a request based on a custom header. The default filter order is opaque, and trying to “just add a bean” often results in ClassCastException or a silent no‑op because the filter never runs where you expect it to.
That friction isn’t just an annoyance—it leads to security bugs that hide in production. A mis‑ordered filter can let an unauthenticated request slip past your JWT validator, or it can cause a legitimate user to be logged out on every request. The real problem is a lack of visibility and control over the Spring Security filter chain. Understanding how the chain is built, how Spring decides the order, and how to inject custom filters at the right spot is essential for any engineer who wants reliable, maintainable security.
In the full guide we peel back the abstraction layer, show you the exact sequence of built‑in filters, and walk through three real‑world scenarios where a custom filter is the right tool. By the end you’ll be able to add, replace, or remove filters without breaking the chain, and you’ll have a checklist to avoid the most common pitfalls.
WHAT YOU'LL LEARN
- The anatomy of the default filter chain – a step‑by‑step breakdown of each built‑in filter, why it exists, and where it sits in the order.
-
How Spring assembles the chain – the role of
SecurityFilterChain,HttpSecurity, and theFilterRegistrationBeanin the bootstrapping process. -
Creating a custom filter – extending
OncePerRequestFilter, handlingdoFilterInternal, and accessing theSecurityContext. -
Strategic placement – using
addFilterBefore,addFilterAfter, andaddFilterAtto position your filter relative toUsernamePasswordAuthenticationFilter,BearerTokenAuthenticationFilter, etc. -
Replacing a built‑in filter – when and how to swap out
BasicAuthenticationFilterwith a bespoke implementation without losing downstream processing. - Production‑ready tips – idempotent registration, avoiding duplicate execution, and profiling the chain with Spring Boot Actuator.
A SHORT CODE SNIPPET
@Configuration
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.addFilterBefore(new RequestIdFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.oauth2ResourceServer(oauth -> oauth.jwt());
return http.build();
}
}
The snippet injects a RequestIdFilter before the username/password authentication step, guaranteeing that every request carries a traceable ID for downstream logging.
KEY TAKEAWAYS
- The filter chain is a deterministic list, not a magic bean forest; knowing the exact order lets you reason about security flow.
- Use the
addFilter*family of methods onHttpSecurityto control placement; never rely on@Componentscanning alone for custom filters. - Replacing a default filter requires explicitly disabling the original (
http.removeFilter(...)) to prevent duplicate processing. - Profiling the final chain with
http.getFilters()or Actuator’shttptraceendpoint reveals hidden ordering issues before they hit production.
👉 Read the complete guide with step-by-step examples, common mistakes, and production tips:
Spring Security filter chain explained with custom filters — Complete Guide
Top comments (0)