Buscar este blog

sábado, 24 de enero de 2015

Java Servlet bypassFilter - Filter exclude path pattern - Filter wrapper

Servlet Filters are very useful components to check request parameters, authentication status, resource authorization, etc. But the main problem they have (at least one very big) is that you only can specify which URLs (patterns) are processed by de filter, but not which you don't want to apply.

For example, with this configuration the filter will intercept all '/secure/' and '/services/' paths.

<filter> 
 <filter-name>OneFilter</filter-name>
 <filter-class>com.MyFilterClass</filter-class>
</filter>

<filter-mapping>
 <filter-name>OneFilter</filter-name>
 <url-pattern>/secure/**</url-pattern>
 <url-pattern>/services/**</url-pattern>
</filter-mapping>

But, what I need is to have a filter which intercepts all paths, except some ones.

I found this great solution in gitHub, https://gist.github.com/superbob/5005291, developed by this superbob guy ;), but it was quite specific. So I decided to enhanced it and give some more config options.

The idea is the same, one filter, wrapper, wich has inside a inner filter. The wrapper filter is responsible of instanciate and initialize the inner filter.
There is a special class called FilterWrapperCondition which evaluate if the inner filter should be called or not. This is in fact an interface, and the particular implementations are configured with the filter config of the wrapper.

These are the main classes. The complete code is in github: https://github.com/evazquezma/JEE6/tree/master/wrapperFilter

WrapperFilter 
 package es.pruebas.jee6.filter.wrapper;  
 import java.io.IOException;  
 import javax.servlet.Filter;  
 import javax.servlet.FilterChain;  
 import javax.servlet.FilterConfig;  
 import javax.servlet.ServletException;  
 import javax.servlet.ServletRequest;  
 import javax.servlet.ServletResponse;  
 import org.slf4j.Logger;  
 import org.slf4j.LoggerFactory;  
 /**  
  * This filter wrapp a inner filter and decides when invoke it.  
  *   
  * If you are using Spring, you can get Spring beans by using "WebApplicationContext springContext =  WebApplicationContextUtils.getWebApplicationContext(config.getServletContext());".   
  * In this way you will not need instanciate class by hand.       
  *   
  * @author SISIFO  
  *  
  */  
 public class WrapperFilter implements Filter {  
      private static final Logger LOGGER = LoggerFactory.getLogger(WrapperFilter.class);  
      private static final String FILTER_PARAM_INNER_FILTER_CLASS = "wrapper.param.filterClass";  
      private static final String FILTER_PARAM_WRAPPER_CONDITION_CLASS = "wrapper.param.wrapperConditionClass";  
      private Filter innerFilter;  
      private FilterWrapperCondition filterWrapperCondition;  
      @Override  
      public void init(final FilterConfig filterConfig) throws ServletException {  
           LOGGER.info("Initializing filter inhibitor");  
           this.innerFilter = createAndInitInnerFilter(filterConfig);                 
           this.filterWrapperCondition = createAndInitFilterWrapperCondition(filterConfig);  
           LOGGER.info("Inhibitor engaged !");  
      }  
      private Filter createAndInitInnerFilter(final FilterConfig filterConfig) throws ServletException {  
           Filter filter = null;  
           try {  
                LOGGER.info("Loading inner filter: "+ filterConfig.getInitParameter(FILTER_PARAM_INNER_FILTER_CLASS));  
                filter = (Filter) Class.forName(filterConfig.getInitParameter(FILTER_PARAM_INNER_FILTER_CLASS)).newInstance();  
           } catch (InstantiationException | RuntimeException | ClassNotFoundException | IllegalAccessException e) {  
                LOGGER.error("exception while creating inner filter", e);  
                throw new RuntimeException(e);  
           }   
           filter.init(filterConfig);  
           return filter;  
      }  
      private FilterWrapperCondition createAndInitFilterWrapperCondition(FilterConfig filterConfig) {  
           FilterWrapperCondition wrapperCondition = null;  
           try {  
                LOGGER.info("Loading filter condition: "+ filterConfig.getInitParameter(FILTER_PARAM_WRAPPER_CONDITION_CLASS));  
                wrapperCondition = (FilterWrapperCondition) Class.forName(filterConfig.getInitParameter(FILTER_PARAM_WRAPPER_CONDITION_CLASS)).newInstance();  
           } catch (InstantiationException | RuntimeException | ClassNotFoundException | IllegalAccessException e) {  
                LOGGER.error("exception while creating filter condition", e);  
                throw new RuntimeException(e);  
           }   
           wrapperCondition.init(filterConfig);  
           return wrapperCondition;  
      }  
      @Override  
      public void destroy() {  
           LOGGER.info("Destroying wrapper filter");       
           LOGGER.info("Destroying inner filter");  
           innerFilter.destroy();  
           LOGGER.info("Destroyed !");  
      }  
      @Override  
      public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain) throws IOException, ServletException {  
           if (filterWrapperCondition.allowInnerFilterCall(req, res)) {  
                innerFilter.doFilter(req, res, chain);  
           } else {  
                chain.doFilter(req, res);  
           }  
      }  
 }  


