mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-26 22:02:41 +00:00
Rewrite FilterChainProxy to separate functionality from FilterToBeanProxy and properly implement filter chaining issues.
This commit is contained in:
parent
57842d4ba8
commit
a5ea6f5436
@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2004 Acegi Technology Pty Limited
|
/* Copyright 2004, 2005 Acegi Technology Pty Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -15,111 +15,113 @@
|
|||||||
|
|
||||||
package net.sf.acegisecurity.util;
|
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.ConfigAttribute;
|
||||||
import net.sf.acegisecurity.ConfigAttributeDefinition;
|
import net.sf.acegisecurity.ConfigAttributeDefinition;
|
||||||
|
import net.sf.acegisecurity.intercept.web.FilterInvocation;
|
||||||
import net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionSource;
|
import net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionSource;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.web.context.support.WebApplicationContextUtils;
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import javax.servlet.Filter;
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.FilterConfig;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletRequest;
|
||||||
|
import javax.servlet.ServletResponse;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegates <code>Filter</code> requests to a Spring-managed bean.
|
* Delegates <code>Filter</code> requests to a list of Spring-managed beans.
|
||||||
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* This class acts as a proxy on behalf of a target <code>Filter</code> that
|
* The <code>FilterChainProxy</code> is loaded via a standard {@link
|
||||||
* is defined in the Spring bean context. It is necessary to specify which
|
* net.sf.acegisecurity.util.FilterToBeanProxy} declaration in
|
||||||
* target <code>Filter</code> should be proxied as a filter initialization
|
* <code>web.xml</code>. <code>FilterChainProxy</code> will then pass {@link
|
||||||
* parameter.
|
* #init(FilterConfig)}, {@link #destroy()}, {@link #doInit()} and {@link
|
||||||
* </p>
|
* #doFilter(ServletRequest, ServletResponse, FilterChain)} invocations
|
||||||
* <p>
|
* through to each <code>Filter</code> defined against
|
||||||
* On filter initialisation, the class will use Spring's {@link
|
* <code>FilterChainProxy</code>.
|
||||||
* 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>
|
* </p>
|
||||||
*
|
*
|
||||||
// * <pre>
|
* <p>
|
||||||
// * <bean id="filterChain" class="net.sf.acegisecurity.FilterChain">
|
* <code>FilterChainProxy</code> is configured using a standard {@link
|
||||||
// * <property name="filters">
|
* net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionSource}. Each
|
||||||
// * <value>
|
* possible URI pattern that <code>FilterChainProxy</code> should service must
|
||||||
// * channelProcessingFilter=/*
|
* be entered. The first matching URI pattern located by
|
||||||
// * authenticationProcessingFilter=/*
|
* <code>FilterInvocationDefinitionSource</code> for a given request will be
|
||||||
// * basicProcessingFilter=/*
|
* used to define all of the <code>Filter</code>s that apply to that request.
|
||||||
// * sessionIntegrationFilter=/*
|
* NB: This means you must put most specific URI patterns at the top of the
|
||||||
// * securityEnforcementFilter=/*
|
* list, and ensure all <code>Filter</code>s that should apply for a given URI
|
||||||
// * </value>
|
* pattern are entered against the respective entry. The
|
||||||
// * </property>
|
* <code>FilterChainProxy</code> will not iterate the remainder of the URI
|
||||||
// * </bean>
|
* patterns to locate additional <code>Filter</code>s. The
|
||||||
// * </pre>
|
* <code>FilterInvocationDefinitionSource</code> described the applicable URI
|
||||||
|
* pattern to fire the filter chain, followed by a list of configuration
|
||||||
|
* attributes. Each configuration attribute's {@link
|
||||||
|
* net.sf.acegisecurity.ConfigAttribute#getAttribute()} corresponds to a bean
|
||||||
|
* name that is available from the application context.
|
||||||
|
* </p>
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
|
* <code>FilterChainProxy</code> respects normal handling of
|
||||||
|
* <code>Filter</code>s that elect not to call {@link
|
||||||
|
* javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
|
||||||
|
* javax.servlet.ServletResponse, javax.servlet.FilterChain)}, in that the
|
||||||
|
* remainder of the origial or <code>FilterChainProxy</code>-declared filter
|
||||||
|
* chain will not be called.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* It is particularly noted the <code>Filter</code> lifecycle mismatch between
|
||||||
|
* the servlet container and IoC container. As per {@link
|
||||||
|
* net.sf.acegisecurity.util.FilterToBeanProxy} JavaDocs, we recommend you
|
||||||
|
* allow the IoC container to manage lifecycle instead of the servlet
|
||||||
|
* container. By default the <code>FilterToBeanProxy</code> will never call
|
||||||
|
* this class' {@link #init(FilterConfig)} and {@link #destroy()} methods,
|
||||||
|
* meaning each of the filters defined against
|
||||||
|
* <code>FilterInvocationDefinitionSource</code> will not be called. If you do
|
||||||
|
* need your filters to be initialized and destroyed, please set the
|
||||||
|
* <code>lifecycle</code> initialization parameter against the
|
||||||
|
* <code>FilterToBeanProxy</code> to specify servlet container lifecycle
|
||||||
|
* management.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
* @author Carlos Sanchez
|
* @author Carlos Sanchez
|
||||||
|
* @author Ben Alex
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public class FilterChainProxy
|
public class FilterChainProxy implements Filter, InitializingBean,
|
||||||
implements Filter, InitializingBean
|
ApplicationContextAware {
|
||||||
{
|
|
||||||
//~ Static fields/initializers =============================================
|
//~ Static fields/initializers =============================================
|
||||||
|
|
||||||
private static final Log logger = LogFactory.getLog(FilterChainProxy.class);
|
private static final Log logger = LogFactory.getLog(FilterChainProxy.class);
|
||||||
|
|
||||||
//~ Instance fields
|
//~ Instance fields ========================================================
|
||||||
// ========================================================
|
|
||||||
|
|
||||||
private Filter delegate;
|
|
||||||
|
|
||||||
private List filters;
|
|
||||||
|
|
||||||
private FilterConfig filterConfig;
|
|
||||||
|
|
||||||
private boolean initialized = false;
|
|
||||||
|
|
||||||
|
private ApplicationContext applicationContext;
|
||||||
private FilterInvocationDefinitionSource filterInvocationDefinitionSource;
|
private FilterInvocationDefinitionSource filterInvocationDefinitionSource;
|
||||||
|
|
||||||
//~ Methods
|
//~ Methods ================================================================
|
||||||
// ================================================================
|
|
||||||
|
public void setApplicationContext(ApplicationContext applicationContext)
|
||||||
|
throws BeansException {
|
||||||
|
this.applicationContext = applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
public void setFilterInvocationDefinitionSource(
|
public void setFilterInvocationDefinitionSource(
|
||||||
FilterInvocationDefinitionSource filterInvocationDefinitionSource) {
|
FilterInvocationDefinitionSource filterInvocationDefinitionSource) {
|
||||||
@ -130,119 +132,185 @@ public class FilterChainProxy
|
|||||||
return filterInvocationDefinitionSource;
|
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 {
|
public void afterPropertiesSet() throws Exception {
|
||||||
if (filterInvocationDefinitionSource == null) {
|
if (filterInvocationDefinitionSource == null) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"filterInvocationDefinitionSource must be specified");
|
"filterInvocationDefinitionSource must be specified");
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator iter = this.filterInvocationDefinitionSource
|
if (this.filterInvocationDefinitionSource.getConfigAttributeDefinitions() == null) {
|
||||||
.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(
|
throw new IllegalArgumentException(
|
||||||
"Unsupported configuration attributes: " + set.toString());
|
"FilterChainProxy requires the FitlerInvocationDefinitionSource to return a non-null response to getConfigAttributeDefinitions()");
|
||||||
}
|
|
||||||
|
|
||||||
iter = filterInvocationDefinitionSource.getConfigAttributeDefinitions();
|
|
||||||
while ( iter.hasNext() )
|
|
||||||
{
|
|
||||||
ConfigAttributeDefinition element = (ConfigAttributeDefinition) iter.next();
|
|
||||||
Iterator configAttributes = element.getConfigAttributes();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
public void destroy() {
|
||||||
|
Filter[] filters = obtainAllDefinedFilters();
|
||||||
|
|
||||||
|
for (int i = 0; i < filters.length; i++) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug(
|
||||||
|
"Destroying Filter defined in ApplicationContext: '"
|
||||||
|
+ filters[i].toString() + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
filters[i].destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doFilter(ServletRequest request, ServletResponse response,
|
||||||
|
FilterChain chain) throws IOException, ServletException {
|
||||||
|
FilterInvocation fi = new FilterInvocation(request, response, chain);
|
||||||
|
|
||||||
|
ConfigAttributeDefinition cad = this.filterInvocationDefinitionSource
|
||||||
|
.getAttributes(fi);
|
||||||
|
|
||||||
|
if (cad == null) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug(fi.getRequestUrl() + " has no matching filters");
|
||||||
|
}
|
||||||
|
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
} else {
|
||||||
|
Filter[] filters = obtainAllDefinedFilters(cad);
|
||||||
|
|
||||||
|
VirtualFilterChain virtualFilterChain = new VirtualFilterChain(fi,
|
||||||
|
filters);
|
||||||
|
virtualFilterChain.doFilter(fi.getRequest(), fi.getResponse());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(FilterConfig filterConfig) throws ServletException {
|
||||||
|
Filter[] filters = obtainAllDefinedFilters();
|
||||||
|
|
||||||
|
for (int i = 0; i < filters.length; i++) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug(
|
||||||
|
"Initializing Filter defined in ApplicationContext: '"
|
||||||
|
+ filters[i].toString() + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
filters[i].init(filterConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all of the <b>unique</b><code>Filter</code> instances registered
|
||||||
|
* against the <code>FilterInvocationDefinitionSource</code>.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This is useful in ensuring a <code>Filter</code> is not initialized or
|
||||||
|
* destroyed twice.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return all of the <code>Filter</code> instances in the application
|
||||||
|
* context for which there has been an entry against the
|
||||||
|
* <code>FilterInvocationDefinitionSource</code> (only one entry
|
||||||
|
* is included in the array for each <code>Filter</code> that
|
||||||
|
* actually exists in application context, even if a given
|
||||||
|
* <code>Filter</code> is defined multiples times by the
|
||||||
|
* <code>FilterInvocationDefinitionSource</code>)
|
||||||
|
*/
|
||||||
|
private Filter[] obtainAllDefinedFilters() {
|
||||||
|
Iterator cads = this.filterInvocationDefinitionSource
|
||||||
|
.getConfigAttributeDefinitions();
|
||||||
|
Set list = new LinkedHashSet();
|
||||||
|
|
||||||
|
while (cads.hasNext()) {
|
||||||
|
ConfigAttributeDefinition attribDef = (ConfigAttributeDefinition) cads
|
||||||
|
.next();
|
||||||
|
Filter[] filters = obtainAllDefinedFilters(attribDef);
|
||||||
|
|
||||||
|
for (int i = 0; i < filters.length; i++) {
|
||||||
|
list.add(filters[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Filter[]) list.toArray(new Filter[] {null});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all of the <code>Filter</code> instances registered against the
|
||||||
|
* specified <code>ConfigAttributeDefinition</code>.
|
||||||
|
*
|
||||||
|
* @param configAttributeDefinition for which we want to obtain associated
|
||||||
|
* <code>Filter</code>s
|
||||||
|
*
|
||||||
|
* @return the <code>Filter</code>s against the specified
|
||||||
|
* <code>ConfigAttributeDefinition</code>
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if a configuration attribute provides a
|
||||||
|
* <code>null</code> return value from the {@link
|
||||||
|
* ConfigAttribute#getAttribute()} method
|
||||||
|
*/
|
||||||
|
private Filter[] obtainAllDefinedFilters(
|
||||||
|
ConfigAttributeDefinition configAttributeDefinition) {
|
||||||
|
List list = new Vector();
|
||||||
|
Iterator attributes = configAttributeDefinition.getConfigAttributes();
|
||||||
|
|
||||||
|
while (attributes.hasNext()) {
|
||||||
|
ConfigAttribute attr = (ConfigAttribute) attributes.next();
|
||||||
|
String filterName = attr.getAttribute();
|
||||||
|
|
||||||
|
if (filterName == null) {
|
||||||
|
throw new IllegalArgumentException("Configuration attribute: '"
|
||||||
|
+ attr
|
||||||
|
+ "' returned null to the getAttribute() method, which is invalid when used with FilterChainProxy");
|
||||||
|
}
|
||||||
|
|
||||||
|
list.add(this.applicationContext.getBean(filterName, Filter.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Filter[]) list.toArray(new Filter[] {null});
|
||||||
|
}
|
||||||
|
|
||||||
|
//~ Inner Classes ==========================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A <code>FilterChain</code> that records whether or not {@link
|
||||||
|
* FilterChain#doFilter(javax.servlet.ServletRequest,
|
||||||
|
* javax.servlet.ServletResponse)} is called.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This <code>FilterChain</code> is used by <code>FilterChainProxy</code>
|
||||||
|
* to determine if the next <code>Filter</code> should be called or not.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
private class VirtualFilterChain implements FilterChain {
|
||||||
|
private FilterInvocation fi;
|
||||||
|
private Filter[] additionalFilters;
|
||||||
|
private int currentPosition = 0;
|
||||||
|
|
||||||
|
public VirtualFilterChain(FilterInvocation filterInvocation,
|
||||||
|
Filter[] additionalFilters) {
|
||||||
|
this.fi = filterInvocation;
|
||||||
|
this.additionalFilters = additionalFilters;
|
||||||
|
}
|
||||||
|
|
||||||
|
private VirtualFilterChain() {}
|
||||||
|
|
||||||
|
public void doFilter(ServletRequest arg0, ServletResponse arg1)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
if (currentPosition == additionalFilters.length) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug(fi.getRequestUrl()
|
||||||
|
+ " reached end of additional filter chain; proceeding with original chain");
|
||||||
|
}
|
||||||
|
|
||||||
|
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
|
||||||
|
} else {
|
||||||
|
currentPosition++;
|
||||||
|
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug(fi.getRequestUrl() + " at position "
|
||||||
|
+ currentPosition + " of " + additionalFilters.length
|
||||||
|
+ " in additional filter chain; firing Filter: '"
|
||||||
|
+ additionalFilters[currentPosition - 1] + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
additionalFilters[currentPosition - 1].doFilter(fi.getRequest(),
|
||||||
|
fi.getResponse(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2004 Acegi Technology Pty Limited
|
/* Copyright 2004, 2005 Acegi Technology Pty Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -17,9 +17,15 @@ package net.sf.acegisecurity.util;
|
|||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import net.sf.acegisecurity.ConfigAttribute;
|
||||||
|
import net.sf.acegisecurity.ConfigAttributeDefinition;
|
||||||
|
import net.sf.acegisecurity.MockApplicationContext;
|
||||||
import net.sf.acegisecurity.MockFilterConfig;
|
import net.sf.acegisecurity.MockFilterConfig;
|
||||||
import net.sf.acegisecurity.MockHttpServletRequest;
|
import net.sf.acegisecurity.MockHttpServletRequest;
|
||||||
import net.sf.acegisecurity.MockHttpServletResponse;
|
import net.sf.acegisecurity.MockHttpServletResponse;
|
||||||
|
import net.sf.acegisecurity.intercept.web.FilterInvocationDefinitionSource;
|
||||||
|
import net.sf.acegisecurity.intercept.web.MockFilterInvocationDefinitionSource;
|
||||||
|
import net.sf.acegisecurity.intercept.web.PathBasedFilterInvocationDefinitionMap;
|
||||||
|
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||||
@ -33,229 +39,138 @@ import javax.servlet.ServletException;
|
|||||||
import javax.servlet.ServletRequest;
|
import javax.servlet.ServletRequest;
|
||||||
import javax.servlet.ServletResponse;
|
import javax.servlet.ServletResponse;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link FilterChainProxy}.
|
* Tests {@link FilterChainProxy}.
|
||||||
*
|
*
|
||||||
* @author Carlos Sanchez
|
* @author Carlos Sanchez
|
||||||
|
* @author Ben Alex
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public class FilterChainProxyTests
|
public class FilterChainProxyTests extends TestCase {
|
||||||
extends TestCase
|
//~ Constructors ===========================================================
|
||||||
{
|
|
||||||
//~ Constructors
|
|
||||||
// ===========================================================
|
|
||||||
|
|
||||||
public FilterChainProxyTests()
|
// ===========================================================
|
||||||
{
|
public FilterChainProxyTests() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FilterChainProxyTests( String arg0 )
|
public FilterChainProxyTests(String arg0) {
|
||||||
{
|
super(arg0);
|
||||||
super( arg0 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//~ Methods
|
//~ Methods ================================================================
|
||||||
|
|
||||||
// ================================================================
|
// ================================================================
|
||||||
|
public static void main(String[] args) {
|
||||||
public final void setUp() throws Exception
|
junit.textui.TestRunner.run(FilterChainProxyTests.class);
|
||||||
{
|
|
||||||
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 )
|
public void testDetectsFilterInvocationDefinitionSourceThatDoesNotReturnAllConfigAttributes()
|
||||||
{
|
throws Exception {
|
||||||
junit.textui.TestRunner.run( FilterChainProxyTests.class );
|
FilterChainProxy filterChainProxy = new FilterChainProxy();
|
||||||
}
|
filterChainProxy.setApplicationContext(MockApplicationContext
|
||||||
|
.getContext());
|
||||||
|
filterChainProxy.setFilterInvocationDefinitionSource(new MockFilterInvocationDefinitionSource(
|
||||||
|
false, false));
|
||||||
|
|
||||||
public void testDetectsTargetBeanIsNotAFilter() throws Exception
|
try {
|
||||||
{
|
filterChainProxy.afterPropertiesSet();
|
||||||
// Setup our filter
|
fail("Should have thrown IllegalArgumentException");
|
||||||
MockFilterConfig config = new MockFilterConfig();
|
} catch (IllegalArgumentException expected) {
|
||||||
config.setInitParmeter( "targetClass", "net.sf.acegisecurity.util.MockNotAFilter" );
|
assertEquals("FilterChainProxy requires the FitlerInvocationDefinitionSource to return a non-null response to getConfigAttributeDefinitions()",
|
||||||
|
expected.getMessage());
|
||||||
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
|
public void testDetectsIfConfigAttributeDoesNotReturnValueForGetAttributeMethod()
|
||||||
{
|
throws Exception {
|
||||||
// Setup our filter
|
FilterChainProxy filterChainProxy = new FilterChainProxy();
|
||||||
MockFilterConfig config = new MockFilterConfig();
|
filterChainProxy.setApplicationContext(MockApplicationContext
|
||||||
config.setInitParmeter( "targetBean", "WRONG_NAME" );
|
.getContext());
|
||||||
|
|
||||||
FilterToBeanProxy filter = new MockFilterToBeanProxy( "net/sf/acegisecurity/util/filtertest-valid.xml" );
|
ConfigAttributeDefinition cad = new ConfigAttributeDefinition();
|
||||||
|
cad.addConfigAttribute(new MockConfigAttribute());
|
||||||
|
|
||||||
try
|
PathBasedFilterInvocationDefinitionMap fids = new PathBasedFilterInvocationDefinitionMap();
|
||||||
{
|
fids.addSecureUrl("/**", cad);
|
||||||
filter.init( config );
|
|
||||||
fail( "Should have thrown ServletException" );
|
filterChainProxy.setFilterInvocationDefinitionSource(fids);
|
||||||
}
|
filterChainProxy.afterPropertiesSet();
|
||||||
catch ( ServletException expected )
|
|
||||||
{
|
try {
|
||||||
assertEquals( "targetBean 'WRONG_NAME' not found in context", expected.getMessage() );
|
filterChainProxy.init(new MockFilterConfig());
|
||||||
|
fail("Should have thrown IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
assertTrue(expected.getMessage().endsWith("returned null to the getAttribute() method, which is invalid when used with FilterChainProxy"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testIgnoresEmptyTargetBean() throws Exception
|
public void testDetectsMissingFilterInvocationDefinitionSource()
|
||||||
{
|
throws Exception {
|
||||||
// Setup our filter
|
FilterChainProxy filterChainProxy = new FilterChainProxy();
|
||||||
MockFilterConfig config = new MockFilterConfig();
|
filterChainProxy.setApplicationContext(MockApplicationContext
|
||||||
config.setInitParmeter( "targetClass", "net.sf.acegisecurity.util.FilterChainProxy" );
|
.getContext());
|
||||||
config.setInitParmeter( "targetBean", "" );
|
|
||||||
|
|
||||||
// Setup our expectation that the filter chain will be invoked
|
try {
|
||||||
MockFilterChain chain = new MockFilterChain( true );
|
filterChainProxy.afterPropertiesSet();
|
||||||
|
fail("Should have thrown IllegalArgumentException");
|
||||||
|
} catch (IllegalArgumentException expected) {
|
||||||
|
assertEquals("filterInvocationDefinitionSource must be specified",
|
||||||
|
expected.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGettersSetters() {
|
||||||
|
FilterChainProxy filterChainProxy = new FilterChainProxy();
|
||||||
|
FilterInvocationDefinitionSource fids = new MockFilterInvocationDefinitionSource(false,
|
||||||
|
false);
|
||||||
|
filterChainProxy.setFilterInvocationDefinitionSource(fids);
|
||||||
|
assertEquals(fids,
|
||||||
|
filterChainProxy.getFilterInvocationDefinitionSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNormalOperation() throws Exception {
|
||||||
|
ApplicationContext appCtx = new ClassPathXmlApplicationContext(
|
||||||
|
"net/sf/acegisecurity/util/filtertest-valid.xml");
|
||||||
|
FilterChainProxy filterChainProxy = (FilterChainProxy) appCtx.getBean("filterChain",
|
||||||
|
FilterChainProxy.class);
|
||||||
|
MockFilter filter = (MockFilter) appCtx.getBean("mockFilter",
|
||||||
|
MockFilter.class);
|
||||||
|
assertFalse(filter.isWasInitialized());
|
||||||
|
assertFalse(filter.isWasDoFiltered());
|
||||||
|
assertFalse(filter.isWasDestroyed());
|
||||||
|
|
||||||
|
filterChainProxy.init(new MockFilterConfig());
|
||||||
|
assertTrue(filter.isWasInitialized());
|
||||||
|
assertFalse(filter.isWasDoFiltered());
|
||||||
|
assertFalse(filter.isWasDestroyed());
|
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest(null);
|
||||||
|
request.setServletPath("/foo/secure/super/somefile.html");
|
||||||
|
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest( "/go" );
|
MockFilterChain chain = new MockFilterChain(true);
|
||||||
|
|
||||||
FilterToBeanProxy filter = new MockFilterToBeanProxy( "net/sf/acegisecurity/util/filtertest-valid.xml" );
|
filterChainProxy.doFilter(request, response, chain);
|
||||||
|
assertTrue(filter.isWasInitialized());
|
||||||
|
assertTrue(filter.isWasDoFiltered());
|
||||||
|
assertFalse(filter.isWasDestroyed());
|
||||||
|
|
||||||
executeFilterInContainerSimulator( config, filter, request, response, chain );
|
request.setServletPath("/a/path/which/doesnt/match/any/filter.html");
|
||||||
|
filterChainProxy.doFilter(request, response, chain);
|
||||||
|
|
||||||
|
filterChainProxy.destroy();
|
||||||
|
assertTrue(filter.isWasInitialized());
|
||||||
|
assertTrue(filter.isWasDoFiltered());
|
||||||
|
assertTrue(filter.isWasDestroyed());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNormalOperationWithLazyTrue() throws Exception
|
//~ Inner Classes ==========================================================
|
||||||
{
|
|
||||||
// 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
|
private class MockConfigAttribute implements ConfigAttribute {
|
||||||
MockFilterChain chain = new MockFilterChain( true );
|
public String getAttribute() {
|
||||||
|
return null;
|
||||||
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 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Copyright 2004 Acegi Technology Pty Limited
|
/* Copyright 2004, 2005 Acegi Technology Pty Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -32,14 +32,37 @@ import javax.servlet.ServletResponse;
|
|||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public class MockFilter implements Filter {
|
public class MockFilter implements Filter {
|
||||||
|
//~ Instance fields ========================================================
|
||||||
|
|
||||||
|
private boolean wasDestroyed = false;
|
||||||
|
private boolean wasDoFiltered = false;
|
||||||
|
private boolean wasInitialized = false;
|
||||||
|
|
||||||
//~ Methods ================================================================
|
//~ Methods ================================================================
|
||||||
|
|
||||||
public void destroy() {}
|
public boolean isWasDestroyed() {
|
||||||
|
return wasDestroyed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWasDoFiltered() {
|
||||||
|
return wasDoFiltered;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWasInitialized() {
|
||||||
|
return wasInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
wasDestroyed = true;
|
||||||
|
}
|
||||||
|
|
||||||
public void doFilter(ServletRequest request, ServletResponse response,
|
public void doFilter(ServletRequest request, ServletResponse response,
|
||||||
FilterChain chain) throws IOException, ServletException {
|
FilterChain chain) throws IOException, ServletException {
|
||||||
|
wasDoFiltered = true;
|
||||||
chain.doFilter(request, response);
|
chain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init(FilterConfig config) throws ServletException {}
|
public void init(FilterConfig config) throws ServletException {
|
||||||
|
wasInitialized = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,21 +23,19 @@
|
|||||||
|
|
||||||
<bean id="mockFilter" class="net.sf.acegisecurity.util.MockFilter"/>
|
<bean id="mockFilter" class="net.sf.acegisecurity.util.MockFilter"/>
|
||||||
|
|
||||||
|
<bean id="mockFilter2" class="net.sf.acegisecurity.util.MockFilter"/>
|
||||||
|
|
||||||
<bean id="mockNotAFilter" class="net.sf.acegisecurity.util.MockNotAFilter"/>
|
<bean id="mockNotAFilter" class="net.sf.acegisecurity.util.MockNotAFilter"/>
|
||||||
|
|
||||||
<bean id="filterChain" class="net.sf.acegisecurity.util.FilterChainProxy">
|
<bean id="filterChain" class="net.sf.acegisecurity.util.FilterChainProxy">
|
||||||
<property name="filterInvocationDefinitionSource">
|
<property name="filterInvocationDefinitionSource">
|
||||||
<value>
|
<value>
|
||||||
/*=mockFilter
|
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
|
||||||
|
PATTERN_TYPE_APACHE_ANT
|
||||||
|
/foo/**=mockFilter
|
||||||
|
/some/other/path/**=mockFilter
|
||||||
</value>
|
</value>
|
||||||
</property>
|
</property>
|
||||||
<!--
|
|
||||||
<property name="filters">
|
|
||||||
<value>
|
|
||||||
/*=mockFilter
|
|
||||||
</value>
|
|
||||||
</property>
|
|
||||||
-->
|
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user