Hi, I am looking for Spring/Summer - 2015 Internships. I am also interested in full time opportunities starting June 2015. My Resume

Search This Blog

Loading...

Tuesday, 3 December 2013

DelegatingFilterProxy And Spring Security

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
  1. Retrieve the ApplicationContext.
  2. Access the beans from the ApplicationContext.
  3. Use the beans.
This isn't the cleanest code to use. Or even the most manageable. Any change in the name of the bean and the code breaks !
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")
public class BasicFilter implements Filter {

    // This will be injected here
    @Autowired
    private UserManager userManager;

    @Override
    public void doFilter(ServletRequest servletRequest,
            ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        System.out.println("In basicFilter: userManager is " + userManager);
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void init(FilterConfig paramFilterConfig) throws ServletException {
        // do nothing
    }

    @Override
    public void destroy() {
        // do nothing
    }

}
As seen here I have defined the filter to be a Spring bean. The bean also has a dependency UserManager.
@Component
public class UserManager {

}
Next is the filter configuration.
<filter>
    <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>
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:
In basicFilter: userManager is com.web.filter.UserManager@5af55f
So 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.
public class DelegatingFilterProxy extends GenericFilterBean {

    private WebApplicationContext webApplicationContext;
    private String targetBeanName;
    private Filter delegate;
    private final Object delegateMonitor = new Object();

    // other members and methods...

}
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:
protected void initFilterBean() throws ServletException {
        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);
                }
            }
        }
    }
As a part of the initialization process:
  1. The class has fetched the filter- name. This name must be same as name of the target bean. 
  2. It uses this name to retrieve the bean (which is an instance of Filter class) from the Spring Application Context.
  3. Once the bean is retrieved it is assigned to the Delegate reference
  4. All Filter API calls are now delegated to this bean instance.
For e.g. when the doFilter is invoked for the DelegatingFilterProxy:
public void doFilter(ServletRequest request, ServletResponse response,
            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);
    }
So how does this work in Spring Security ?
With Spring security the web.xml definition would be :
<filter>
  <filter-name>springSecurityFilterChain</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
The filter name "springSecurityFilterChain" represents an bean of type FilterChainProxy.
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 component 
[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
References:
  1. http://stackoverflow.com/questions/6725234/whats-the-point-of-spring-mvcs-delegatingfilterproxy 
  2. http://forum.springsource.org/showthread.php?20230-Howto-The-joy-that-is-DelegatingFilterProxy 
  3. http://blog.springsource.com/2010/03/06/behind-the-spring-security-namespace/

No comments:

Post a Comment