Search This Blog

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/

25 comments:

  1. Thankyou for share, this is information very useful for all..

    ReplyDelete
  2. Hi, excellent blog, thanks for sharing it.
    I just wanted to comment a bit on this one and the GenericFilterBean here: http://learningviacode.blogspot.be/2013/07/filters-and-spring.html.
    You argue here that DelegatingFilterProxy transfers the code to our bean, UserManager, which was somewhat more cumbersome with GenericFilterBean. However, if your AuditRequestFilter gets the annotation @Component and inside that class you declare a member:
    @Autowired
    private UserManager userManager,
    don't you get the same effect as here, with the DelegatingFilterProxy ? So, what's the added value of DelegatingFilterProxy w.r.t. the beans ?

    Thanks,
    Greetings,
    Sorin

    ReplyDelete
  3. excellent.. thanks

    ReplyDelete
  4. Explanation was good,Beginners can learn easily thanks buddy

    ReplyDelete
  5. excellent, really helpful

    ReplyDelete
  6. Copy pasted your code and when i start my Tomcat got this error:

    SEVERE: Exception starting filter basicFilter
    org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'basicFilter' is defined
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:687)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1168)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:281)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:962)
    at org.springframework.web.filter.DelegatingFilterProxy.initDelegate(DelegatingFilterProxy.java:324)
    at org.springframework.web.filter.DelegatingFilterProxy.initFilterBean(DelegatingFilterProxy.java:235)
    at org.springframework.web.filter.GenericFilterBean.init(GenericFilterBean.java:199)
    at org.apache.catalina.core.ApplicationFilterConfig.initFilter(ApplicationFilterConfig.java:279)
    at org.apache.catalina.core.ApplicationFilterConfig.getFilter(ApplicationFilterConfig.java:260)
    at org.apache.catalina.core.ApplicationFilterConfig.(ApplicationFilterConfig.java:105)
    at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4917)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5609)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1574)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1564)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)

    ReplyDelete
  7. my bad...sorted it out. Thank you very much. Very clear document.

    Found this other document very useful:
    http://www.deadcoderising.com/2015-05-04-dependency-injection-into-filters-using-delegatingfilterproxy/

    ReplyDelete
  8. Thank its working fine for me.

    ReplyDelete
  9. Nice post and explanation.
    Thanks

    ReplyDelete
  10. Wow! I have read your article and it's so good I will share it with family and friends. I want to inform the travelers who want to visit Turkey that they need to fill a Turkey visa application form through the online process which is easy to track and get on email.

    ReplyDelete
  11. Congratulations on your article, it was very helpful and successful. 388889d0824830509e61d003f2769a0e
    website kurma
    website kurma
    numara onay

    ReplyDelete
  12. Thank you for your explanation, very good content. 8b8b97cc9dfda8d619ea10f45ec855bd
    altın dedektörü

    ReplyDelete
  13. جراحی زیبایی چانه یا ژنیوپلاستی بیشتر به عنوان تغییر موقعیت چانه شناخته می شود. ژنیوپلاستی نوعی جراحی است که روی چانه انجام می شود. هم جراحان پلاستیک و هم جراحان فک و صورت (جراحانی که روی دهان و فک کار می کنند) می توانند این نوع جراحی را انجام دهند. ژنیوپلاستی اغلب یک جراحی زیبایی است، به این معنی که افراد آن را برای ظاهر و نه به دلیل یک مشکل پزشکی انتخاب می کنند. به همین دلیل، اغلب تحت پوشش بیمه قرار نمی گیرد.

    جراحی زیبایی چانه شامل مجموعه ای از جراحی های استخوان، کاشت پروتز، تزریق و شکل دادن به استخوان چانه است که هدف از انجام آن، دادن اندازه و شکل مناسب به چانه است. در جراحی زیبایی چانه بسته به اینکه چانه فرد در جلوتر یا عقب تر از حد استاندارد باشد، می توان یکی از تکنیک های جراحی چانه را انجام داد تا چانه بزرگ یا کوچکتر شود.

    ReplyDelete
  14. در چند دهه اخیر استفاده از روش لیزر بدن بانوان با تحت‌تأثیر قراردادن فولیکول مو و تخریب آن موجب شده که رویش مجدد موها به‌مرور کم شده و در نهایت رفع می‌شود.
    لیزر موهای شکم

    ReplyDelete
  15. به طور معمول لیزر موهای زائد باعث ضخیم تر شدن مو نمی شود. اگر موها پس از اتمام دوره درمانی دوباره رشد کنند، معمولاً خیلی نرم تر و نازکتر از موهای قبلی هستند و هر شش ماه یکبار میتوان با یک جلسه درمانی رشد آنها را کنترل کرد.
    لیزر موهای ریز

    ReplyDelete