Procházet zdrojové kódy

Added filter chain

Carlos Sanchez před 20 roky
rodič
revize
7c9fad0477

+ 248 - 0
core/src/main/java/org/acegisecurity/util/FilterChainProxy.java

@@ -0,0 +1,248 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.util;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import net.sf.acegisecurity.ConfigAttribute;
+import net.sf.acegisecurity.ConfigAttributeDefinition;
+import net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionSource;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.BeanFactoryUtils;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.context.ApplicationContext;
+import org.springframework.web.context.support.WebApplicationContextUtils;
+
+/**
+ * Delegates <code>Filter</code> requests to a Spring-managed bean.
+ * <p>
+ * This class acts as a proxy on behalf of a target <code>Filter</code> that
+ * is defined in the Spring bean context. It is necessary to specify which
+ * target <code>Filter</code> should be proxied as a filter initialization
+ * parameter.
+ * </p>
+ * <p>
+ * On filter initialisation, the class will use Spring's {@link
+ * WebApplicationContextUtils#getWebApplicationContext(ServletContext sc)}
+ * method to obtain an <code>ApplicationContext</code> instance. It will
+ * expect to find the target <code>Filter</code> in this
+ * <code>ApplicationContext</code>.
+ * </p>
+ * <p>
+ * To use this filter, it is necessary to specify <b>one </b> of the following
+ * filter initialization parameters:
+ * </p>
+ * <ul>
+ * <li><code>targetClass</code> indicates the class of the target
+ * <code>Filter</code> defined in the bean context. The only requirements are
+ * that this target class implements the <code>javax.servlet.Filter</code>
+ * interface and at least one instance is available in the
+ * <code>ApplicationContext</code>.</li>
+ * <li><code>targetBean</code> indicates the bean name of the target class.
+ * </li>
+ * </ul>
+ * If both initialization parameters are specified, <code>targetBean</code>
+ * takes priority.
+ * <P>
+ * An additional initialization parameter, <code>init</code>, is also
+ * supported. If set to "<code>lazy</code>" the initialization will take
+ * place on the first HTTP request, rather than at filter creation time. This
+ * makes it possible to use <code>FilterToBeanProxy</code> with the Spring
+ * <code>ContextLoaderServlet</code>. Where possible you should not use this
+ * initialization parameter, instead using <code>ContextLoaderListener</code>.
+ * </p>
+ * 
+// * <pre>
+// * &lt;bean id=&quot;filterChain&quot; class=&quot;net.sf.acegisecurity.FilterChain&quot;&gt;
+// *   &lt;property name=&quot;filters&quot;&gt;
+// *   &lt;value&gt;
+// *     channelProcessingFilter=/*
+// *     authenticationProcessingFilter=/*
+// *     basicProcessingFilter=/*
+// *     sessionIntegrationFilter=/*
+// *     securityEnforcementFilter=/*
+// *   &lt;/value&gt;
+// *   &lt;/property&gt;
+// * &lt;/bean&gt;
+// * </pre>
+ * 
+ * @author Carlos Sanchez
+ * @version $Id$
+ */
+public class FilterChainProxy
+    implements Filter, InitializingBean
+{
+    //~ Static fields/initializers =============================================
+
+    private static final Log logger = LogFactory.getLog(FilterChainProxy.class);
+
+    //~ Instance fields
+    // ========================================================
+
+    private Filter delegate;
+
+    private List filters;
+
+    private FilterConfig filterConfig;
+
+    private boolean initialized = false;
+
+    private FilterInvocationDefinitionSource filterInvocationDefinitionSource;
+
+    //~ Methods
+    // ================================================================
+
+    public void setFilterInvocationDefinitionSource(
+        FilterInvocationDefinitionSource filterInvocationDefinitionSource) {
+        this.filterInvocationDefinitionSource = filterInvocationDefinitionSource;
+    }
+
+    public FilterInvocationDefinitionSource getFilterInvocationDefinitionSource() {
+        return filterInvocationDefinitionSource;
+    }
+
+    public void destroy()
+    {
+        Iterator it = filters.iterator();
+        while ( it.hasNext() )
+        {
+            Filter filter = (Filter) it.next();
+            if ( filter != null )
+            {
+                filter.destroy();
+            }
+        }
+    }
+
+    public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException,
+        ServletException
+    {
+        if ( !initialized )
+        {
+            doInit();
+        }
+
+        Iterator it = filters.iterator();
+        while ( it.hasNext() )
+        {
+            Filter filter = (Filter) it.next();
+            filter.doFilter( request, response, chain );
+        }
+    }
+
+    public void init( FilterConfig filterConfig ) throws ServletException
+    {
+        this.filterConfig = filterConfig;
+
+        String strategy = filterConfig.getInitParameter( "init" );
+
+        if ( (strategy != null) && strategy.toLowerCase().equals( "lazy" ) )
+        {
+            return;
+        }
+
+        doInit();
+    }
+
+    /**
+     * Allows test cases to override where application context obtained from.
+     * 
+     * @param filterConfig
+     *            which can be used to find the <code>ServletContext</code>
+     * @return the Spring application context
+     */
+    protected ApplicationContext getContext( FilterConfig filterConfig )
+    {
+        return WebApplicationContextUtils.getRequiredWebApplicationContext( filterConfig.getServletContext() );
+    }
+
+    private void doInit() throws ServletException
+    {
+        initialized = true;
+        
+        Iterator it = filters.iterator();
+        while ( it.hasNext() )
+        {
+            Filter filter = (Filter) it.next();
+            filter.init( filterConfig );
+        }
+
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        if (filterInvocationDefinitionSource == null) {
+            throw new IllegalArgumentException(
+                "filterInvocationDefinitionSource must be specified");
+        }
+
+        Iterator iter = this.filterInvocationDefinitionSource
+            .getConfigAttributeDefinitions();
+
+        if (iter == null) {
+            if (logger.isWarnEnabled()) {
+                logger.warn(
+                    "Could not validate configuration attributes as the FilterInvocationDefinitionSource did not return a ConfigAttributeDefinition Iterator");
+            }
+
+            return;
+        }
+
+        Set set = new HashSet();
+
+        while (iter.hasNext()) {
+            ConfigAttributeDefinition def = (ConfigAttributeDefinition) iter
+                .next();
+            Iterator attributes = def.getConfigAttributes();
+
+            while (attributes.hasNext()) {
+                ConfigAttribute attr = (ConfigAttribute) attributes.next();
+            }
+        }
+
+        if (set.size() == 0) {
+            if (logger.isInfoEnabled()) {
+                logger.info("Validated configuration attributes");
+            }
+        } else {
+            throw new IllegalArgumentException(
+                "Unsupported configuration attributes: " + set.toString());
+        }
+        
+        iter = filterInvocationDefinitionSource.getConfigAttributeDefinitions();
+        while ( iter.hasNext() )
+        {
+            ConfigAttributeDefinition element = (ConfigAttributeDefinition) iter.next();
+            Iterator configAttributes = element.getConfigAttributes();
+        }
+    }
+
+}

