Architecture and Filter Chain Questions
Explain how a request flows through Spring Security.
HTTP request
-> Servlet container filters
-> DelegatingFilterProxy (a plain servlet Filter registered by Boot)
-> FilterChainProxy (bean "springSecurityFilterChain")
-> picks the first matching SecurityFilterChain
SecurityContextHolderFilter -> CorsFilter -> CsrfFilter -> LogoutFilter
-> UsernamePasswordAuthenticationFilter / BearerTokenAuthenticationFilter
-> ... -> ExceptionTranslationFilter -> AuthorizationFilter
-> DispatcherServlet -> your @RestControllerSecurity runs before the controller, as servlet filters, so an unauthenticated request never reaches your code.
What is DelegatingFilterProxy and why is it needed?
The servlet container creates filters itself and knows nothing about Spring beans. DelegatingFilterProxy is a normal servlet filter that looks up a Spring bean (springSecurityFilterChain, a FilterChainProxy) and delegates to it. That way the security filters are Spring beans that can use dependency injection.
How do you configure security in Spring Security 6?
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable()) // stateless JWT API
.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers(HttpMethod.GET, "/api/products/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated())
.httpBasic(Customizer.withDefaults());
return http.build();
}
}Tip: Rules are checked top to bottom and the first match wins, so put specific paths before general ones and anyRequest() last.
What are AuthenticationManager, ProviderManager and AuthenticationProvider?
AuthenticationManager: the interface with one method,authenticate(Authentication).ProviderManager: the usual implementation; it asks a list ofAuthenticationProviders in turn until one supports the token type.AuthenticationProvider: does the real check.DaoAuthenticationProviderloads the user withUserDetailsServiceand compares passwords withPasswordEncoder;JwtAuthenticationProvidervalidates a JWT.- On success it returns an authenticated
Authentication; on failure it throws anAuthenticationException.
What is the SecurityContextHolder and how does it store the user?
SecurityContextHolder holds the SecurityContext (which holds the Authentication) for the current request. The default strategy is MODE_THREADLOCAL, so each request thread sees its own user. That is also why the user is "lost" in @Async methods or new threads: wrap the executor with DelegatingSecurityContextAsyncTaskExecutor or use MODE_INHERITABLETHREADLOCAL. Between requests the context is stored by a SecurityContextRepository (HTTP session by default; nothing for stateless APIs).
Can you have more than one SecurityFilterChain? When would you?
@Bean @Order(1)
SecurityFilterChain apiChain(HttpSecurity http) throws Exception {
http.securityMatcher("/api/**")
.authorizeHttpRequests(a -> a.anyRequest().authenticated())
.oauth2ResourceServer(o -> o.jwt(Customizer.withDefaults()))
.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
@Bean @Order(2)
SecurityFilterChain webChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(a -> a.anyRequest().authenticated())
.formLogin(Customizer.withDefaults());
return http.build();
}Yes. FilterChainProxy uses the first chain whose securityMatcher matches. A common case: stateless JWT for /api/** and session-based form login for an admin web UI.