Skip to main content

Getting filters to play nicely with Spring

After having some struggle with getting filters to integrate well with my Spring context, I decided to write down a small tutorial for it...

To begin with, filters are defined in web.xml and are therefore not automatically part of the Spring application context, which is normally set up using the DispatcherServlet. A good way to abstract away all this is to use the DelegatingFilterProxy, which is a Spring utility class that acts as a filter, but will delegate the doFilter-method to a Spring-managed bean - normally a class extending GenericFilterBean.

As a filter is defined in web.xml and looks for the bean defined in the Spring context in applicationContext.xml, it's not really the set up that I want. Filters are part of a web application and should have access to what's usually defined in webmvc-config.xml, as part of the DispatcherServlet set up. To be able to use, e.g. request scoped beans in applicationContext.xml, you need to defined them as proxied objects in order to have them looked up at a time where the web context is available.

The small application I'll be showing here is set up with an example filter that stores a request header value into a request scoped variable which is available as a Spring bean, and can therefor be autowired in any other Spring bean later on in the execution of the request. This demonstrates a nice way to have a loose coupling between the different components, without having to e.g. pass the information in method variables. It also gives the ability to test the components outside a web context.

To begin with, a normal filter definition using the DelegatingFilterProxy needs to be added to web.xml:

    
    
        Header Filter
        org.springframework.web.filter.DelegatingFilterProxy
        
        
            targetBeanName
            headerFilter
        
    

    
        Header Filter
        /*
    


Make sure the init param "targetBeanName" is set to the name of the Spring bean to delegate to (or rely on the default, where the filter's name is used to look up the bean). Now, let's have a look at the definition of the Spring filter bean and the request scoped bean in applicationContext.xml, incl. the acutal implementation of the filter:

    
    

     
    
        
   


Note how the request scoped bean is defined to be a proxied, using
.

    package com.technowobble.web;

    import java.io.IOException;
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.filter.GenericFilterBean;

    /**
     * Intercepts all incoming requests and parses out Ninaa headers into a session scoped variable.
     */
    public class HeadersFilter extends GenericFilterBean {
        @Autowired
        private Headers headers;

        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            if (request instanceof HttpServletRequest) {
                HttpServletRequest httpRequest = (HttpServletRequest) request;
                Headers.Types[] values = Headers.Types.values();
                for (int i = 0; i < values.length; i++) {
                    headers.setHeader(values[i], httpRequest.getHeader(values[i].toString()));
                }
            }

            chain.doFilter(request, response);
        }
    }


For this set up to work and have the filter to acutually be able to look up the real object at runtime, we need to add a RequestListener to web.xml, which will wire up the request context to all beans running outsite of the DispatcherServlet, i.e. our filter.

    
    
      org.springframework.web.context.request.RequestContextListener
    


Ok - so far so good. Let's wire it up together in a controller, which uses an example component that will serve as the component that will use the information gathered from the filter. Note that the component used by the controller is not aware of any request/response objects and doesn't have any parameters in it's methods.

    package com.technowobble.controller;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseBody;
    import com.technowobble.component.MyComponent;

    @Controller
    @RequestMapping("/myController")
    public class MyController {
        @Autowired
        MyComponent myComponent;
        
        @ResponseBody
        @RequestMapping(method = RequestMethod.GET)
        public String createResponse() {
            return myComponent.getSomethingDependingOnHeaders();
        }
    }


The @ResponseBody is just an easy way to get rid of all view lookups and send the header values straight to the browser...

    package com.technowobble.component;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;

    import com.technowobble.web.Headers;

    @Component
    public class MyComponent {
        @Autowired
        Headers headers;
        
        public String getSomethingDependingOnHeaders() {
            return "You are accepting " + headers.getHeader(Headers.Types.Accept);
        }
    }


Now, check if everything workes as expected by launching the application using "mvn jetty:run" and browse to "http://localhost:8080/technowobble/myController/".