+ 261 - 0
core/src/test/java/org/acegisecurity/util/FilterChainProxyTests.java

@@ -0,0 +1,261 @@
+/* Copyright 2004 Acegi Technology Pty Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.acegisecurity.util;
+
+import junit.framework.TestCase;
+
+import net.sf.acegisecurity.MockFilterConfig;
+import net.sf.acegisecurity.MockHttpServletRequest;
+import net.sf.acegisecurity.MockHttpServletResponse;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+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;
+
+/**
+ * Tests {@link FilterChainProxy}.
+ * 
+ * @author Carlos Sanchez
+ * @version $Id$
+ */
+public class FilterChainProxyTests
+    extends TestCase
+{
+    //~ Constructors
+    // ===========================================================
+
+    public FilterChainProxyTests()
+    {
+        super();
+    }
+
+    public FilterChainProxyTests( String arg0 )
+    {
+        super( arg0 );
+    }
+
+    //~ Methods
+    // ================================================================
+
+    public final void setUp() throws Exception
+    {
+        super.setUp();
+//        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
+//            "net/sf/acegisecurity/util/filtertest-valid.xml" );
+//        FilterChainProxy filterChainProxy = (FilterChainProxy)applicationContext.getBean("filterChain");
+//        System.out.println(filterChainProxy);
+    }
+
+    public static void main( String[] args )
+    {
+        junit.textui.TestRunner.run( FilterChainProxyTests.class );
+    }
+
+    public void testDetectsTargetBeanIsNotAFilter() throws Exception
+    {
+        // Setup our filter
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter( "targetClass", "net.sf.acegisecurity.util.MockNotAFilter" );
+
+        FilterToBeanProxy filter = new MockFilterToBeanProxy( "net/sf/acegisecurity/util/filtertest-valid.xml" );
+
+        try
+        {
+            filter.init( config );
+            fail( "Should have thrown ServletException" );
+        }
+        catch ( ServletException expected )
+        {
+            assertEquals( "Bean 'mockNotAFilter' does not implement javax.servlet.Filter", expected.getMessage() );
+        }
+    }
+
+    public void testDetectsTargetBeanNotInBeanContext() throws Exception
+    {
+        // Setup our filter
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter( "targetBean", "WRONG_NAME" );
+
+        FilterToBeanProxy filter = new MockFilterToBeanProxy( "net/sf/acegisecurity/util/filtertest-valid.xml" );
+
+        try
+        {
+            filter.init( config );
+            fail( "Should have thrown ServletException" );
+        }
+        catch ( ServletException expected )
+        {
+            assertEquals( "targetBean 'WRONG_NAME' not found in context", expected.getMessage() );
+        }
+    }
+
+    public void testIgnoresEmptyTargetBean() throws Exception
+    {
+        // Setup our filter
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter( "targetClass", "net.sf.acegisecurity.util.FilterChainProxy" );
+        config.setInitParmeter( "targetBean", "" );
+
+        // Setup our expectation that the filter chain will be invoked
+        MockFilterChain chain = new MockFilterChain( true );
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockHttpServletRequest request = new MockHttpServletRequest( "/go" );
+
+        FilterToBeanProxy filter = new MockFilterToBeanProxy( "net/sf/acegisecurity/util/filtertest-valid.xml" );
+
+        executeFilterInContainerSimulator( config, filter, request, response, chain );
+    }
+
+    public void testNormalOperationWithLazyTrue() throws Exception
+    {
+        // Setup our filter
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter( "targetBean", "filterChain" );
+        config.setInitParmeter( "init", "lazy" );
+
+        // Setup our expectation that the filter chain will be invoked
+        MockFilterChain chain = new MockFilterChain( true );
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockHttpServletRequest request = new MockHttpServletRequest( "/go" );
+
+        FilterToBeanProxy filter = new MockFilterToBeanProxy( "net/sf/acegisecurity/util/filtertest-valid.xml" );
+
+        executeFilterInContainerSimulator( config, filter, request, response, chain );
+    }
+
+    public void testNormalOperationWithSpecificBeanName() throws Exception
+    {
+        // Setup our filter
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter( "targetBean", "filterChain" );
+
+        // Setup our expectation that the filter chain will be invoked
+        MockFilterChain chain = new MockFilterChain( true );
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockHttpServletRequest request = new MockHttpServletRequest( "/go" );
+
+        FilterToBeanProxy filter = new MockFilterToBeanProxy( "net/sf/acegisecurity/util/filtertest-valid.xml" );
+
+        executeFilterInContainerSimulator( config, filter, request, response, chain );
+    }
+
+    public void testNormalOperationWithTargetClass() throws Exception
+    {
+        // Setup our filter
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter( "targetClass", "net.sf.acegisecurity.util.FilterChainProxy" );
+
+        // Setup our expectation that the filter chain will be invoked
+        MockFilterChain chain = new MockFilterChain( true );
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockHttpServletRequest request = new MockHttpServletRequest( "/go" );
+
+        FilterToBeanProxy filter = new MockFilterToBeanProxy( "net/sf/acegisecurity/util/filtertest-valid.xml" );
+
+        executeFilterInContainerSimulator( config, filter, request, response, chain );
+    }
+
+    public void testNullDelegateDoesNotCauseNullPointerException() throws Exception
+    {
+        // Setup our filter
+        MockFilterConfig config = new MockFilterConfig();
+        config.setInitParmeter( "targetBean", "aFilterThatDoesntExist" );
+        config.setInitParmeter( "init", "lazy" );
+
+        // Setup our expectation that the filter chain will be invoked
+        MockFilterChain chain = new MockFilterChain( true );
+
+        MockHttpServletResponse response = new MockHttpServletResponse();
+        MockHttpServletRequest request = new MockHttpServletRequest( "/go" );
+
+        FilterToBeanProxy filter = new MockFilterToBeanProxy( "net/sf/acegisecurity/util/filtertest-valid.xml" );
+
+        // do not init (which would hapen if called .doFilter)
+        filter.destroy();
+    }
+
+    private void executeFilterInContainerSimulator( FilterConfig filterConfig, Filter filter, ServletRequest request,
+        ServletResponse response, FilterChain filterChain ) throws ServletException, IOException
+    {
+        filter.init( filterConfig );
+        filter.doFilter( request, response, filterChain );
+        filter.destroy();
+    }
+
+    //~ Inner Classes
+    // ==========================================================
+
+    private class MockFilterChain
+        implements FilterChain
+    {
+        private boolean expectToProceed;
+
+        public MockFilterChain( boolean expectToProceed )
+        {
+            this.expectToProceed = expectToProceed;
+        }
+
+        private MockFilterChain()
+        {
+            super();
+        }
+
+        public void doFilter( ServletRequest request, ServletResponse response ) throws IOException, ServletException
+        {
+            if ( expectToProceed )
+            {
+                assertTrue( true );
+            }
+            else
+            {
+                fail( "Did not expect filter chain to proceed" );
+            }
+        }
+    }
+
+    private class MockFilterToBeanProxy
+        extends FilterToBeanProxy
+    {
+        private String appContextLocation;
+
+        public MockFilterToBeanProxy( String appContextLocation )
+        {
+            this.appContextLocation = appContextLocation;
+        }
+
+        private MockFilterToBeanProxy()
+        {
+            super();
+        }
+
+        protected ApplicationContext getContext( FilterConfig filterConfig )
+        {
+            return new ClassPathXmlApplicationContext( appContextLocation );
+        }
+    }
+}

+ 16 - 0
core/src/test/resources/org/acegisecurity/util/filtertest-valid.xml

@@ -24,4 +24,20 @@
 	<bean id="mockFilter" class="net.sf.acegisecurity.util.MockFilter"/>
 
 	<bean id="mockNotAFilter" class="net.sf.acegisecurity.util.MockNotAFilter"/>
+
+	<bean id="filterChain" class="net.sf.acegisecurity.util.FilterChainProxy">
+      <property name="filterInvocationDefinitionSource">
+         <value>
+                /*=mockFilter
+         </value>
+      </property>
+<!--
+        <property name="filters">
+            <value>
+                /*=mockFilter
+            </value>
+        </property>
+-->
+    </bean>
+
 </beans>