ExclusionFilterWrapperCondition 
 package es.pruebas.jee6.filter.wrapper;  
 import javax.servlet.FilterConfig;  
 import javax.servlet.ServletRequest;  
 import javax.servlet.ServletResponse;  
 import javax.servlet.http.HttpServletRequest;  
 /**  
  * Example basic implementation.  
  *   
  * @author SISIFO  
  *  
  */  
 public class ExclusionFilterWrapperCondition implements FilterWrapperCondition {  
      private static final String PARAM_EXCLUSION_PREFIX = "wrapper.exclusionFilterWrapperCondition.param";  
      private String[] exclusionsRules;  
      @Override  
      public void init(FilterConfig filterConfig) {            
           exclusionsRules = filterConfig.getInitParameter(PARAM_EXCLUSION_PREFIX).split(",");  
           for (int i=0; i<exclusionsRules.length; i++) {  
                exclusionsRules[i] = filterConfig.getServletContext().getContextPath() + exclusionsRules[i].trim();                                
           }  
      }  
      @Override  
      public boolean allowInnerFilterCall(ServletRequest req, ServletResponse res) {  
           HttpServletRequest httpReq = (HttpServletRequest) req;  
           for (String pattern : exclusionsRules) {  
                if (httpReq.getRequestURI().startsWith(pattern)) {  
                     return false;  
                }  
           }  
           return true;  
      }  
 }  

web.xml
 <?xml version="1.0" encoding="UTF-8"?>  
 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
      xmlns="http://java.sun.com/xml/ns/javaee"  
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"  
      version="3.0">  
      <display-name>wrapperFilter</display-name>  
      <filter>  
           <display-name>wrapperFilter</display-name>  
           <filter-name>wrapperFilter</filter-name>  
           <filter-class>es.pruebas.jee6.filter.wrapper.WrapperFilter</filter-class>  
           <init-param>  
                <param-name>wrapper.param.filterClass</param-name>  
                <param-value>es.pruebas.jee6.filter.SayHelloFilter</param-value>  
           </init-param>  
           <init-param>  
                <param-name>wrapper.param.wrapperConditionClass</param-name>  
                <param-value>es.pruebas.jee6.filter.wrapper.ExclusionFilterWrapperCondition</param-value>  
           </init-param>  
           <init-param>  
                <param-name>wrapper.exclusionFilterWrapperCondition.param</param-name>  
                <param-value>  
                     /dontCallFilter/,  
                     /neitherInThisURL/   
                </param-value>  
           </init-param>  
           <!-- Any other params for the inner filter -->  
           <!-- .... -->            
      </filter>  
      <filter-mapping>  
           <filter-name>wrapperFilter</filter-name>  
           <url-pattern>/*</url-pattern>  
      </filter-mapping>  
      <filter>  
           <display-name>SayWorldFilter</display-name>  
           <filter-name>SayWorldFilter</filter-name>  
           <filter-class>es.pruebas.jee6.filter.SayWorldFilter</filter-class>  
      </filter>  
      <filter-mapping>  
           <filter-name>SayWorldFilter</filter-name>  
           <url-pattern>/*</url-pattern>  
      </filter-mapping>  
 </web-app>  



No hay comentarios:

Publicar un comentario