Spring Security relies on a critical component to integrate seamlessly with web applications: the DelegatingFilterProxy. This filter acts as a bridge between the Servlet world and Spring’s powerful dependency injection system, ensuring that security-related logic is managed within the Spring context.
In this comprehensive guide, we’ll:
- Uncover why DelegatingFilterProxy is essential in Spring Security
- Explore its role as an implementation of the Bridge design pattern
- Dissect its internal workings and key methods
By the end of this article, you’ll have a deep understanding of how DelegatingFilterProxy enables smooth communication between Servlet filters and Spring-managed beans.tingFilterProxy enables seamless integration between Servlet filters and Spring-managed beans.
Example Code
This article is accompanied by the following code example: GitHub Link.
The Need for DelegatingFilterProxy
Maven Project Setup
- Create a maven project using the web archetype
- Add Spring webmvc dependency to
pom.xml
:
<properties>
<spring.version>6.2.3</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
Spring MVC Configuration
- Create base configuration class:
@Configuration
@EnableWebMvc
@ComponentScan("com")
public class AppConfig {
}
This configuration class alongside with @ComponentScan("com")
Tells Spring to scan the “com” package and its sub-packages for components (e.g., @Controller
, @Service
, @Repository
).
- Configure DispatcherServlet:
public class DispatcherServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] {AppConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[] {"/"};
}
}
This configuration maps DispatcherServlet to handle all incoming requests (“/”) and loads web-specific configurations from AppConfig.
Note: For detailed information about DispatcherServlet configuration, refer to “The 3 Ways to Configure DispatcherServlet In Spring MVC” article.
Creating a Simple Servlet Filter
Before diving into DelegatingFilterProxy, let’s first understand the problem that necessitated its creation. Suppose we want to build an AuthenticationFilter that retrieves the username and password from the request and then authenticates the user (verifying user existence, password match, etc.)—all of this before reaching the Front Controller (DispatcherServlet). The simplest way to achieve this is by developing a Filter.
Here’s a basic implementation of such a filter:
package com.javalaunchpad.filter;
import com.javalaunchpad.service.UserService;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
public class SimpleAuthenticationFilter implements Filter {
@Autowired
private UserService userService; // Assume this service handles user authentication
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// Initialization code
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String username = httpRequest.getParameter("username");
String password = httpRequest.getParameter("password");
System.out.println(username + " " + password);
if (userService.authenticate(username, password)) {
// Authentication successful
chain.doFilter(request, response);
} else {
// Authentication failed
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failed");
}
}
@Override
public void destroy() {
// Cleanup code
}
}
Here is a dummy implementation of the UserService
:
@Service
public class UserService {
public boolean authenticate(String username, String password) {
System.out.println("Authenticating user " + username);
return true;
}
}
We then register this filter in web.xml
:
<filter>
<filter-name>SimpleAuthenticationFilter</filter-name>
<filter-class>com.javalaunchpad.filter.SimpleAuthenticationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SimpleAuthenticationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
This configuration ensures that every incoming request passes through SimpleAuthenticationFilter
, allowing it to handle authentication before reaching the application’s servlets or resources.
When running the application, you will observe the following error:

The Problem: Injecting a Spring Bean into a Servlet Filter
Servlet Filters present a unique challenge when it comes to integrating with Spring’s dependency injection. The root of this issue lies in the initialization order of web application components. Servlet containers (ie, Tomcat ) initialize filters before they initialize servlets, including the DispatcherServlet responsible for creating Spring’s WebApplicationContext. This timing mismatch means that when a filter’s init() method is called, the Spring context hasn’t been created yet, making it impossible to inject Spring-managed beans directly into the filter. Furthermore, Servlet Filters are managed by the Servlet container, not the Spring container, creating a disconnect between these two ecosystems. This separation prevents filters from leveraging Spring’s powerful dependency injection, and other features that Spring-managed beans enjoy.
Solution: Bridging Servlet Filters with Spring
Implementing a Spring-Managed Filter
A logical solution to this problem is to separate the filter’s functionality from its Servlet container integration. Instead of making SimpleAuthenticationFilter a Servlet Filter directly, we can transform it into a Spring-managed bean. This approach allows it to fully leverage Spring’s features such as dependency injection, AOP, and lifecycle management.
To bridge the gap between the Servlet world and Spring’s ApplicationContext, we need a special filter that acts as a proxy. This proxy filter would:
- Be initialized early by the Servlet container
- Intercept incoming requests
- Retrieve the WebApplicationContext when it’s available
- Lookup our SimpleAuthenticationFilter bean
- Delegate the actual filtering logic to this Spring-managed bean
Using DelegatingFilterProxy
The Spring-managed filter that we want to implement is exactly what Spring’s DelegatingFilterProxy
provides. It acts as a bridge between the Servlet container’s filter chain and Spring’s ApplicationContext
.
By using DelegatingFilterProxy, we can keep our SimpleAuthenticationFilter as a Spring bean (we added @Component(“simpleAuthenticationFilter”) ):
package com.javalaunchpad.filter;
import com.javalaunchpad.service.UserService;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component("simpleAuthenticationFilter")
public class SimpleAuthenticationFilter implements Filter {
@Autowired
private UserService userService; // Assume this service handles user authentication
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// Initialization code
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String username = httpRequest.getParameter("username");
String password = httpRequest.getParameter("password");
System.out.println(username + " " + password);
if (userService.authenticate(username, password)) {
// Authentication successful
chain.doFilter(request, response);
} else {
// Authentication failed
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failed");
}
}
@Override
public void destroy() {
// Cleanup code
}
}
Here is how we define DelegatingFilterProxy in web.xml
and delegate filtering to a Spring-managed bean called simpleAuthenticationFilter:
<filter>
<filter-name>delegatingFilterProxy</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>simpleAuthenticationFilter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>delegatingFilterProxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
The targetBeanName
parameter in the web.xml
configuration tells the DelegatingFilterProxy which bean to look up in the Spring ApplicationContext. In this case, it points to the bean named simpleAuthenticationFilter
(as defined with the @Component("simpleAuthenticationFilter")
annotation). This explicit naming is crucial because it allows for easier modification and testing of the filter logic, as you can now manage it using Spring’s features rather than being tightly bound to the Servlet container’s lifecycle.
In order to delegate the request to the actual Spring Security filter chain, you should specify springSecurityFilterChain
as the target bean name. This is important because the bean is responsible for executing a set of filters before ultimately passing the request to the DispatcherServlet
.
Here’s how you can configure it in web.xml
:
<filter>
<filter-name>delegatingFilterProxy</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>springSecurityFilterChain</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>delegatingFilterProxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Important Note:
By default, the springSecurityFilterChain
bean is not created. However, as we explained in our previous article, you need to explicitly enable Spring Security to create it. To achieve this, you can annotate a configuration class with @EnableWebSecurity
, as shown below:
@Configuration
@EnableWebSecurity(debug = true)
public class SecurityConfig {
}
This ensures that Spring Security initializes the necessary filters and integrates them into the request processing pipeline.
Additionally, make sure to add the necessary Spring Security dependencies in your pom.xml
, especially when working with the actual Spring Security filter chain.
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.version}</version>
</dependency>
Internals of DelegatingFilterProxy
Internally, DelegatingFilterProxy works as follows:
- It is instantiated by the servlet container and initialized with a bean name (e.g.,
springSecurityFilterChain
). - During initialization, it retrieves the corresponding Spring bean from the application context.
- Calls to
doFilter()
are delegated to the Spring-managed filter bean.
Here’s a simplified version of DelegatingFilterProxy (you can read the complete source code of the class on GitHub):
public class DelegatingFilterProxy extends GenericFilterBean {
private String targetBeanName;
private WebApplicationContext webApplicationContext;
private Filter delegate;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// Lazily get the delegate from Spring context
Filter delegateToUse = getDelegateFilter();
// Delegate the filtering to our Spring-managed bean
delegateToUse.doFilter(request, response, chain);
}
private Filter getDelegateFilter() {
if (delegate == null) {
if (webApplicationContext == null) {
webApplicationContext = findWebApplicationContext();
}
delegate = webApplicationContext.getBean(targetBeanName, Filter.class);
}
return delegate;
}
}
This implementation highlights how DelegatingFilterProxy dynamically retrieves the actual filter bean and delegates all operations to it.
Conclusion
DelegatingFilterProxy is a powerful tool that bridges the gap between servlet filters and Spring’s dependency injection. It enables the seamless integration of filters within Spring’s ecosystem, promoting cleaner and more modular code.
In the next article, we will dive deeper into FilterChainProxy, which plays a crucial role in managing multiple filters within Spring Security. Stay tuned!