The last thing to report is how to test the MyComponent in a separate JUnit-test... First of all, there's no web context in a JUnit-test, and therefor no notion of a request scope. This is solved by defining a custom scope (implemented by a ThreadLocal implementation) in a separate applicationContext-test.xml which is used in the test.

  
    
      
        
          
        
      
    
  


After this, the actual test is straightforward:

    package com.technowobble.component;

    import static org.junit.Assert.assertEquals;

    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

    import com.technowobble.web.Headers;

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations="classpath*:META-INF/spring/applicationContext*.xml")
    public class MyComponentTest {
        @Autowired 
        private Headers headers;
        
        @Autowired
        private MyComponent myComponent;
        
        private static final String ACCEPT = "text/html";

        @Before
        public void setUp() throws Exception {
            headers.setHeader(Headers.Types.Accept, ACCEPT);
        }

        @Test
        public void test() {
            assertEquals(myComponent.getSomethingDependingOnHeaders(), "You are accepting " + ACCEPT);
        }
    }


Note that the headers bean is setup during the setUp()-method and that the myComponent have access to it using normal dependency injection.

That's it. The full source code can be downloaded from here

Thanks for reading!

Comments

  1. Nice example code! Your readers may also want to see my Servlet filter tutorial. I recommend using filters for tasks like blocking specific IP access, compressing response and stopping malicious attacks.

    ReplyDelete
  2. nice exampe if you share the working full code will be very help full.

    ReplyDelete

Post a Comment

Popular posts from this blog

GWT and Spring Security

Update! - Based on the post below, and my other post regarding Spring Security and OpenID, I have added Open-ID support to the sample application below. For those interested, here's the write-up of changes. I've spent quite some time digging into ways of integrating GWT and Spring Security. It all started by reading the following post in the GWT Forum - Best practices/ideas for GWT with Spring Security (or equivalent) , and then checking out this blog - GWT and Spring Security . To make matters worse, I started reading Security for GWT Applications and specifically about the "Cross-Site Request Forging"-attacks. Now, what could I do about it? Well, starting by setting up my own project (Maven-based) with all updated dependencies (GWT 2.0.3 etc) and started reading the Spring Security Reference Documentation (puh!). Instead of See Wah Cheng's approach of implementing a custom authentication service, I decided to rely on standard namespace configuration...

Using Spring Security's OpenID implementation (openid4java) on Google App Engine

The goal with this exercise is to have a running example of an OpenID login on a simple Spring application, using Google as the OpenID Provider. Note that the application will be running on Google App Engine and that Spring Roo is only used for simplicity of creating the project files. Any Spring-based application could use the same implementation. First of all, create a simple project using Spring Roo (or any equivalent framework), including the default security setup: project --topLevelPackage com.technowobble persistence setup --provider DATANUCLEUS --database GOOGLE_APP_ENGINE entity --class ~.domain.MyEntity field string --fieldName name controller all --package com.technowobble.controller security setup This setup only provides us with a form-login, which is not what we wanted. So what about OpenID? Well, if it wasn't for Google App Engine, I would happily have added an <openid-login>-tag to applicationContext-security.xml, but things are never that easy, are the...

Google Apps Script and ES Modules

Currently, Google Apps Script does not support ES modules - and any usage of export/import will fail. One way of handling this is to use rollup.js to bundle your project into one single JavaScript file. The trick here is to make sure not to export any functions in your entry point code, e.g. index.ts , and to prevent any generation of export statement in the final bundle (see the custom rollup plugin in the rollup.config.js below). import { babel } from "@rollup/plugin-babel"; import { nodeResolve } from "@rollup/plugin-node-resolve"; const extensions = [".ts", ".js"]; const preventThreeShakingPlugin = () => { return { name: 'no-threeshaking', resolveId(id, importer) { if (!importer) { // let's not theeshake entry points, as we're not exporting anything in Apps Script files return {id, moduleSideEffects: "no-treeshake" } } return null; }...