The first article from this category covers the subject of Spring Security ACL features. But we can still use Spring Security without the roles management. And to do it correctly, we should be familiar with a concept calling security chain.
Data Engineering Design Patterns

Looking for a book that defines and solves most common data engineering problems? I wrote
one on that topic! You can read it online
on the O'Reilly platform,
or get a print copy on Amazon.
I also help solve your data engineering problems 👉 contact@waitingforcode.com 📩
In this article we'll cover a very important topic of Spring Security project: security filter chain. At the begin we'll discover how Spring Security handles authentication and authorization requests. After that we'll start to talking about available security filters.
Authentication and authorization handling in Spring Security
Authentication and authorization process in Spring Security project are handled with filters technology. javax.servlet.Filter objects are standard part of Java Servlet API. They're objects which can be invoked on every request (independently on requested resource's type: static or dynamic). They all have 3 methods:
- init(FilterConfig config): called by servlet container after the filter is instantiated. This method is called only once and can be used, for example, to configure filter object with specified parameters. These parameters are retrieved from FilterConfig instance.
- doFilter(ServletRequest request, ServletResponse response, FilterChain chain): this is working method which makes some filtering operations on request and response objects. It's here for, for example, examine ServletRequest object and check if demanded resource is allowed to user making the request.
- destroy(): as the name indicates, this method is called when filter is taken out of service.
Filter are defined in web.xml descriptor file within
<filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
It may look strangely that we are supposed to use FilterChainProxy but we are defining DelegatingFilterProxy in web.xml. But is not. Let's take a look on DelegatingFilterProxy class to see what it does. This object comes from Spring web project and helps to dispatch request catched by filter mapping to appropriate Spring bean. This strategy allows to take full advantage of Spring environment because filters invoked by servlet containers are Spring-managed beans and not detached objects, difficult to plug to Spring's application context. The name of bean to invoke is defined in targetBeanName parameter. If this parameter is absent, DelegatingFilterProxy uses filter name to find appropriate bean. As you can see in our example, bean used to execute security requests will be called springSecurityFilterChain and it corresponds to already mentioned FilterChainProxy.
One mystery is solved. Now we can focus on another one, FilterChainProxy. This class extends org.springframework.web.filter.GenericFilterBean. Through this inheritance, it also implements javax.servlet.Filter interface, so it can be treated as standard filter with doFilter method implemented. Content of method used to filter the requests looks like:
show doFilter implementationAs you can see, it filters against FirewalledRequest which is a secured version of ServletRequest object. This secured version allows to deal with normalized requests objects. Normalization process consists on translate received path on the path that will always be able to match security patterns. For example, it can resolve multiple forward slashes (//) into one slash and do not break security strategy with incorrectly formatted request. After the normalization, FilterChainProxy gets a list of filters, wrapps them into inner class VirtualFilterChain and executes through it. How does FilterChainProxy know about filters to call ? It's able to resolve this situation thanks to its constructors:
private List<SecurityFilterChain> filterChains; public FilterChainProxy(SecurityFilterChain chain) { this(Arrays.asList(chain)); } public FilterChainProxy(List<SecurityFilterChain> filterChains) { this.filterChains = filterChains; }We can specify these parameters with a simple bean definition:
<-- bean definition here--> <constructor-arg> <list> <spring-security:filter-chain pattern="/secure/**", filters="filter1, filter2" /> <spring-security:filter-chain pattern="/no-secure/**", filters="filter3" /> </list> </constructor-arg> <-- end of bean definition here -->
If you compare doFilter with bean's XML definition, you will see that filters are executed in the order of definition (ie. filter1 before filter2). However, the order of definition is important and some filters shouldn't be executed before or after others, ie:
- ChannelProcessingFilter: to redirect the request to another protocol
- SecurityContextPersistenceFilter: to allow copy security details to HttpSession
- ConcurrentSessionFilter: because it uses functionalities of objects manipulated in previous step (SecurityContextHolder) and updates session informations
- UsernamePasswordAuthenticationFilter, CasAuthenticationFilter, BasicAuthenticationFilter and other authentication mechanisms: to update security context created at the 2nd step with details of authenticated user.
- SecurityContextHolderAwareRequestFilter: if we want to install HttpServletRequestWrapper into servlet container
- JaasApiIntegrationFilter: executed when JaasAuthenticationToken is present in SecurityContextHolder.
- RememberMeAuthenticationFilter: if authentication wasn't processed and a remember-me cookie is present in the request.
- AnonymousAuthenticationFilter: when user is not authenticated, neither by authentication mechanism nor by remember-me cookie.
- ExceptionTranslationFilter: to catch all Spring Security exceptions.
- FilterSecurityInterceptor: to handle ACL and raise exceptions when access is denied for given URL.
Spring Security filters
We already know the main purposes of majority of filters. But we haven't implemented them yet. We'll do this in this article.
- ChannelProcessingFilter -
Thanks to this filter we can be sure that request passes through right channel. It can be useful in the situations where all security layer must be managed over HTTPS. In this case, if one of these security requests passes in HTTP, ChannelProcessingFilter will redirect it into HTTPS protocol.
Internally it uses org.springframework.security.web.access.channel.ChannelDecisionManager which decides what to do with analyzed request. This decidor is helped by instances of ChannelProcessor object which, in its turn, uses an implementation of AbstractRetryEntryPoint to make a redirect. The redirect is made in commence(HttpServletRequest request, HttpServletResponse response) method of this abstract class and looks like:
String queryString = request.getQueryString(); String redirectUrl = request.getRequestURI() + ((queryString == null) ? "" : ("?" + queryString)); Integer currentPort = Integer.valueOf(portResolver.getServerPort(request)); Integer redirectPort = getMappedPort(currentPort); if (redirectPort != null) { boolean includePort = redirectPort.intValue() != standardPort; redirectUrl = scheme + request.getServerName() + ((includePort) ? (":" + redirectPort) : "") + redirectUrl; } if (logger.isDebugEnabled()) { logger.debug("Redirecting to: " + redirectUrl); } // redirectStrategy is an instance of org.springframework.security.web.RedirectStrategy // interface and defines the way to execute the redirects. Its default implementation // (DefaultRedirectStrategy) uses HttpServletResponse's sendRedirect() method. redirectStrategy.sendRedirect(request, response, redirectUrl);
- SecurityContextPersistenceFilter -
As we mentioned, thanks to this filter we can make org.springframework.security.core.context.SecurityContext data persisting between HTTP request. SecurityContext is an object representing security information about request and user. It allows to retrieve org.springframework.security.core.Authentication object associated to the request.
SecurityContext data is first put into org.springframework.security.core.context.SecurityContextHolder object. Thanks to this container object we can easily get SecurityContext associated to current request from elsewhere in the application. And even in the situations when we don't have direct access to HttpServletRequest or HttpServletResponse.
At the end of request lifetime, SecurityContextPersistanceFilter is called again. This time it uses defined SecurityContextRepository to store SecurityContext data to next request. Repository used by default is HttpSessionSecurityContextRepository, based on standard HTTP sessions.
If we're looking on source code, we'll see that they're 2 security contexts in use: the first created before security chain execution and the second retrieved directly from SecurityContextHolder, so containing potential changes made by another security filters. You can see it thanks to try-finally block analyze:
// private SecurityContextRepository repo SecurityContext contextBeforeChainExecution = repo.loadContext(holder); try { SecurityContextHolder.setContext(contextBeforeChainExecution); chain.doFilter(holder.getRequest(), holder.getResponse()); } finally { SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext(); // Crucial removal of SecurityContextHolder contents - do this before anything else. SecurityContextHolder.clearContext(); repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse()); request.removeAttribute(FILTER_APPLIED); }
Thanks to this code analyze we understand better why SecurityContextPersistenceFilter should be called before all filters changing SecurityContext. In fact, invoking them in bad order should make SecurityContext in incoherent state.
- ConcurrentSessionFilter -
This filter has two purposes: refresh last modified time for request's session and control if session isn't expired. This first feature is made with refreshLastRequest(String sessionId) method of org.springframework.security.core.session.SessionRegistry implementation.
The control on session expiration is made with SessionInformation object which provides isExpired() method. If it returns true, it means that session is out-of-date. If it's the case, it logouts connected user (ie. invalidates org.springframework.security.core.Authentication instance) and redirects to "session expired" page.
- UsernamePasswordAuthenticationFilter (and other authentication filters) -
This filter is used to handle authentication request. It extends abstract AbstractAuthenticationProcessingFilter class. All authentication logic is implemented by doFilter(ServletRequest req, ServletResponse res, FilterChain chain) of this class. First, it checks if current request is the authentication request. It makes it by comparing requests path to filterProcessesUrl parameter. This parameter represents the URL in which authentication request will be proceesed.
If request is authentication request, attemptAuthentication(HttpServletRequest request, HttpServletResponse response) method is invoked. In UsernamePasswordAuthenticationFilter this method starts by checking if only POST requests are accepted. If it's the case, and the current request is not POST, an AuthenticationServiceException is thrown. Otherwise AuthenticationManager's authenticate method is invoked and the system tries to authentify user with captured login and password parameters. By default, they're named, respectively, j_username and j_password.
When authentication succeeds, UsernamePasswordAuthenticationFilter makes two things: nothing or it continues the execution of security filters chain. If authentication fails, one of org.springframework.security.web.authentication.AuthenticationFailureHandler implementations is used to handle this situation. By default, SimpleUrlAuthenticationFailureHandler is used. It returns the response in 401 state if failure page is not defined. Otherwise, it redirects or forwards to failure page.
- RememberMeAuthenticationFilter -
When standard authentication failed, Spring Security checks if user has a "remember-me" cookie valable for given webapp. It detects that through RememberMeAuthenticationFilter. This filter starts by ensuring that current SecurityContext doesn't contain Authentication object. If it's the case, it uses the right implementation of org.springframework.security.web.authentication.RememberMeServices interface to resolve "remember-me" cookie to Authentication object.
Two possible resolving methods exist. The first one is handled by TokenBasedRememberMeServices class. It searches user's login value in encoded "remember-me" cookie. After it retrieves user corresponding to this login from org.springframework.security.core.userdetails.UserDetailsService used by whole application.
The second method, handled by PersistentTokenBasedRememberMeServices, uses different elements to resolve "remember-me" cookie. It looks for token identifier included in the remember-me cookie. After it uses it to search user associated to this identifier in the persistent storage (as database). This search process is helped by PersistentTokenRepository implementations. One of them, JdbcTokenRepositoryImpl, will look for user to authentify by comparing cookie's identifier with a value of series column from persistent_logins table.
Both of these resolving methods extend AbstractRememberMeServices which Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) method is invoked by RememberMeAuthenticationFilter. Inside this method, AbstractRememberMeServices call processAutoLoginCookie method of used remember-me service.
If auto-login through remember-me service success, RememberMeAuthenticationFilter attempts to make full authentication with org.springframework.security.authentication.AuthenticationManager implementation. Its authenticate(Authentication authentication) returns fully Authentication. Unlike Authentication object passed in parameter, Authentication instance returned by authenticate method contain supplementary informations as granted authorities. This returned object is after stored in SecurityContext.
When remember-me authentication fails, remember-me cookie is removed.
- AnonymousAuthenticationFilter -
This filter ensures us that there'll be always at least one Authentication object in SecurityContext. If any from the previously executed filters succeeded to put Authentication object to SecurityContext, AnonymousAuthenticationFilter does it. First, it checks if they're no Authentication in current context. If it's the case, it creates new Authentication's implementation, instance of org.springframework.security.web.authentication.AnonymousAuthenticationToken. After that this object is put into SecurityContext and the execution of security filters chain continues.
- ExceptionTranslationFilter -
This filter is used to render authentication failures for final user. It handles two cases:
- authentication error (AuthenticationException): user is redirected to login page through already described org.springframework.security.web.AuthenticationEntryPoint.
- authorization error (AccessDeniedException) : when user is not authorized to access a resource (for example: URL of page reserved to administrators), AccessDeniedException is thrown. When it's anonymous user who tries to reach protected resource, he's redirected to login page (as in the case of AuthenticationException). The case of authentified user who is not allowed to consult protected resource, is handled by AccessDeniedHandler. Its default implementation, AccessDeniedHandlerImpl, will sends a response in 403 status (forbidden) and will display error page.Security exceptions resolving is made in below handleSpringSecurityException method:
private void handleSpringSecurityException(HttpServletRequest request, HttpServletResponse response, FilterChain chain, RuntimeException exception) throws IOException, ServletException { if (exception instanceof AuthenticationException) { logger.debug("Authentication exception occurred; redirecting to authentication entry point", exception); sendStartAuthentication(request, response, chain, (AuthenticationException) exception); } else if (exception instanceof AccessDeniedException) { if (authenticationTrustResolver.isAnonymous(SecurityContextHolder.getContext().getAuthentication())) { logger.debug("Access is denied (user is anonymous); redirecting to authentication entry point", exception); sendStartAuthentication(request, response, chain, new InsufficientAuthenticationException( "Full authentication is required to access this resource")); } else { logger.debug("Access is denied (user is not anonymous); delegating to AccessDeniedHandler", exception); accessDeniedHandler.handle(request, response, (AccessDeniedException) exception); } } } protected void sendStartAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, AuthenticationException reason) throws ServletException, IOException { // SEC-112: Clear the SecurityContextHolder's Authentication, as the // existing Authentication is no longer considered valid SecurityContextHolder.getContext().setAuthentication(null); requestCache.saveRequest(request, response); logger.debug("Calling Authentication entry point."); authenticationEntryPoint.commence(request, response, reason); } public void setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) { Assert.notNull(accessDeniedHandler, "AccessDeniedHandler required"); this.accessDeniedHandler = accessDeniedHandler; }
- FilterSecurityInterceptor -
This filter is executed after all of previously presented ones. It's responsible for handling authorization, ie. it decides if user can access protected resource. FilterSecurityIntereceptor uses an instance of FilterInvocationSecurityMetadataSource which represents protected resources. Each resource is represented by URL's path. The entries are matched one by one, from the begin to the end. When matcher finds entry corresponding to request's path, it ends the search.
FilterSecurityInterceptor uses an instance of org.springframework.security.access.AccessDecisionManager to decide if current Authentication object is authorized to reach protected resource. When authorization fails, an AccessDeniedException is thrown.
In this article we discovered one of key concepts of Spring Security - filter security chain. At the first part we explained this idea more globally by reminding the definition of filters. After that, we focused more on Spring Security filters and presented some of important filters of this project.
Consulting

With nearly 16 years of experience, including 8 as data engineer, I offer expert consulting to design and optimize scalable data solutions.
As an O’Reilly author, Data+AI Summit speaker, and blogger, I bring cutting-edge insights to modernize infrastructure, build robust pipelines, and
drive data-driven decision-making. Let's transform your data challenges into opportunities—reach out to elevate your data engineering game today!
👉 contact@waitingforcode.com
đź”— past projects