We saw an example of the GenericFilterBean earlier. While the class included some nifty techniques from Spring, it isn't really a part of the Spring Environment. We saw how to get access to the Spring Environment we had to
With Filters forming a very extensive part of web based Spring security having this type of code within the filters isn't ideal. This is where Spring developers have leveraged the power of the DelegatingFilterProxy.
To look at an example I built a simple filter which does .... nothing.
With Spring security the web.xml definition would be :
The class actually holds a list of filters or a filter chain which is capable of being matched against a Request in order to decide which of the filters apply to that request.
A review of the Spring Framework indicates that the Spring security code is not in one filter but in a series of filters. Nine Filters to be exact. Each filter is a modular component responsible for a certain functionality. For e.g. a SessionManagementFilter, an ExceptionTranslationFilter, an AnonymousAuthenticationFilter etc. This entire complexity is hidden behind the line:
- Retrieve the ApplicationContext.
- Access the beans from the ApplicationContext.
- Use the beans.
With Filters forming a very extensive part of web based Spring security having this type of code within the filters isn't ideal. This is where Spring developers have leveraged the power of the DelegatingFilterProxy.
To look at an example I built a simple filter which does .... nothing.
@Component(value = "basicFilter")As seen here I have defined the filter to be a Spring bean. The bean also has a dependency UserManager.
publicclass BasicFilter implements Filter {
// This will be injected here
@Autowired
private UserManager userManager;
@Override
publicvoid doFilter(ServletRequest servletRequest,
ServletResponse servletResponse, FilterChain filterChain)
throwsIOException, ServletException {
System.out.println("In basicFilter: userManager is " + userManager);
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
publicvoid init(FilterConfig paramFilterConfig) throws ServletException {
// do nothing
}
@Override
publicvoid destroy() {
// do nothing
}
}
@ComponentNext is the filter configuration.
publicclass UserManager {
}
<filter>This is the most significant part in the code. The filter-class argument is of the type DelegatingFilterProxy and not our FilterClass.If we run the code and hit any url then the console will display our message:
<filter-name>basicFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>basicFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
In basicFilter: userManager is com.web.filter.UserManager@5af55fSo how did this work ? What made the DelegatingFilterProxy transfer control to our Bean ?First things first DelegatingFilterProxy extends the genericFilterBean class thereby inheriting all the initializing code and abilities of the GenricFilterBean.
publicclassDelegatingFilterProxy extends GenericFilterBean {As a part of its init code, the DelegatingFilterProxy acquires its filter name (name given in web.xml) This is the default value which can be obtained by the filterConfig.getFilterName() method. Here is the code block taken from the class:
private WebApplicationContext webApplicationContext;
privateString targetBeanName;
private Filter delegate;
privatefinalObject delegateMonitor = newObject();
// other members and methods...
}
protected void initFilterBean() throws ServletException {As a part of the initialization process:
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
// If no target bean name specified, use filter name.
if (this.targetBeanName == null) {
this.targetBeanName = getFilterName();
}
// Fetch Spring root application context and initialize the
// delegate early, if possible.
// If the root application context will be started
// after this filter proxy, we'll have to resort to lazy
// initialization.
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
this.delegate = initDelegate(wac);
}
}
}
}
- The class has fetched the filter- name. This name must be same as name of the target bean.
- It uses this name to retrieve the bean (which is an instance of Filter class) from the Spring Application Context.
- Once the bean is retrieved it is assigned to the Delegate reference.
- All Filter API calls are now delegated to this bean instance.
public void doFilter(ServletRequest request, ServletResponse response,So how does this work in Spring Security ?
FilterChain filterChain) throws ServletException, IOException {
// Lazily initialize the delegate if necessary.
// ... code here
// Let the delegate perform the actual doFilter operation.
invokeDelegate(delegateToUse, request, response, filterChain);
}
protected void invokeDelegate(Filter delegate, ServletRequest request,
ServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
delegate.doFilter(request, response, filterChain);
}
With Spring security the web.xml definition would be :
<filter>The filter name "springSecurityFilterChain" represents an bean of type FilterChainProxy.
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
The class actually holds a list of filters or a filter chain which is capable of being matched against a Request in order to decide which of the filters apply to that request.
A review of the Spring Framework indicates that the Spring security code is not in one filter but in a series of filters. Nine Filters to be exact. Each filter is a modular component responsible for a certain functionality. For e.g. a SessionManagementFilter, an ExceptionTranslationFilter, an AnonymousAuthenticationFilter etc. This entire complexity is hidden behind the line:
<http auto-config="true">This is how the Spring Security flow is initiated for a request - through a simple filter to a Spring managed FilterChain to a series of pre configured Filters. Infact if we are running Spring Security with default settings and we change the name of the above filter than the whole thing will crash:
Caused by: org.apache.catalina.LifecycleException: Failed to start componentReferences:
[StandardEngine[Catalina].StandardHost[localhost].StandardContext[/SpringSim]]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
... 7 more
Caused by: java.lang.IllegalArgumentException: Filter mapping specifies an
unknown filter name springSecurityFilterChain
- http://stackoverflow.com/questions/6725234/whats-the-point-of-spring-mvcs-delegatingfilterproxy
- http://forum.springsource.org/showthread.php?20230-Howto-The-joy-that-is-DelegatingFilterProxy
- http://blog.springsource.com/2010/03/06/behind-the-spring-security-namespace/