diff --git a/core/src/main/java/org/springframework/security/config/FilterChainMapBeanDefinitionDecorator.java b/core/src/main/java/org/springframework/security/config/FilterChainMapBeanDefinitionDecorator.java
index 3cc7113709..c2966dee10 100644
--- a/core/src/main/java/org/springframework/security/config/FilterChainMapBeanDefinitionDecorator.java
+++ b/core/src/main/java/org/springframework/security/config/FilterChainMapBeanDefinitionDecorator.java
@@ -1,8 +1,11 @@
package org.springframework.security.config;
import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
-import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
+import org.springframework.beans.factory.support.ManagedList;
+import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.context.ApplicationContext;
@@ -15,10 +18,7 @@ import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
-import javax.servlet.Filter;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
+import java.util.*;
/**
* Sets the FilterChainMap for a FilterChainProxy bean declaration.
@@ -29,82 +29,45 @@ import java.util.List;
public class FilterChainMapBeanDefinitionDecorator implements BeanDefinitionDecorator {
public static final String FILTER_CHAIN_ELT_NAME = "filter-chain";
- public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
- FilterChainMap filterChainMap = new FilterChainMap();
+ public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder holder, ParserContext parserContext) {
+ BeanDefinition filterChainProxy = holder.getBeanDefinition();
+
+ Map filterChainMap = new LinkedHashMap();
Element elt = (Element)node;
String pathType = elt.getAttribute(HttpSecurityBeanDefinitionParser.PATTERN_TYPE_ATTRIBUTE);
if (HttpSecurityBeanDefinitionParser.PATTERN_TYPE_REGEX.equals(pathType)) {
- filterChainMap.setUrlPathMatcher(new RegexUrlPathMatcher());
+ filterChainProxy.getPropertyValues().addPropertyValue("matcher", new RegexUrlPathMatcher());
}
- List paths = new ArrayList();
- List filterChains = new ArrayList();
-
Iterator filterChainElts = DomUtils.getChildElementsByTagName(elt, FILTER_CHAIN_ELT_NAME).iterator();
while (filterChainElts.hasNext()) {
Element chain = (Element) filterChainElts.next();
String path = chain.getAttribute(HttpSecurityBeanDefinitionParser.PATH_PATTERN_ATTRIBUTE);
- Assert.hasText(path, "The attribute '" + HttpSecurityBeanDefinitionParser.PATH_PATTERN_ATTRIBUTE + "' must not be empty");
+ Assert.hasText(path, "The attribute '" + HttpSecurityBeanDefinitionParser.PATH_PATTERN_ATTRIBUTE +
+ "' must not be empty");
String filters = chain.getAttribute(HttpSecurityBeanDefinitionParser.FILTERS_ATTRIBUTE);
Assert.hasText(filters, "The attribute '" + HttpSecurityBeanDefinitionParser.FILTERS_ATTRIBUTE +
"'must not be empty");
- paths.add(path);
- filterChains.add(filters);
- }
- // Set the FilterChainMap on the FilterChainProxy bean.
- definition.getBeanDefinition().getPropertyValues().addPropertyValue("filterChainMap", filterChainMap);
+ if (filters.equals(HttpSecurityBeanDefinitionParser.NO_FILTERS_VALUE)) {
+ filterChainMap.put(path, Collections.EMPTY_LIST);
+ } else {
+ String[] filterBeanNames = StringUtils.tokenizeToStringArray(filters, ",");
+ ManagedList filterChain = new ManagedList(filterBeanNames.length);
- // Register the ApplicationContextAware bean which will add the filter chains to the FilterChainMap
- RootBeanDefinition chainResolver = new RootBeanDefinition(FilterChainResolver.class);
- chainResolver.getConstructorArgumentValues().addIndexedArgumentValue(0, filterChainMap);
- chainResolver.getConstructorArgumentValues().addIndexedArgumentValue(1, paths);
- chainResolver.getConstructorArgumentValues().addIndexedArgumentValue(2, filterChains);
-
- parserContext.getRegistry().registerBeanDefinition(definition.getBeanName() + ".filterChainMapChainResolver",
- chainResolver);
-
- return definition;
- }
-
- /**
- * Bean which stores the filter chains as lists of bean names (e.g.
- * "filter1, filter2, filter3") until the application context is available, then resolves them
- * to actual Filter instances when the setApplicationContext method is called.
- * It then uses them to build the secure URL configuration for the supplied FilterChainMap.
- */
- static class FilterChainResolver implements ApplicationContextAware {
- private List paths;
- private List filterChains;
- FilterChainMap filterChainMap;
-
- FilterChainResolver(FilterChainMap filterChainMap, List paths, List filterChains) {
- this.paths = paths;
- this.filterChains = filterChains;
- this.filterChainMap = filterChainMap;
- }
-
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- for (int i=0; i < paths.size(); i++) {
- String path = (String)paths.get(i);
- String filterList = (String) filterChains.get(i);
-
- if (filterList.equals(HttpSecurityBeanDefinitionParser.NO_FILTERS_VALUE)) {
- filterChainMap.addSecureUrl(path, HttpSecurityBeanDefinitionParser.EMPTY_FILTER_CHAIN);
- } else {
- String[] filterNames = StringUtils.tokenizeToStringArray(filterList, ",");
- Filter[] filters = new Filter[filterNames.length];
-
- for (int j=0; j < filterNames.length; j++) {
- filters[j] = (Filter) applicationContext.getBean(filterNames[j], Filter.class);
- }
-
- filterChainMap.addSecureUrl(path, filters);
+ for (int i=0; i < filterBeanNames.length; i++) {
+ filterChain.add(new RuntimeBeanReference(filterBeanNames[i]));
}
+
+ filterChainMap.put(path, filterChain);
}
}
+
+ filterChainProxy.getPropertyValues().addPropertyValue("filterChainMap", new ManagedMap(filterChainMap));
+
+ return holder;
}
}
diff --git a/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java b/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java
index 3c69d07403..e323af7a14 100644
--- a/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java
+++ b/core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java
@@ -9,7 +9,10 @@ import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.ConfigAttributeEditor;
import org.springframework.security.context.HttpSessionContextIntegrationFilter;
-import org.springframework.security.intercept.web.*;
+import org.springframework.security.intercept.web.FilterInvocationDefinitionMap;
+import org.springframework.security.intercept.web.FilterSecurityInterceptor;
+import org.springframework.security.intercept.web.PathBasedFilterInvocationDefinitionMap;
+import org.springframework.security.intercept.web.RegExpBasedFilterInvocationDefinitionMap;
import org.springframework.security.ui.ExceptionTranslationFilter;
import org.springframework.security.util.FilterChainProxy;
import org.springframework.security.util.RegexUrlPathMatcher;
@@ -19,8 +22,7 @@ import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import javax.servlet.Filter;
-import java.util.Iterator;
-import java.util.List;
+import java.util.*;
/**
* Sets up HTTP security: filter stack and protected URLs.
@@ -48,7 +50,6 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
static final String FILTERS_ATTRIBUTE = "filters";
static final String NO_FILTERS_VALUE = "none";
- static final Filter[] EMPTY_FILTER_CHAIN = new Filter[0];
private static final String ACCESS_CONFIG_ATTRIBUTE = "access";
@@ -71,14 +72,14 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
// TODO: Get path type attribute and determine FilDefInvS class
- FilterChainMap filterChainMap = new FilterChainMap();
+ Map filterChainMap = new LinkedHashMap();
String patternType = element.getAttribute(PATTERN_TYPE_ATTRIBUTE);
FilterInvocationDefinitionMap interceptorFilterInvDefSource = new PathBasedFilterInvocationDefinitionMap();
if (patternType.equals(PATTERN_TYPE_REGEX)) {
- filterChainMap.setUrlPathMatcher(new RegexUrlPathMatcher());
+ filterChainProxy.getPropertyValues().addPropertyValue("matcher", new RegexUrlPathMatcher());
interceptorFilterInvDefSource = new RegExpBasedFilterInvocationDefinitionMap();
}
@@ -131,10 +132,10 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
}
/**
- * Parses the intercept-url elements and populates the FilterChainProxy's FilterChainMap and the
+ * Parses the intercept-url elements and populates the FilterChainProxy's filter chain Map and the
* FilterInvocationDefinitionSource used in FilterSecurityInterceptor.
*/
- private void parseInterceptUrls(List urlElts, FilterChainMap filterChainMap,
+ private void parseInterceptUrls(List urlElts, Map filterChainMap,
FilterInvocationDefinitionMap interceptorFilterInvDefSource) {
Iterator urlEltsIterator = urlElts.iterator();
@@ -166,7 +167,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
throw new IllegalStateException("Currently only 'none' is supported as the custom filters attribute");
}
- filterChainMap.addSecureUrl(path, EMPTY_FILTER_CHAIN);
+ filterChainMap.put(path, Collections.EMPTY_LIST);
}
}
}
diff --git a/core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java b/core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java
index 01cee6494b..47a25cdd7e 100644
--- a/core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java
+++ b/core/src/main/java/org/springframework/security/config/HttpSecurityConfigPostProcessor.java
@@ -79,11 +79,14 @@ public class HttpSecurityConfigPostProcessor implements BeanFactoryPostProcessor
// Set the default match
List defaultFilterChain = orderFilters(beanFactory);
- FilterChainMap filterMap = filterChainProxy.getFilterChainMap();
+ // Note that this returns a copy
+ Map filterMap = filterChainProxy.getFilterChainMap();
- String allUrlsMatch = filterMap.getMatcher().getUniversalMatchPattern();
+ String allUrlsMatch = filterChainProxy.getMatcher().getUniversalMatchPattern();
- filterMap.addSecureUrl(allUrlsMatch, (Filter[]) defaultFilterChain.toArray(new Filter[0]));
+ filterMap.put(allUrlsMatch, defaultFilterChain);
+
+ filterChainProxy.setFilterChainMap(filterMap);
}
private List orderFilters(ConfigurableListableBeanFactory beanFactory) {
diff --git a/core/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java b/core/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java
index b9efe94523..3cd6ded317 100644
--- a/core/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java
+++ b/core/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java
@@ -1,6 +1,14 @@
package org.springframework.security.config;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.security.util.FilterChainProxy;
+import org.springframework.util.xml.DomUtils;
+import org.w3c.dom.Element;
/**
@@ -10,6 +18,8 @@ import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
* @version $Id$
*/
public class SecurityNamespaceHandler extends NamespaceHandlerSupport {
+ public static final String DEFAULT_FILTER_CHAIN_PROXY_ID = "_filterChainProxy";
+
public void init() {
registerBeanDefinitionParser("ldap", new LdapBeanDefinitionParser());
registerBeanDefinitionParser("http", new HttpSecurityBeanDefinitionParser());
@@ -18,4 +28,19 @@ public class SecurityNamespaceHandler extends NamespaceHandlerSupport {
registerBeanDefinitionDecorator("intercept-methods", new InterceptMethodsBeanDefinitionDecorator());
registerBeanDefinitionDecorator("filter-chain-map", new FilterChainMapBeanDefinitionDecorator());
}
+
+// private class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
+//
+// public BeanDefinition parse(Element element, ParserContext parserContext) {
+// RootBeanDefinition filterChainProxy = new RootBeanDefinition(FilterChainProxy.class);
+//
+// Element formLoginElt = DomUtils.getChildElementByTagName(element, "form-login");
+//
+// if (formLoginElt != null) {
+// parse(formLoginElt, parserContext);
+// }
+//
+// return filterChainProxy;
+// }
+// }
}
diff --git a/core/src/main/java/org/springframework/security/intercept/web/FIDSToFilterChainMapConverter.java b/core/src/main/java/org/springframework/security/intercept/web/FIDSToFilterChainMapConverter.java
index 3a6eccb5df..c51ca02dbc 100644
--- a/core/src/main/java/org/springframework/security/intercept/web/FIDSToFilterChainMapConverter.java
+++ b/core/src/main/java/org/springframework/security/intercept/web/FIDSToFilterChainMapConverter.java
@@ -4,26 +4,24 @@ import org.springframework.context.ApplicationContext;
import org.springframework.security.ConfigAttribute;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.util.FilterChainProxy;
-import org.springframework.security.util.RegexUrlPathMatcher;
import javax.servlet.Filter;
-import java.util.List;
-import java.util.Iterator;
-import java.util.ArrayList;
+import java.util.*;
/**
* Used internally to provide backward compatibility for configuration of FilterChainProxy using a
* FilterInvocationDefinitionSource. This is deprecated in favour of namespace-based configuration.
*
- * This class will convert a FilterInvocationDefinitionSource into a FilterChainMap, provided it is one of the
- * recognised implementations (ant path or regular expression).
+ * This class will convert a FilterInvocationDefinitionSource into a suitable Map, provided it is one of the
+ * recognised implementations (ant path or regular expression). The order of the mappings will be
+ * preserved in the Map.
*
* @author Luke Taylor
* @version $Id$
*/
public class FIDSToFilterChainMapConverter {
- private FilterChainMap filterChainMap = new FilterChainMap();
+ private LinkedHashMap filterChainMap = new LinkedHashMap();
public FIDSToFilterChainMapConverter(FilterInvocationDefinitionSource fids, ApplicationContext appContext) {
@@ -39,7 +37,6 @@ public class FIDSToFilterChainMapConverter {
requestMap = ((PathBasedFilterInvocationDefinitionMap)fids).getRequestMap();
} else if (fids instanceof RegExpBasedFilterInvocationDefinitionMap) {
requestMap = ((RegExpBasedFilterInvocationDefinitionMap)fids).getRequestMap();
- filterChainMap.setUrlPathMatcher(new RegexUrlPathMatcher());
} else {
throw new IllegalArgumentException("Can't handle FilterInvocationDefinitionSource type " + fids.getClass());
}
@@ -77,11 +74,11 @@ public class FIDSToFilterChainMapConverter {
}
}
- filterChainMap.addSecureUrl(path, (Filter[]) filters.toArray(new Filter[filters.size()]));
+ filterChainMap.put(path, filters);
}
}
- public FilterChainMap getFilterChainMap() {
+ public Map getFilterChainMap() {
return filterChainMap;
}
}
diff --git a/core/src/main/java/org/springframework/security/util/FilterChainProxy.java b/core/src/main/java/org/springframework/security/util/FilterChainProxy.java
index 158cdd11d1..59b52c47ff 100644
--- a/core/src/main/java/org/springframework/security/util/FilterChainProxy.java
+++ b/core/src/main/java/org/springframework/security/util/FilterChainProxy.java
@@ -15,30 +15,18 @@
package org.springframework.security.util;
-import org.springframework.security.intercept.web.FilterInvocation;
-import org.springframework.security.intercept.web.FilterInvocationDefinitionSource;
-import org.springframework.security.intercept.web.FilterChainMap;
-import org.springframework.security.intercept.web.FIDSToFilterChainMapConverter;
-
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
-
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
-
+import org.springframework.security.intercept.web.*;
import org.springframework.util.Assert;
+import javax.servlet.*;
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 java.util.*;
/**
@@ -53,10 +41,11 @@ import javax.servlet.ServletResponse;
* #doFilter(ServletRequest, ServletResponse, FilterChain)} invocations through to each Filter defined
* against FilterChainProxy.
As of version 2.0, FilterChainProxy is configured using a {@link FilterChainMap}. In previous + *
As of version 2.0, FilterChainProxy is configured using an ordered Map of path patterns to Lists
+ * of Filter objects. In previous
* versions, a {@link FilterInvocationDefinitionSource} was used. This is now deprecated in favour of namespace-based
- * configuration which provides a more robust and simplfied syntax. The FilterChainMap instance will be
- * created while parsing the namespace configuration, so it doesn't require an explicit bean declaration.
+ * configuration which provides a more robust and simplfied syntax. The Map instance will normally be
+ * created while parsing the namespace configuration, so doesn't have to be set explicitly.
* Instead the <filter-chain-map> element should be used within the FilterChainProxy bean declaration.
* This in turn should have a list of child <filter-chain> elements which each define a URI pattern and the list
* of filters (as comma-separated bean names) which should be applied to requests which match the pattern.
@@ -92,7 +81,7 @@ import javax.servlet.ServletResponse;
* container. As per {@link org.springframework.security.util.FilterToBeanProxy} JavaDocs, we recommend you allow the IoC
* container to manage lifecycle instead of the servlet container. By default the FilterToBeanProxy will
* never call this class' {@link #init(FilterConfig)} and {@link #destroy()} methods, meaning each of the filters
- * defined in the FilterChainMap will not be called. If you do need your filters to be
+ * defined in the filter chain map will not be called. If you do need your filters to be
* initialized and destroyed, please set the lifecycle initialization parameter against the
* FilterToBeanProxy to specify servlet container lifecycle management.
Filter instances registered in the
- * FilterChainMap.
- * This is useful in ensuring a Filter is not
- * initialized or destroyed twice.
Filter instances registered in the map of
+ * filter chains.
+ * This is useful in ensuring a Filter is not initialized or destroyed twice.
Filter instances in the application context which have an entry
- * in the FilterChainMap (only one entry is included in the array for
+ * in the map (only one entry is included in the array for
* each Filter that actually exists in application context, even if a given
* Filter is defined multiples times by the FilterChainMap)
*/
protected Filter[] obtainAllDefinedFilters() {
- return filterChainMap.getAllDefinedFilters();
+ Set allFilters = new HashSet();
+
+ Iterator it = filterChainMap.values().iterator();
+
+ while (it.hasNext()) {
+ allFilters.addAll((List) it.next());
+ }
+
+ return (Filter[]) new ArrayList(allFilters).toArray(new Filter[0]);
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
@@ -200,15 +227,69 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo
* @deprecated Use namespace configuration or call setFilterChainMap instead.
*/
public void setFilterInvocationDefinitionSource(FilterInvocationDefinitionSource fids) {
+ if( fids instanceof RegExpBasedFilterInvocationDefinitionMap) {
+ matcher = new RegexUrlPathMatcher();
+ }
this.fids = fids;
}
- public void setFilterChainMap(FilterChainMap filterChainMap) {
- this.filterChainMap = filterChainMap;
+ /**
+ * Sets the mapping of URL patterns to filter chains.
+ *
+ * The map keys should be the paths and the values should be arrays of Filter objects.
+ * It's VERY important that the type of map used preserves ordering - the order in which the iterator
+ * returns the entries must be the same as the order they were added to the map, otherwise you have no way
+ * of guaranteeing that the most specific patterns are returned before the more general ones. So make sure
+ * the Map used is an instance of LinkedHashMap or an equivalent, rather than a plain HashMap, for
+ * example.
+ *
+ * @param filterChainMap the map of path Strings to Filter[]s.
+ */
+ public void setFilterChainMap(Map filterChainMap) {
+ uncompiledFilterChainMap = new LinkedHashMap(filterChainMap);
+ createCompiledMap();
}
- public FilterChainMap getFilterChainMap() {
- return filterChainMap;
+ private void createCompiledMap() {
+ Iterator paths = uncompiledFilterChainMap.keySet().iterator();
+ filterChainMap = new LinkedHashMap(uncompiledFilterChainMap.size());
+
+ while (paths.hasNext()) {
+ Object path = paths.next();
+ Assert.isInstanceOf(String.class, path, "Path pattern must be a String");
+ Object compiledPath = matcher.compile((String)path);
+ Object filters = uncompiledFilterChainMap.get(path);
+
+ Assert.isInstanceOf(List.class, filters);
+ // Check the contents
+ Iterator filterIterator = ((List)filters).iterator();
+
+ while (filterIterator.hasNext()) {
+ Object filter = filterIterator.next();
+ Assert.isInstanceOf(Filter.class, filter, "Objects in filter chain must be of type Filter. ");
+ }
+
+ filterChainMap.put(compiledPath, filters);
+ }
+ }
+
+
+ /**
+ * Returns a copy of the underlying filter chain map. Modifications to the map contents
+ * will not affect the FilterChainProxy state - to change the map call setFilterChainMap.
+ *
+ * @return the map of path pattern Strings to filter chain arrays (with ordering guaranteed).
+ */
+ public Map getFilterChainMap() {
+ return new LinkedHashMap(uncompiledFilterChainMap);
+ }
+
+ public void setMatcher(UrlMatcher matcher) {
+ this.matcher = matcher;
+ }
+
+ public UrlMatcher getMatcher() {
+ return matcher;
}
//~ Inner Classes ==================================================================================================
@@ -221,17 +302,17 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo
*/
private static class VirtualFilterChain implements FilterChain {
private FilterInvocation fi;
- private Filter[] additionalFilters;
+ private List additionalFilters;
private int currentPosition = 0;
- public VirtualFilterChain(FilterInvocation filterInvocation, Filter[] additionalFilters) {
+ private VirtualFilterChain(FilterInvocation filterInvocation, List additionalFilters) {
this.fi = filterInvocation;
this.additionalFilters = additionalFilters;
}
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
- if (currentPosition == additionalFilters.length) {
+ if (currentPosition == additionalFilters.size()) {
if (logger.isDebugEnabled()) {
logger.debug(fi.getRequestUrl()
+ " reached end of additional filter chain; proceeding with original chain");
@@ -241,13 +322,15 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo
} else {
currentPosition++;
+ Filter nextFilter = (Filter) additionalFilters.get(currentPosition - 1);
+
if (logger.isDebugEnabled()) {
logger.debug(fi.getRequestUrl() + " at position " + currentPosition + " of "
- + additionalFilters.length + " in additional filter chain; firing Filter: '"
- + additionalFilters[currentPosition - 1] + "'");
+ + additionalFilters.size() + " in additional filter chain; firing Filter: '"
+ + nextFilter + "'");
}
- additionalFilters[currentPosition - 1].doFilter(request, response, this);
+ nextFilter.doFilter(request, response, this);
}
}
}
diff --git a/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java b/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java
index e8888b1e22..aa45a57e3f 100644
--- a/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java
+++ b/core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java
@@ -6,7 +6,6 @@ import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.context.HttpSessionContextIntegrationFilter;
-import org.springframework.security.intercept.web.FilterChainMap;
import org.springframework.security.intercept.web.FilterSecurityInterceptor;
import org.springframework.security.ui.ExceptionTranslationFilter;
import org.springframework.security.ui.basicauth.BasicProcessingFilter;
@@ -15,7 +14,8 @@ import org.springframework.security.ui.webapp.AuthenticationProcessingFilter;
import org.springframework.security.ui.webapp.DefaultLoginPageGeneratingFilter;
import org.springframework.security.util.FilterChainProxy;
-import javax.servlet.Filter;
+import java.util.Iterator;
+import java.util.List;
/**
* @author Luke Taylor
@@ -41,11 +41,9 @@ public class HttpSecurityBeanDefinitionParserTests {
FilterChainProxy filterChainProxy =
(FilterChainProxy) appContext.getBean(HttpSecurityBeanDefinitionParser.DEFAULT_FILTER_CHAIN_PROXY_ID);
- FilterChainMap filterChainMap = filterChainProxy.getFilterChainMap();
+ List filters = filterChainProxy.getFilters("/unprotected");
- Filter[] filters = filterChainMap.getFilters("/unprotected");
-
- assertTrue(filters.length == 0);
+ assertTrue(filters.size() == 0);
}
@Test
@@ -53,20 +51,18 @@ public class HttpSecurityBeanDefinitionParserTests {
FilterChainProxy filterChainProxy =
(FilterChainProxy) appContext.getBean(HttpSecurityBeanDefinitionParser.DEFAULT_FILTER_CHAIN_PROXY_ID);
- FilterChainMap filterChainMap = filterChainProxy.getFilterChainMap();
-
- Filter[] filters = filterChainMap.getFilters("/someurl");
-
-
+ List filterList = filterChainProxy.getFilters("/someurl");
- assertTrue("Expected 7 filters in chain", filters.length == 7);
+ assertTrue("Expected 7 filterList in chain", filterList.size() == 7);
- assertTrue(filters[0] instanceof HttpSessionContextIntegrationFilter);
- assertTrue(filters[1] instanceof LogoutFilter);
- assertTrue(filters[2] instanceof AuthenticationProcessingFilter);
- assertTrue(filters[3] instanceof DefaultLoginPageGeneratingFilter);
- assertTrue(filters[4] instanceof BasicProcessingFilter);
- assertTrue(filters[5] instanceof ExceptionTranslationFilter);
- assertTrue(filters[6] instanceof FilterSecurityInterceptor);
+ Iterator filters = filterList.iterator();
+
+ assertTrue(filters.next() instanceof HttpSessionContextIntegrationFilter);
+ assertTrue(filters.next() instanceof LogoutFilter);
+ assertTrue(filters.next() instanceof AuthenticationProcessingFilter);
+ assertTrue(filters.next() instanceof DefaultLoginPageGeneratingFilter);
+ assertTrue(filters.next() instanceof BasicProcessingFilter);
+ assertTrue(filters.next() instanceof ExceptionTranslationFilter);
+ assertTrue(filters.next() instanceof FilterSecurityInterceptor);
}
}
diff --git a/core/src/test/java/org/springframework/security/util/FilterChainProxyTests.java b/core/src/test/java/org/springframework/security/util/FilterChainProxyTests.java
index 744874707d..0efd25ce4b 100644
--- a/core/src/test/java/org/springframework/security/util/FilterChainProxyTests.java
+++ b/core/src/test/java/org/springframework/security/util/FilterChainProxyTests.java
@@ -15,10 +15,7 @@
package org.springframework.security.util;
-import org.junit.After;
-import static org.junit.Assert.*;
-import org.junit.Before;
-import org.junit.Test;
+
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
@@ -26,9 +23,17 @@ import org.springframework.security.ConfigAttribute;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.MockApplicationContext;
import org.springframework.security.MockFilterConfig;
+import org.springframework.security.context.HttpSessionContextIntegrationFilter;
import org.springframework.security.intercept.web.MockFilterInvocationDefinitionSource;
import org.springframework.security.intercept.web.PathBasedFilterInvocationDefinitionMap;
+import org.springframework.security.ui.webapp.AuthenticationProcessingFilter;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+import java.util.List;
/**
* Tests {@link FilterChainProxy}.
@@ -123,17 +128,50 @@ public class FilterChainProxyTests {
@Test
public void normalOperation() throws Exception {
- doNormalOperation((FilterChainProxy) appCtx.getBean("filterChain", FilterChainProxy.class));
+ FilterChainProxy filterChainProxy = (FilterChainProxy) appCtx.getBean("filterChain", FilterChainProxy.class);
+ doNormalOperation(filterChainProxy);
}
@Test
public void normalOperationWithNewConfig() throws Exception {
- doNormalOperation((FilterChainProxy) appCtx.getBean("newFilterChainProxy", FilterChainProxy.class));
+ FilterChainProxy filterChainProxy = (FilterChainProxy) appCtx.getBean("newFilterChainProxy", FilterChainProxy.class);
+ checkPathAndFilterOrder(filterChainProxy);
+ doNormalOperation(filterChainProxy);
}
@Test
public void normalOperationWithNewConfigRegex() throws Exception {
- doNormalOperation((FilterChainProxy) appCtx.getBean("newFilterChainProxyRegex", FilterChainProxy.class));
+ FilterChainProxy filterChainProxy = (FilterChainProxy) appCtx.getBean("newFilterChainProxyRegex", FilterChainProxy.class);
+ checkPathAndFilterOrder(filterChainProxy);
+ doNormalOperation(filterChainProxy);
+ }
+
+ @Test
+ public void normalOperationWithNewConfigNonNamespace() throws Exception {
+ FilterChainProxy filterChainProxy = (FilterChainProxy) appCtx.getBean("newFilterChainProxyNonNamespace", FilterChainProxy.class);
+ checkPathAndFilterOrder(filterChainProxy);
+ doNormalOperation(filterChainProxy);
+ }
+
+ private void checkPathAndFilterOrder(FilterChainProxy filterChainProxy) throws Exception {
+ List filters = filterChainProxy.getFilters("/foo/blah");
+ assertEquals(1, filters.size());
+ assertTrue(filters.get(0) instanceof MockFilter);
+
+ filters = filterChainProxy.getFilters("/some/other/path/blah");
+ assertEquals(3, filters.size());
+ assertTrue(filters.get(0) instanceof HttpSessionContextIntegrationFilter);
+ assertTrue(filters.get(1) instanceof MockFilter);
+ assertTrue(filters.get(2) instanceof MockFilter);
+
+ filters = filterChainProxy.getFilters("/do/not/filter");
+ assertEquals(0, filters.size());
+
+ filters = filterChainProxy.getFilters("/another/nonspecificmatch");
+ assertEquals(3, filters.size());
+ assertTrue(filters.get(0) instanceof HttpSessionContextIntegrationFilter);
+ assertTrue(filters.get(1) instanceof AuthenticationProcessingFilter);
+ assertTrue(filters.get(2) instanceof MockFilter);
}
private void doNormalOperation(FilterChainProxy filterChainProxy) throws Exception {
@@ -165,7 +203,6 @@ public class FilterChainProxyTests {
assertTrue(filter.isWasInitialized());
assertTrue(filter.isWasDoFiltered());
assertTrue(filter.isWasDestroyed());
-
}
//~ Inner Classes ==================================================================================================
diff --git a/core/src/test/resources/org/springframework/security/util/filtertest-valid.xml b/core/src/test/resources/org/springframework/security/util/filtertest-valid.xml
index 082bb95f52..0866bc180e 100644
--- a/core/src/test/resources/org/springframework/security/util/filtertest-valid.xml
+++ b/core/src/test/resources/org/springframework/security/util/filtertest-valid.xml
@@ -28,7 +28,18 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc