DelegatingFilterProxy and FilterChainProxy

Bridging Tomcat and Spring

Tomcat creates servlet filters itself and knows nothing about Spring beans. DelegatingFilterProxy is the bridge: Tomcat calls it, and it looks up the bean named springSecurityFilterChain (a FilterChainProxy) in the Spring context and delegates to it. Spring Boot registers all of this automatically.

Tomcat --> DelegatingFilterProxy --(bean lookup)--> FilterChainProxy
                                                     |
                                   +-----------------+-----------------+
                                   |                                   |
                         chain 1: /api/**                    chain 2: any request
                         (stateless, HTTP Basic)             (form login, sessions)
                     first chain whose securityMatcher matches the request wins

Multiple Chains

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    @Order(1)                                  // checked first
    SecurityFilterChain apiChain(HttpSecurity http) throws Exception {
        http
            .securityMatcher("/api/**")        // this chain only handles /api/**
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            .httpBasic(Customizer.withDefaults())
            .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .csrf(csrf -> csrf.disable());
        return http.build();
    }

    @Bean
    @Order(2)                                  // everything else: the web pages
    SecurityFilterChain webChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            .formLogin(Customizer.withDefaults());
        return http.build();
    }
}
  • The chain without securityMatcher matches everything, so it must have the highest @Order number (checked last).
  • FilterChainProxy uses only one chain per request: the first match.
  • Why it is useful: APIs want stateless 401 responses, browsers want login pages and redirects.

FilterChainProxy Extras

Because every security request passes through one bean, FilterChainProxy also does housekeeping: it clears the SecurityContextHolder after each request (so a user never leaks into the next request on the same thread) and applies the HttpFirewall, which rejects malicious URLs such as /api/../admin or paths with encoded slashes.

The Firewall at Work

curl -i "localhost:8080/api/products/..;/admin"

Example output

HTTP/1.1 400
The request was rejected because the URL contained a potentially malicious String ";"