The filter chain infrastructure is the foundation of Spring Security Web, managing the security filter chain that processes HTTP requests.
Required Dependencies:
spring-security-web (this package)spring-security-core is required (provides SecurityContextHolderStrategy, AuthenticationException)spring-security-config is required for HttpSecurity DSL and SecurityFilterChain bean creationFilter interfaceGenericFilterBean and OncePerRequestFilterDefault Behaviors:
StrictHttpFirewall (rejects suspicious requests)DefaultRequestRejectedHandler (rethrows exception)SecurityFilterChain is used (order matters)ObservationFilterChainDecorator for metrics)DefaultRedirectStrategy with HTTP 302 statusPortMapperImpl with default mappings (80:443, 8080:8443)LoginUrlAuthenticationEntryPoint (redirects to /login)Threading Model:
FilterChainProxy.doFilter() executes on servlet container thread (synchronous)SecurityFilterChain.matches() called for each chain until match foundLifecycle:
FilterChainProxy implements Filter (registered in servlet container via FilterRegistrationBean)SecurityFilterChain beans are discovered and collected at application startupFilterChainProxy.afterPropertiesSet() validates filter chain configurationExceptions:
RequestRejectedException - Firewall rejects request (wrapped by request rejected handler)UnreachableFilterChainException - Filter chain configuration error (chain cannot be reached)ServletException, IOException - Standard servlet exceptions from filter processingIllegalArgumentException - Invalid filter chain configurationEdge Cases:
SecurityFilterChain beans: Order matters, first matching chain is usedforceHttps=true in LoginUrlAuthenticationEntryPointsetContextRelative(true)RequestRejectedException or application will fail/api/** before /**)FilterChainProxy is the central entry point that delegates Filter requests to Spring-managed security filter chains.
package org.springframework.security.web;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import java.io.IOException;
import java.util.List;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.RequestRejectedHandler;
import org.springframework.web.filter.GenericFilterBean;
public class FilterChainProxy extends GenericFilterBean {
/**
* Creates an empty FilterChainProxy with no filter chains.
*/
public FilterChainProxy();
/**
* Creates a FilterChainProxy with a single security filter chain.
*
* @param chain the SecurityFilterChain instance
*/
public FilterChainProxy(SecurityFilterChain chain);
/**
* Creates a FilterChainProxy with the given security filter chains.
*
* @param filterChains the list of SecurityFilterChain instances
*/
public FilterChainProxy(List<SecurityFilterChain> filterChains);
/**
* Delegates the request through the matching security filter chain.
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException;
/**
* Sets the HttpFirewall for request validation.
* Default is StrictHttpFirewall.
*/
public void setFirewall(HttpFirewall firewall);
/**
* Sets the handler for rejected requests.
*/
public void setRequestRejectedHandler(RequestRejectedHandler requestRejectedHandler);
/**
* Sets the SecurityContextHolderStrategy.
*/
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy);
/**
* Sets a validator for the filter chain configuration.
*/
public void setFilterChainValidator(FilterChainValidator filterChainValidator);
/**
* Sets a decorator for customizing the filter chain execution.
*/
public void setFilterChainDecorator(FilterChainDecorator filterChainDecorator);
/**
* Returns the list of SecurityFilterChains which will be matched against
* and applied to incoming requests.
*
* @return unmodifiable list of SecurityFilterChain instances
*/
public List<SecurityFilterChain> getFilterChains();
/**
* Convenience method for getting filters for a specific URL, mainly for testing.
*
* @param url the URL to match
* @return list of Filter instances or null if no chain matches
*/
public List<Filter> getFilters(String url);
public void afterPropertiesSet();
public String toString();
}SecurityFilterChain defines a filter chain capable of matching requests and providing filters.
package org.springframework.security.web;
import jakarta.servlet.Filter;
import jakarta.servlet.http.HttpServletRequest;
import java.util.List;
public interface SecurityFilterChain {
/**
* Determines if this filter chain applies to the given request.
*
* @param request the HttpServletRequest to match
* @return true if this filter chain should process the request
*/
boolean matches(HttpServletRequest request);
/**
* Returns the list of filters to be applied.
*
* @return the ordered list of Filter instances
*/
List<Filter> getFilters();
}Standard implementation of SecurityFilterChain (Since 3.1).
package org.springframework.security.web;
import jakarta.servlet.Filter;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.web.util.matcher.RequestMatcher;
import java.util.List;
/**
* Standard implementation of SecurityFilterChain.
*/
public final class DefaultSecurityFilterChain implements SecurityFilterChain {
/**
* Creates a DefaultSecurityFilterChain with a RequestMatcher and filters.
*
* @param requestMatcher the matcher to determine if this chain applies
* @param filters the filters to be applied (varargs)
*/
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters);
/**
* Creates a DefaultSecurityFilterChain with a RequestMatcher and a list of filters.
*
* @param requestMatcher the matcher to determine if this chain applies
* @param filters the list of filters to be applied
*/
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters);
/**
* Returns the RequestMatcher used by this filter chain.
*
* @return the RequestMatcher instance
*/
public RequestMatcher getRequestMatcher();
/**
* Returns the list of filters in this chain.
*
* @return the ordered list of Filter instances
*/
public List<Filter> getFilters();
/**
* Determines if this filter chain applies to the given request.
*
* @param request the HttpServletRequest to match
* @return true if this filter chain should process the request
*/
public boolean matches(HttpServletRequest request);
}FilterInvocation holds HTTP filter-related objects and provides request information.
package org.springframework.security.web;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
public class FilterInvocation {
/**
* Creates a FilterInvocation from actual servlet objects.
*
* @param request the ServletRequest
* @param response the ServletResponse
* @param chain the FilterChain
*/
public FilterInvocation(ServletRequest request, ServletResponse response, FilterChain chain);
public FilterChain getChain();
public String getFullRequestUrl();
public HttpServletRequest getHttpRequest();
public HttpServletResponse getHttpResponse();
public String getRequestUrl();
public HttpServletRequest getRequest();
public HttpServletResponse getResponse();
public String getCharacterEncoding();
public String getContextPath();
public String toString();
}Utility filter that redirects requests matching a RequestMatcher to a specified URL (Since 5.6).
package org.springframework.security.web;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
/**
* Filter that redirects requests that match RequestMatcher to the specified URL.
*/
public final class RequestMatcherRedirectFilter extends OncePerRequestFilter {
/**
* Creates a RequestMatcherRedirectFilter.
*
* @param requestMatcher the request matcher to determine which requests to redirect
* @param redirectUrl the URL to redirect to
*/
public RequestMatcherRedirectFilter(RequestMatcher requestMatcher, String redirectUrl);
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException;
}AuthenticationEntryPoint commences an authentication scheme when authentication is required.
package org.springframework.security.web;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.security.core.AuthenticationException;
public interface AuthenticationEntryPoint {
/**
* Commences an authentication scheme.
* Called by ExceptionTranslationFilter when authentication is required.
*
* @param request the request during which authentication failed
* @param response the response
* @param authException the exception that caused authentication to fail
*/
void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException;
}Redirects to a login page when authentication is required.
package org.springframework.security.web.authentication;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.PortMapper;
import org.springframework.security.web.RedirectStrategy;
public class LoginUrlAuthenticationEntryPoint implements AuthenticationEntryPoint {
/**
* Creates an entry point with the specified login form URL.
*
* @param loginFormUrl the URL of the login page
*/
public LoginUrlAuthenticationEntryPoint(String loginFormUrl);
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException;
/**
* Forces HTTPS for the login page.
*/
public void setForceHttps(boolean forceHttps);
/**
* Sets the PortMapper for HTTP to HTTPS redirection.
*/
public void setPortMapper(PortMapper portMapper);
/**
* Uses forward instead of redirect to login page.
*/
public void setUseForward(boolean useForward);
/**
* Enables relative URIs in redirects.
*/
public void setFavorRelativeUris(boolean favorRelativeUris);
public String getLoginFormUrl();
public int getServerPort(ServletRequest request);
public void afterPropertiesSet();
}Returns HTTP 403 Forbidden for pre-authenticated scenarios.
package org.springframework.security.web.authentication;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
public class Http403ForbiddenEntryPoint implements AuthenticationEntryPoint {
public Http403ForbiddenEntryPoint();
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException arg2) throws IOException, ServletException;
}Selects an AuthenticationEntryPoint based on RequestMatcher evaluation.
package org.springframework.security.web.authentication;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.LinkedHashMap;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.RequestMatcher;
public class DelegatingAuthenticationEntryPoint implements AuthenticationEntryPoint {
/**
* Creates a delegating entry point with RequestMatcher to EntryPoint mappings.
*
* @param entryPoints map of RequestMatcher to AuthenticationEntryPoint
*/
public DelegatingAuthenticationEntryPoint(
LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints);
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException;
/**
* Sets the default entry point when no matcher matches.
*/
public void setDefaultEntryPoint(AuthenticationEntryPoint defaultEntryPoint);
public void afterPropertiesSet();
/**
* Creates a builder for configuring entry points.
*/
public static Builder builder();
}package org.springframework.security.web.authentication;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.RequestMatcher;
public static final class DelegatingAuthenticationEntryPoint.Builder {
/**
* Sets the default entry point.
*/
public Builder defaultEntryPoint(AuthenticationEntryPoint defaultEntryPoint);
/**
* Adds an entry point for the given request matcher.
*/
public Builder addEntryPointFor(AuthenticationEntryPoint entryPoint, RequestMatcher requestMatcher);
/**
* Builds the DelegatingAuthenticationEntryPoint.
*/
public AuthenticationEntryPoint build();
}An AuthenticationEntryPoint that does nothing.
package org.springframework.security.web.authentication;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
public class NoOpAuthenticationEntryPoint implements AuthenticationEntryPoint {
public NoOpAuthenticationEntryPoint();
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException;
}Interface for decorating filter chain execution.
package org.springframework.security.web;
import jakarta.servlet.FilterChain;
public interface FilterChainDecorator {
/**
* Decorates the filter chain with additional behavior.
*
* @param filterChain the original filter chain
* @return the decorated filter chain
*/
FilterChain decorate(FilterChain filterChain);
}Decorates filter chain with observability support using Micrometer.
package org.springframework.security.web;
import io.micrometer.observation.ObservationRegistry;
import jakarta.servlet.FilterChain;
public class ObservationFilterChainDecorator implements FilterChainDecorator {
/**
* Creates a decorator with the given observation registry.
*
* @param observationRegistry the Micrometer observation registry
*/
public ObservationFilterChainDecorator(ObservationRegistry observationRegistry);
public FilterChain decorate(FilterChain filterChain);
}Constants for storing security-related attributes in request/session scope.
package org.springframework.security.web;
public final class WebAttributes {
/**
* Attribute for storing authentication exception.
*/
public static final String AUTHENTICATION_EXCEPTION =
"SPRING_SECURITY_LAST_EXCEPTION";
/**
* Attribute for storing access denied exception.
*/
public static final String ACCESS_DENIED_403 =
"SPRING_SECURITY_403_EXCEPTION";
/**
* Attribute for storing web invocation privilege evaluator.
*/
public static final String WEB_INVOCATION_PRIVILEGE_EVALUATOR_ATTRIBUTE =
"org.springframework.security.web.access.WebInvocationPrivilegeEvaluator.CONTEXT_ATTRIBUTE";
}Interface for redirection logic.
package org.springframework.security.web;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
public interface RedirectStrategy {
/**
* Performs a redirect to the supplied URL.
*
* @param request the current request
* @param response the response to redirect
* @param url the target URL
*/
void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url)
throws IOException;
}Default implementation of RedirectStrategy.
package org.springframework.security.web;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.http.HttpStatus;
public class DefaultRedirectStrategy implements RedirectStrategy {
public DefaultRedirectStrategy();
public void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url)
throws IOException;
/**
* Makes the redirect URL relative to the context path.
*/
public void setContextRelative(boolean useRelativeContext);
/**
* Sets the HTTP status code for redirects (default: 302).
*/
public void setStatusCode(HttpStatus statusCode);
}Maps HTTP ports to HTTPS ports for secure redirects.
package org.springframework.security.web;
import jakarta.servlet.ServletRequest;
public interface PortMapper {
/**
* Locates the HTTP port for the given HTTPS port.
*
* @param httpsPort the HTTPS port
* @return the corresponding HTTP port, or null if unknown
*/
Integer lookupHttpPort(Integer httpsPort);
/**
* Locates the HTTPS port for the given HTTP port.
*
* @param httpPort the HTTP port
* @return the corresponding HTTPS port, or null if unknown
*/
Integer lookupHttpsPort(Integer httpPort);
}Default PortMapper implementation with configurable mappings.
package org.springframework.security.web;
import java.util.Map;
public class PortMapperImpl implements PortMapper {
public PortMapperImpl();
public Integer lookupHttpPort(Integer httpsPort);
public Integer lookupHttpsPort(Integer httpPort);
/**
* Sets custom port mappings. Keys are String HTTP ports, values are String HTTPS ports.
* Default mappings are 80:443 and 8080:8443.
*
* @param newMappings map of HTTP to HTTPS port mappings
*/
public void setPortMappings(Map<String, String> newMappings);
}Thrown when a SecurityFilterChain cannot be reached due to configuration issues.
package org.springframework.security.web;
public class UnreachableFilterChainException extends IllegalArgumentException {
/**
* Creates an exception with the specified message.
*
* @param message the detail message
*/
public UnreachableFilterChainException(String message);
/**
* Creates an exception with filter chain details.
*
* @param message the detail message
* @param filterChain the filter chain that caused the issue
* @param unreachableFilterChain the unreachable filter chain
*/
public UnreachableFilterChainException(String message, SecurityFilterChain filterChain,
SecurityFilterChain unreachableFilterChain);
public SecurityFilterChain getFilterChain();
public SecurityFilterChain getUnreachableFilterChain();
}import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
http
.securityMatchers(matchers -> matchers
.requestMatchers("/api/**")
)
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults());
return http.build();
}
@Bean
public SecurityFilterChain webFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.permitAll()
);
return http.build();
}
}import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("{\"error\": \"Authentication required\"}");
}
}
// Configure in security chain
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.exceptionHandling(handling -> handling
.authenticationEntryPoint(new CustomAuthenticationEntryPoint())
);
return http.build();
}import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
@Bean
public AuthenticationEntryPoint delegatingEntryPoint() {
return DelegatingAuthenticationEntryPoint.builder()
.addEntryPointFor(
new Http403ForbiddenEntryPoint(),
PathPatternRequestMatcher.pathPattern("/api/**")
)
.addEntryPointFor(
new LoginUrlAuthenticationEntryPoint("/login"),
PathPatternRequestMatcher.pathPattern("/admin/**")
)
.defaultEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
.build();
}import org.springframework.security.web.FilterChainDecorator;
import org.springframework.security.web.ObservationFilterChainDecorator;
import io.micrometer.observation.ObservationRegistry;
import jakarta.servlet.FilterChain;
@Configuration
public class ObservableSecurityConfig {
@Bean
public FilterChainProxy filterChainProxy(List<SecurityFilterChain> filterChains,
ObservationRegistry observationRegistry) {
FilterChainProxy proxy = new FilterChainProxy(filterChains);
// Add observability support
proxy.setFilterChainDecorator(
new ObservationFilterChainDecorator(observationRegistry)
);
return proxy;
}
}import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
@Configuration
public class MultiChainConfig {
// High priority: API endpoints
@Bean
@Order(1)
public SecurityFilterChain apiChain(HttpSecurity http) throws Exception {
http
.securityMatchers(matchers -> matchers
.requestMatchers(PathPatternRequestMatcher.pathPattern("/api/**"))
)
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.httpBasic(basic -> {})
.csrf(csrf -> csrf.disable());
return http.build();
}
// Lower priority: Web endpoints
@Bean
@Order(2)
public SecurityFilterChain webChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
);
return http.build();
}
}import org.springframework.security.web.firewall.RequestRejectedException;
import org.springframework.security.web.firewall.RequestRejectedHandler;
public class LoggingRequestRejectedHandler implements RequestRejectedHandler {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
RequestRejectedException requestRejectedException)
throws IOException, ServletException {
logger.warn("Request rejected: {} {} from {} - {}",
request.getMethod(),
request.getRequestURI(),
request.getRemoteAddr(),
requestRejectedException.getMessage()
);
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
response.setContentType("application/json");
response.getWriter().write(
"{\"error\": \"Invalid request\", \"message\": \"" +
requestRejectedException.getMessage() + "\"}"
);
}
}
// Configuration
@Bean
public FilterChainProxy filterChainProxy(List<SecurityFilterChain> filterChains) {
FilterChainProxy proxy = new FilterChainProxy(filterChains);
proxy.setRequestRejectedHandler(new LoggingRequestRejectedHandler());
return proxy;
}