SEC-568: Introduced FilterChainMap as a simpler option for configuring FilterChainProxy and introduced a namespace-based for configuring it. The Url pattern matching is factored out into a separate strategy with ant and regex versions.
This commit is contained in:
parent
d6fe97de43
commit
9b8c06e9f6
|
@ -0,0 +1,110 @@
|
||||||
|
package org.springframework.security.config;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
|
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
|
||||||
|
import org.springframework.beans.factory.xml.ParserContext;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
import org.springframework.security.intercept.web.FilterChainMap;
|
||||||
|
import org.springframework.security.util.RegexUrlPathMatcher;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the FilterChainMap for a FilterChainProxy bean declaration.
|
||||||
|
*
|
||||||
|
* @author Luke Taylor
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
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();
|
||||||
|
Element elt = (Element)node;
|
||||||
|
|
||||||
|
String pathType = elt.getAttribute(HttpSecurityBeanDefinitionParser.PATTERN_TYPE_ATTRIBUTE);
|
||||||
|
|
||||||
|
if (HttpSecurityBeanDefinitionParser.PATTERN_TYPE_REGEX.equals(pathType)) {
|
||||||
|
filterChainMap.setUrlPathMatcher(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");
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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 <tt>setApplicationContext</tt> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
package org.springframework.security.intercept.web;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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).
|
||||||
|
*
|
||||||
|
* @author Luke Taylor
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class FIDSToFilterChainMapConverter {
|
||||||
|
|
||||||
|
private FilterChainMap filterChainMap = new FilterChainMap();
|
||||||
|
|
||||||
|
public FIDSToFilterChainMapConverter(FilterInvocationDefinitionSource fids, ApplicationContext appContext) {
|
||||||
|
|
||||||
|
List requestMap;
|
||||||
|
|
||||||
|
// TODO: Check if this is necessary. Retained from refactoring of FilterChainProxy
|
||||||
|
if (fids.getConfigAttributeDefinitions() == null) {
|
||||||
|
throw new IllegalArgumentException("FilterChainProxy requires the FilterInvocationDefinitionSource to " +
|
||||||
|
"return a non-null response to getConfigAttributeDefinitions()");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fids instanceof PathBasedFilterInvocationDefinitionMap) {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator entries = requestMap.iterator();
|
||||||
|
|
||||||
|
while (entries.hasNext()) {
|
||||||
|
Object entry = entries.next();
|
||||||
|
String path;
|
||||||
|
ConfigAttributeDefinition configAttributeDefinition;
|
||||||
|
|
||||||
|
if (entry instanceof PathBasedFilterInvocationDefinitionMap.EntryHolder) {
|
||||||
|
path = ((PathBasedFilterInvocationDefinitionMap.EntryHolder)entry).getAntPath();
|
||||||
|
configAttributeDefinition = ((PathBasedFilterInvocationDefinitionMap.EntryHolder)entry).getConfigAttributeDefinition();
|
||||||
|
} else {
|
||||||
|
path = ((RegExpBasedFilterInvocationDefinitionMap.EntryHolder)entry).getCompiledPattern().pattern();
|
||||||
|
configAttributeDefinition = ((RegExpBasedFilterInvocationDefinitionMap.EntryHolder)entry).getConfigAttributeDefinition();
|
||||||
|
}
|
||||||
|
|
||||||
|
List filters = new ArrayList();
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filterName.equals(FilterChainProxy.TOKEN_NONE)) {
|
||||||
|
filters.add(appContext.getBean(filterName, Filter.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filterChainMap.addSecureUrl(path, (Filter[]) filters.toArray(new Filter[filters.size()]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FilterChainMap getFilterChainMap() {
|
||||||
|
return filterChainMap;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
package org.springframework.security.intercept.web;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.security.util.AntUrlPathMatcher;
|
||||||
|
import org.springframework.security.util.UrlMatcher;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
import javax.servlet.Filter;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps filter invocations to filter chains. Used to configure FilterChainProxy.
|
||||||
|
*
|
||||||
|
* @see org.springframework.security.util.FilterChainProxy
|
||||||
|
*
|
||||||
|
* @author luke
|
||||||
|
* @version $Id$
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public class FilterChainMap implements InitializingBean {
|
||||||
|
private static final Log logger = LogFactory.getLog(FilterChainMap.class);
|
||||||
|
|
||||||
|
private List paths = new ArrayList();
|
||||||
|
private List compiledPaths = new ArrayList();
|
||||||
|
private List filterChains = new ArrayList();
|
||||||
|
|
||||||
|
private UrlMatcher matcher = new AntUrlPathMatcher();
|
||||||
|
|
||||||
|
public FilterChainMap() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void afterPropertiesSet() throws Exception {
|
||||||
|
Assert.notEmpty(paths, "No secure URL paths defined");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSecureUrl(String path, Filter[] filters) {
|
||||||
|
Assert.hasText(path, "The Path must not be empty or null");
|
||||||
|
Assert.notNull(filters, "The Filter array must not be null");
|
||||||
|
paths.add(path);
|
||||||
|
compiledPaths.add(matcher.compile(path));
|
||||||
|
filterChains.add(filters);
|
||||||
|
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Added pattern: " + path + "; filters: " + Arrays.asList(filters));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUrlPathMatcher(UrlMatcher matcher) {
|
||||||
|
this.matcher = matcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UrlMatcher getMatcher() {
|
||||||
|
return matcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first filter chain matching the supplied URL.
|
||||||
|
*
|
||||||
|
* @param url the request URL
|
||||||
|
* @return an ordered array of Filters defining the filter chain
|
||||||
|
*/
|
||||||
|
public Filter[] getFilters(String url) {
|
||||||
|
|
||||||
|
for (int i=0; i < compiledPaths.size(); i++) {
|
||||||
|
Object path = compiledPaths.get(i);
|
||||||
|
|
||||||
|
boolean matched = matcher.pathMatchesUrl(path, url);
|
||||||
|
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Candidate is: '" + url + "'; pattern is " + paths.get(i) + "; matched=" + matched);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matched) {
|
||||||
|
return (Filter[]) filterChains.get(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all of the <b>unique</b><code>Filter</code> instances registered in the
|
||||||
|
* <code>FilterChainMap</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 which have an entry
|
||||||
|
* in the <code>FilterChainMap</code> (only one entry is included in the array for
|
||||||
|
* each <code>Filter</code> instance, even if a given
|
||||||
|
* <code>Filter</code> is used multiples times by the <code>FilterChainMap</code>)
|
||||||
|
*/
|
||||||
|
public Filter[] getAllDefinedFilters() {
|
||||||
|
Set allFilters = new HashSet();
|
||||||
|
|
||||||
|
Iterator it = filterChains.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Filter[] filterChain = (Filter[])it.next();
|
||||||
|
|
||||||
|
for(int i=0; i < filterChain.length; i++) {
|
||||||
|
allFilters.add(filterChain[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Filter[]) new ArrayList(allFilters).toArray(new Filter[0]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -131,6 +131,10 @@ public class PathBasedFilterInvocationDefinitionMap extends AbstractFilterInvoca
|
||||||
this.convertUrlToLowercaseBeforeComparison = convertUrlToLowercaseBeforeComparison;
|
this.convertUrlToLowercaseBeforeComparison = convertUrlToLowercaseBeforeComparison;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List getRequestMap() {
|
||||||
|
return requestMap;
|
||||||
|
}
|
||||||
|
|
||||||
//~ Inner Classes ==================================================================================================
|
//~ Inner Classes ==================================================================================================
|
||||||
|
|
||||||
protected class EntryHolder {
|
protected class EntryHolder {
|
||||||
|
|
|
@ -118,6 +118,10 @@ public class RegExpBasedFilterInvocationDefinitionMap extends AbstractFilterInvo
|
||||||
this.convertUrlToLowercaseBeforeComparison = convertUrlToLowercaseBeforeComparison;
|
this.convertUrlToLowercaseBeforeComparison = convertUrlToLowercaseBeforeComparison;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List getRequestMap() {
|
||||||
|
return requestMap;
|
||||||
|
}
|
||||||
|
|
||||||
//~ Inner Classes ==================================================================================================
|
//~ Inner Classes ==================================================================================================
|
||||||
|
|
||||||
protected class EntryHolder {
|
protected class EntryHolder {
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
package org.springframework.security.util;
|
||||||
|
|
||||||
|
import org.springframework.util.PathMatcher;
|
||||||
|
import org.springframework.util.AntPathMatcher;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ant path strategy for URL matching.
|
||||||
|
*
|
||||||
|
* @author luke
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class AntUrlPathMatcher implements UrlMatcher {
|
||||||
|
private static final Log logger = LogFactory.getLog(AntUrlPathMatcher.class);
|
||||||
|
|
||||||
|
private boolean convertToLowercaseBeforeComparison = true;
|
||||||
|
private PathMatcher pathMatcher = new AntPathMatcher();
|
||||||
|
|
||||||
|
public Object compile(String path) {
|
||||||
|
if (convertToLowercaseBeforeComparison) {
|
||||||
|
return path.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConvertToLowercaseBeforeComparison(boolean convertToLowercaseBeforeComparison) {
|
||||||
|
this.convertToLowercaseBeforeComparison = convertToLowercaseBeforeComparison;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean pathMatchesUrl(Object path, String url) {
|
||||||
|
if (convertToLowercaseBeforeComparison) {
|
||||||
|
url = url.toLowerCase();
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Converted URL to lowercase, from: '" + url + "'; to: '" + url + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathMatcher.match((String)path, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUniversalMatchPattern() {
|
||||||
|
return "/**";
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,11 +15,10 @@
|
||||||
|
|
||||||
package org.springframework.security.util;
|
package org.springframework.security.util;
|
||||||
|
|
||||||
import org.springframework.security.ConfigAttribute;
|
|
||||||
import org.springframework.security.ConfigAttributeDefinition;
|
|
||||||
|
|
||||||
import org.springframework.security.intercept.web.FilterInvocation;
|
import org.springframework.security.intercept.web.FilterInvocation;
|
||||||
import org.springframework.security.intercept.web.FilterInvocationDefinitionSource;
|
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.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
@ -34,12 +33,6 @@ import org.springframework.util.Assert;
|
||||||
|
|
||||||
import java.io.IOException;
|
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.Filter;
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.FilterChain;
|
||||||
import javax.servlet.FilterConfig;
|
import javax.servlet.FilterConfig;
|
||||||
|
@ -49,22 +42,48 @@ import javax.servlet.ServletResponse;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegates <code>Filter</code> requests to a list of Spring-managed beans.<p>The <code>FilterChainProxy</code> is
|
* Delegates <code>Filter</code> requests to a list of Spring-managed beans.
|
||||||
* loaded via a standard {@link org.springframework.security.util.FilterToBeanProxy} declaration in <code>web.xml</code>.
|
* As of version 2.0, you shouldn't need to explicitly configure a <tt>FilterChainProxy</tt> bean in your application
|
||||||
|
* context unless you need very fine control over the filter chain contents. Most cases should be adequately covered
|
||||||
|
* by the default <tt><security:http /></tt> namespace configuration options.
|
||||||
|
*
|
||||||
|
* <p>The <code>FilterChainProxy</code> is loaded via a standard
|
||||||
|
* {@link org.springframework.security.util.FilterToBeanProxy} declaration in <code>web.xml</code>.
|
||||||
* <code>FilterChainProxy</code> will then pass {@link #init(FilterConfig)}, {@link #destroy()} and {@link
|
* <code>FilterChainProxy</code> will then pass {@link #init(FilterConfig)}, {@link #destroy()} and {@link
|
||||||
* #doFilter(ServletRequest, ServletResponse, FilterChain)} invocations through to each <code>Filter</code> defined
|
* #doFilter(ServletRequest, ServletResponse, FilterChain)} invocations through to each <code>Filter</code> defined
|
||||||
* against <code>FilterChainProxy</code>.</p>
|
* against <code>FilterChainProxy</code>.</p>
|
||||||
* <p><code>FilterChainProxy</code> is configured using a standard {@link
|
*
|
||||||
* org.springframework.security.intercept.web.FilterInvocationDefinitionSource}. Each possible URI pattern that
|
* <p>As of version 2.0, <tt>FilterChainProxy</tt> is configured using a {@link FilterChainMap}. In previous
|
||||||
* <code>FilterChainProxy</code> should service must be entered. The first matching URI pattern located by
|
* versions, a {@link FilterInvocationDefinitionSource} was used. This is now deprecated in favour of namespace-based
|
||||||
* <code>FilterInvocationDefinitionSource</code> for a given request will be used to define all of the
|
* configuration which provides a more robust and simplfied syntax. The <tt>FilterChainMap</tt> instance will be
|
||||||
|
* created while parsing the namespace configuration, so it doesn't require an explicit bean declaration.
|
||||||
|
* 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.
|
||||||
|
* An example configuration might look like this:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
<bean id="myfilterChainProxy" class="org.springframework.security.util.FilterChainProxy">
|
||||||
|
<security:filter-chain-map pathType="ant">
|
||||||
|
<security:filter-chain pattern="/do/not/filter" filters="none"/>
|
||||||
|
<security:filter-chain pattern="/**" filters="filter1,filter2,filter3"/>
|
||||||
|
</security:filter-chain-map>
|
||||||
|
</bean>
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* The names "filter1", "filter2", "filter3" should be the bean names of <tt>Filter</tt> instances defined in the
|
||||||
|
* application context. The order of the names defines the order in which the filters will be applied. As shown above,
|
||||||
|
* use of the value "none" for the "filters" can be used to exclude
|
||||||
|
* Please consult the security namespace schema file for a full list of available configuration options.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
*<p>
|
||||||
|
* Each possible URI pattern that <code>FilterChainProxy</code> should service must be entered.
|
||||||
|
* The first matching URI pattern for a given request will be used to define all of the
|
||||||
* <code>Filter</code>s that apply to that request. NB: This means you must put most specific URI patterns at the top
|
* <code>Filter</code>s that apply to that request. NB: This means you must put most specific URI patterns at the top
|
||||||
* of the list, and ensure all <code>Filter</code>s that should apply for a given URI pattern are entered against the
|
* of the list, and ensure all <code>Filter</code>s that should apply for a given URI pattern are entered against the
|
||||||
* respective entry. The <code>FilterChainProxy</code> will not iterate the remainder of the URI patterns to locate
|
* respective entry. The <code>FilterChainProxy</code> will not iterate the remainder of the URI patterns to locate
|
||||||
* additional <code>Filter</code>s. The <code>FilterInvocationDefinitionSource</code> described the applicable URI
|
* additional <code>Filter</code>s.</p>
|
||||||
* pattern to fire the filter chain, followed by a list of configuration attributes. Each configuration attribute's
|
|
||||||
* {@link org.springframework.security.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
|
* <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.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse,
|
||||||
* javax.servlet.FilterChain)}, in that the remainder of the origial or <code>FilterChainProxy</code>-declared filter
|
* javax.servlet.FilterChain)}, in that the remainder of the origial or <code>FilterChainProxy</code>-declared filter
|
||||||
|
@ -73,14 +92,14 @@ import javax.servlet.ServletResponse;
|
||||||
* container. As per {@link org.springframework.security.util.FilterToBeanProxy} JavaDocs, we recommend you allow the IoC
|
* 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 <code>FilterToBeanProxy</code> will
|
* 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
|
* 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
|
* defined in the FilterChainMap 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
|
* initialized and destroyed, please set the <code>lifecycle</code> initialization parameter against the
|
||||||
* <code>FilterToBeanProxy</code> to specify servlet container lifecycle management.</p>
|
* <code>FilterToBeanProxy</code> to specify servlet container lifecycle management.</p>
|
||||||
* <p>If a filter name of {@link #TOKEN_NONE} is used, this allows specification of a filter pattern which should
|
|
||||||
* never cause any filters to fire.</p>
|
|
||||||
*
|
*
|
||||||
* @author Carlos Sanchez
|
* @author Carlos Sanchez
|
||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
|
* @author Luke Taylor
|
||||||
|
*
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public class FilterChainProxy implements Filter, InitializingBean, ApplicationContextAware {
|
public class FilterChainProxy implements Filter, InitializingBean, ApplicationContextAware {
|
||||||
|
@ -92,19 +111,22 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo
|
||||||
//~ Instance fields ================================================================================================
|
//~ Instance fields ================================================================================================
|
||||||
|
|
||||||
private ApplicationContext applicationContext;
|
private ApplicationContext applicationContext;
|
||||||
private FilterInvocationDefinitionSource filterInvocationDefinitionSource;
|
private FilterChainMap filterChainMap;
|
||||||
|
private FilterInvocationDefinitionSource fids;
|
||||||
//~ Methods ========================================================================================================
|
//~ Methods ========================================================================================================
|
||||||
|
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
Assert.notNull(filterInvocationDefinitionSource, "filterInvocationDefinitionSource must be specified");
|
// Convert the FilterDefinitionSource to a filterChainMap if set
|
||||||
Assert.notNull(this.filterInvocationDefinitionSource.getConfigAttributeDefinitions(),
|
if (fids != null) {
|
||||||
"FilterChainProxy requires the FilterInvocationDefinitionSource to return a non-null response to "
|
Assert.isNull(filterChainMap, "Set the FilterChainMap or FilterInvocationDefinitionSource but not both");
|
||||||
+ "getConfigAttributeDefinitions()");
|
setFilterChainMap(new FIDSToFilterChainMapConverter(fids, applicationContext).getFilterChainMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.notNull(filterChainMap, "A FilterChainMap must be supplied");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
Filter[] filters = obtainAllDefinedFilters();
|
Filter[] filters = filterChainMap.getAllDefinedFilters();
|
||||||
|
|
||||||
for (int i = 0; i < filters.length; i++) {
|
for (int i = 0; i < filters.length; i++) {
|
||||||
if (filters[i] != null) {
|
if (filters[i] != null) {
|
||||||
|
@ -118,26 +140,16 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo
|
||||||
}
|
}
|
||||||
|
|
||||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||||
throws IOException, ServletException {
|
throws IOException, ServletException {
|
||||||
|
|
||||||
FilterInvocation fi = new FilterInvocation(request, response, chain);
|
FilterInvocation fi = new FilterInvocation(request, response, chain);
|
||||||
|
|
||||||
ConfigAttributeDefinition cad = this.filterInvocationDefinitionSource.getAttributes(fi);
|
Filter[] filters = filterChainMap.getFilters(fi.getRequestUrl());
|
||||||
|
|
||||||
if (cad == null) {
|
if (filters == null || filters.length == 0) {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(fi.getRequestUrl() + " has no matching filters");
|
logger.debug(fi.getRequestUrl() +
|
||||||
}
|
filters == null ? " has no matching filters" : " has an empty filter list");
|
||||||
|
|
||||||
chain.doFilter(request, response);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Filter[] filters = obtainAllDefinedFilters(cad);
|
|
||||||
|
|
||||||
if (filters.length == 0) {
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug(fi.getRequestUrl() + " has an empty filter list");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
chain.doFilter(request, response);
|
chain.doFilter(request, response);
|
||||||
|
@ -149,12 +161,8 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo
|
||||||
virtualFilterChain.doFilter(fi.getRequest(), fi.getResponse());
|
virtualFilterChain.doFilter(fi.getRequest(), fi.getResponse());
|
||||||
}
|
}
|
||||||
|
|
||||||
public FilterInvocationDefinitionSource getFilterInvocationDefinitionSource() {
|
|
||||||
return filterInvocationDefinitionSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init(FilterConfig filterConfig) throws ServletException {
|
public void init(FilterConfig filterConfig) throws ServletException {
|
||||||
Filter[] filters = obtainAllDefinedFilters();
|
Filter[] filters = filterChainMap.getAllDefinedFilters();
|
||||||
|
|
||||||
for (int i = 0; i < filters.length; i++) {
|
for (int i = 0; i < filters.length; i++) {
|
||||||
if (filters[i] != null) {
|
if (filters[i] != null) {
|
||||||
|
@ -168,70 +176,39 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtains all of the <b>unique</b><code>Filter</code> instances registered against the
|
* Obtains all of the <b>unique</b><code>Filter</code> instances registered in the
|
||||||
* <code>FilterInvocationDefinitionSource</code>.<p>This is useful in ensuring a <code>Filter</code> is not
|
* <code>FilterChainMap</code>.
|
||||||
|
* <p>This is useful in ensuring a <code>Filter</code> is not
|
||||||
* initialized or destroyed twice.</p>
|
* initialized or destroyed twice.</p>
|
||||||
*
|
*
|
||||||
* @return all of the <code>Filter</code> instances in the application context for which there has been an entry
|
* @deprecated
|
||||||
* against the <code>FilterInvocationDefinitionSource</code> (only one entry is included in the array for
|
* @return all of the <code>Filter</code> instances in the application context which have an entry
|
||||||
|
* in the <code>FilterChainMap</code> (only one entry is included in the array for
|
||||||
* each <code>Filter</code> that actually exists in application context, even if a given
|
* 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>)
|
* <code>Filter</code> is defined multiples times by the <code>FilterChainMap</code>)
|
||||||
*/
|
*/
|
||||||
protected Filter[] obtainAllDefinedFilters() {
|
protected Filter[] obtainAllDefinedFilters() {
|
||||||
Iterator cads = this.filterInvocationDefinitionSource.getConfigAttributeDefinitions();
|
return filterChainMap.getAllDefinedFilters();
|
||||||
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[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||||
* 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> (never
|
|
||||||
* <code>null</code>)
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException DOCUMENT ME!
|
|
||||||
*/
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!filterName.equals(TOKEN_NONE)) {
|
|
||||||
list.add(this.applicationContext.getBean(filterName, Filter.class));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (Filter[]) list.toArray(new Filter[list.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setApplicationContext(ApplicationContext applicationContext)
|
|
||||||
throws BeansException {
|
|
||||||
this.applicationContext = applicationContext;
|
this.applicationContext = applicationContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFilterInvocationDefinitionSource(FilterInvocationDefinitionSource filterInvocationDefinitionSource) {
|
/**
|
||||||
this.filterInvocationDefinitionSource = filterInvocationDefinitionSource;
|
*
|
||||||
|
* @deprecated Use namespace configuration or call setFilterChainMap instead.
|
||||||
|
*/
|
||||||
|
public void setFilterInvocationDefinitionSource(FilterInvocationDefinitionSource fids) {
|
||||||
|
this.fids = fids;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFilterChainMap(FilterChainMap filterChainMap) {
|
||||||
|
this.filterChainMap = filterChainMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FilterChainMap getFilterChainMap() {
|
||||||
|
return filterChainMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
//~ Inner Classes ==================================================================================================
|
//~ Inner Classes ==================================================================================================
|
||||||
|
@ -242,7 +219,7 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo
|
||||||
* <code>FilterChain</code> is used by <code>FilterChainProxy</code> to determine if the next <code>Filter</code>
|
* <code>FilterChain</code> is used by <code>FilterChainProxy</code> to determine if the next <code>Filter</code>
|
||||||
* should be called or not.</p>
|
* should be called or not.</p>
|
||||||
*/
|
*/
|
||||||
private class VirtualFilterChain implements FilterChain {
|
private static class VirtualFilterChain implements FilterChain {
|
||||||
private FilterInvocation fi;
|
private FilterInvocation fi;
|
||||||
private Filter[] additionalFilters;
|
private Filter[] additionalFilters;
|
||||||
private int currentPosition = 0;
|
private int currentPosition = 0;
|
||||||
|
@ -252,8 +229,6 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo
|
||||||
this.additionalFilters = additionalFilters;
|
this.additionalFilters = additionalFilters;
|
||||||
}
|
}
|
||||||
|
|
||||||
private VirtualFilterChain() {}
|
|
||||||
|
|
||||||
public void doFilter(ServletRequest request, ServletResponse response)
|
public void doFilter(ServletRequest request, ServletResponse response)
|
||||||
throws IOException, ServletException {
|
throws IOException, ServletException {
|
||||||
if (currentPosition == additionalFilters.length) {
|
if (currentPosition == additionalFilters.length) {
|
||||||
|
@ -276,4 +251,5 @@ public class FilterChainProxy implements Filter, InitializingBean, ApplicationCo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
package org.springframework.security.util;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author luke
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class RegexUrlPathMatcher implements UrlMatcher {
|
||||||
|
private static final Log logger = LogFactory.getLog(RegexUrlPathMatcher.class);
|
||||||
|
|
||||||
|
private boolean convertUrlToLowercaseBeforeComparison = true;
|
||||||
|
|
||||||
|
public Object compile(String path) {
|
||||||
|
return Pattern.compile(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConvertUrlToLowercaseBeforeComparison(boolean convertUrlToLowercaseBeforeComparison) {
|
||||||
|
this.convertUrlToLowercaseBeforeComparison = convertUrlToLowercaseBeforeComparison;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean pathMatchesUrl(Object compiledPath, String url) {
|
||||||
|
Pattern pattern = (Pattern)compiledPath;
|
||||||
|
|
||||||
|
if (convertUrlToLowercaseBeforeComparison) {
|
||||||
|
url = url.toLowerCase();
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("Converted URL to lowercase, from: '" + url + "'; to: '" + url + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pattern.matcher(url).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUniversalMatchPattern() {
|
||||||
|
return "/.*";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package org.springframework.security.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strategy for deciding whether configured path matches a submitted candidate URL.
|
||||||
|
*
|
||||||
|
* @author luke
|
||||||
|
* @version $Id$
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public interface UrlMatcher {
|
||||||
|
|
||||||
|
Object compile(String urlPattern);
|
||||||
|
|
||||||
|
boolean pathMatchesUrl(Object compiledUrlPattern, String url);
|
||||||
|
|
||||||
|
/** Returns the path which matches every URL */
|
||||||
|
String getUniversalMatchPattern();
|
||||||
|
}
|
|
@ -15,23 +15,20 @@
|
||||||
|
|
||||||
package org.springframework.security.util;
|
package org.springframework.security.util;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
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;
|
||||||
import org.springframework.security.ConfigAttribute;
|
import org.springframework.security.ConfigAttribute;
|
||||||
import org.springframework.security.ConfigAttributeDefinition;
|
import org.springframework.security.ConfigAttributeDefinition;
|
||||||
import org.springframework.security.MockApplicationContext;
|
import org.springframework.security.MockApplicationContext;
|
||||||
import org.springframework.security.MockFilterConfig;
|
import org.springframework.security.MockFilterConfig;
|
||||||
|
|
||||||
import org.springframework.security.intercept.web.FilterInvocationDefinitionSource;
|
|
||||||
import org.springframework.security.intercept.web.MockFilterInvocationDefinitionSource;
|
import org.springframework.security.intercept.web.MockFilterInvocationDefinitionSource;
|
||||||
import org.springframework.security.intercept.web.PathBasedFilterInvocationDefinitionMap;
|
import org.springframework.security.intercept.web.PathBasedFilterInvocationDefinitionMap;
|
||||||
|
|
||||||
import org.springframework.context.ApplicationContext;
|
|
||||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
|
||||||
|
|
||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
|
||||||
import org.springframework.mock.web.MockHttpServletResponse;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link FilterChainProxy}.
|
* Tests {@link FilterChainProxy}.
|
||||||
|
@ -40,32 +37,30 @@ import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public class FilterChainProxyTests extends TestCase {
|
public class FilterChainProxyTests {
|
||||||
//~ Constructors ===================================================================================================
|
private ClassPathXmlApplicationContext appCtx;
|
||||||
|
|
||||||
// ===========================================================
|
|
||||||
public FilterChainProxyTests() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public FilterChainProxyTests(String arg0) {
|
|
||||||
super(arg0);
|
|
||||||
}
|
|
||||||
|
|
||||||
//~ Methods ========================================================================================================
|
//~ Methods ========================================================================================================
|
||||||
|
|
||||||
// ================================================================
|
@Before
|
||||||
public static void main(String[] args) {
|
public void loadContext() {
|
||||||
junit.textui.TestRunner.run(FilterChainProxyTests.class);
|
appCtx = new ClassPathXmlApplicationContext("org/springframework/security/util/filtertest-valid.xml");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDetectsFilterInvocationDefinitionSourceThatDoesNotReturnAllConfigAttributes()
|
@After
|
||||||
throws Exception {
|
public void closeContext() {
|
||||||
|
if (appCtx != null) {
|
||||||
|
appCtx.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDetectsFilterInvocationDefinitionSourceThatDoesNotReturnAllConfigAttributes() throws Exception {
|
||||||
FilterChainProxy filterChainProxy = new FilterChainProxy();
|
FilterChainProxy filterChainProxy = new FilterChainProxy();
|
||||||
filterChainProxy.setApplicationContext(MockApplicationContext.getContext());
|
filterChainProxy.setApplicationContext(MockApplicationContext.getContext());
|
||||||
filterChainProxy.setFilterInvocationDefinitionSource(new MockFilterInvocationDefinitionSource(false, false));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
filterChainProxy.setFilterInvocationDefinitionSource(new MockFilterInvocationDefinitionSource(false, false));
|
||||||
filterChainProxy.afterPropertiesSet();
|
filterChainProxy.afterPropertiesSet();
|
||||||
fail("Should have thrown IllegalArgumentException");
|
fail("Should have thrown IllegalArgumentException");
|
||||||
} catch (IllegalArgumentException expected) {
|
} catch (IllegalArgumentException expected) {
|
||||||
|
@ -74,8 +69,8 @@ public class FilterChainProxyTests extends TestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDetectsIfConfigAttributeDoesNotReturnValueForGetAttributeMethod()
|
@Test
|
||||||
throws Exception {
|
public void testDetectsIfConfigAttributeDoesNotReturnValueForGetAttributeMethod() throws Exception {
|
||||||
FilterChainProxy filterChainProxy = new FilterChainProxy();
|
FilterChainProxy filterChainProxy = new FilterChainProxy();
|
||||||
filterChainProxy.setApplicationContext(MockApplicationContext.getContext());
|
filterChainProxy.setApplicationContext(MockApplicationContext.getContext());
|
||||||
|
|
||||||
|
@ -86,9 +81,9 @@ public class FilterChainProxyTests extends TestCase {
|
||||||
fids.addSecureUrl("/**", cad);
|
fids.addSecureUrl("/**", cad);
|
||||||
|
|
||||||
filterChainProxy.setFilterInvocationDefinitionSource(fids);
|
filterChainProxy.setFilterInvocationDefinitionSource(fids);
|
||||||
filterChainProxy.afterPropertiesSet();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
filterChainProxy.afterPropertiesSet();
|
||||||
filterChainProxy.init(new MockFilterConfig());
|
filterChainProxy.init(new MockFilterConfig());
|
||||||
fail("Should have thrown IllegalArgumentException");
|
fail("Should have thrown IllegalArgumentException");
|
||||||
} catch (IllegalArgumentException expected) {
|
} catch (IllegalArgumentException expected) {
|
||||||
|
@ -97,8 +92,8 @@ public class FilterChainProxyTests extends TestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDetectsMissingFilterInvocationDefinitionSource()
|
@Test
|
||||||
throws Exception {
|
public void testDetectsMissingFilterInvocationDefinitionSource() throws Exception {
|
||||||
FilterChainProxy filterChainProxy = new FilterChainProxy();
|
FilterChainProxy filterChainProxy = new FilterChainProxy();
|
||||||
filterChainProxy.setApplicationContext(MockApplicationContext.getContext());
|
filterChainProxy.setApplicationContext(MockApplicationContext.getContext());
|
||||||
|
|
||||||
|
@ -106,12 +101,11 @@ public class FilterChainProxyTests extends TestCase {
|
||||||
filterChainProxy.afterPropertiesSet();
|
filterChainProxy.afterPropertiesSet();
|
||||||
fail("Should have thrown IllegalArgumentException");
|
fail("Should have thrown IllegalArgumentException");
|
||||||
} catch (IllegalArgumentException expected) {
|
} catch (IllegalArgumentException expected) {
|
||||||
assertEquals("filterInvocationDefinitionSource must be specified", expected.getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testDoNotFilter() throws Exception {
|
public void testDoNotFilter() throws Exception {
|
||||||
ApplicationContext appCtx = new ClassPathXmlApplicationContext("org/springframework/security/util/filtertest-valid.xml");
|
|
||||||
FilterChainProxy filterChainProxy = (FilterChainProxy) appCtx.getBean("filterChain", FilterChainProxy.class);
|
FilterChainProxy filterChainProxy = (FilterChainProxy) appCtx.getBean("filterChain", FilterChainProxy.class);
|
||||||
MockFilter filter = (MockFilter) appCtx.getBean("mockFilter", MockFilter.class);
|
MockFilter filter = (MockFilter) appCtx.getBean("mockFilter", MockFilter.class);
|
||||||
|
|
||||||
|
@ -127,16 +121,22 @@ public class FilterChainProxyTests extends TestCase {
|
||||||
assertFalse(filter.isWasDestroyed());
|
assertFalse(filter.isWasDestroyed());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGettersSetters() {
|
@Test
|
||||||
FilterChainProxy filterChainProxy = new FilterChainProxy();
|
public void normalOperation() throws Exception {
|
||||||
FilterInvocationDefinitionSource fids = new MockFilterInvocationDefinitionSource(false, false);
|
doNormalOperation((FilterChainProxy) appCtx.getBean("filterChain", FilterChainProxy.class));
|
||||||
filterChainProxy.setFilterInvocationDefinitionSource(fids);
|
|
||||||
assertEquals(fids, filterChainProxy.getFilterInvocationDefinitionSource());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testNormalOperation() throws Exception {
|
@Test
|
||||||
ApplicationContext appCtx = new ClassPathXmlApplicationContext("org/springframework/security/util/filtertest-valid.xml");
|
public void normalOperationWithNewConfig() throws Exception {
|
||||||
FilterChainProxy filterChainProxy = (FilterChainProxy) appCtx.getBean("filterChain", FilterChainProxy.class);
|
doNormalOperation((FilterChainProxy) appCtx.getBean("newFilterChainProxy", FilterChainProxy.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void normalOperationWithNewConfigRegex() throws Exception {
|
||||||
|
doNormalOperation((FilterChainProxy) appCtx.getBean("newFilterChainProxyRegex", FilterChainProxy.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doNormalOperation(FilterChainProxy filterChainProxy) throws Exception {
|
||||||
MockFilter filter = (MockFilter) appCtx.getBean("mockFilter", MockFilter.class);
|
MockFilter filter = (MockFilter) appCtx.getBean("mockFilter", MockFilter.class);
|
||||||
assertFalse(filter.isWasInitialized());
|
assertFalse(filter.isWasInitialized());
|
||||||
assertFalse(filter.isWasDoFiltered());
|
assertFalse(filter.isWasDoFiltered());
|
||||||
|
@ -165,6 +165,7 @@ public class FilterChainProxyTests extends TestCase {
|
||||||
assertTrue(filter.isWasInitialized());
|
assertTrue(filter.isWasInitialized());
|
||||||
assertTrue(filter.isWasDoFiltered());
|
assertTrue(filter.isWasDoFiltered());
|
||||||
assertTrue(filter.isWasDestroyed());
|
assertTrue(filter.isWasDestroyed());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//~ Inner Classes ==================================================================================================
|
//~ Inner Classes ==================================================================================================
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
|
|
||||||
<!--
|
<!--
|
||||||
* Copyright 2004 Acegi Technology Pty Limited
|
* Copyright 2004 Acegi Technology Pty Limited
|
||||||
*
|
*
|
||||||
|
@ -18,8 +18,11 @@
|
||||||
*
|
*
|
||||||
* $Id$
|
* $Id$
|
||||||
-->
|
-->
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
<beans>
|
xmlns:sec="http://www.springframework.org/schema/security"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
|
||||||
|
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">
|
||||||
|
|
||||||
<bean id="mockFilter" class="org.springframework.security.util.MockFilter"/>
|
<bean id="mockFilter" class="org.springframework.security.util.MockFilter"/>
|
||||||
|
|
||||||
|
@ -39,4 +42,22 @@
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="newFilterChainProxy" class="org.springframework.security.util.FilterChainProxy">
|
||||||
|
<sec:filter-chain-map pathType="ant">
|
||||||
|
<sec:filter-chain pattern="/foo/**" filters="mockFilter"/>
|
||||||
|
<sec:filter-chain pattern="/some/other/path/**" filters="sif,mockFilter,mockFilter2"/>
|
||||||
|
<sec:filter-chain pattern="/do/not/filter" filters="none"/>
|
||||||
|
</sec:filter-chain-map>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="newFilterChainProxyRegex" class="org.springframework.security.util.FilterChainProxy">
|
||||||
|
<sec:filter-chain-map pathType="regex">
|
||||||
|
<sec:filter-chain pattern="\A/foo/.*\Z" filters="mockFilter"/>
|
||||||
|
<sec:filter-chain pattern="\A/some/other/path/.*\Z" filters="sif,mockFilter,mockFilter2"/>
|
||||||
|
<sec:filter-chain pattern="\A/do/not/filter\Z" filters="none"/>
|
||||||
|
</sec:filter-chain-map>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="sif" class="org.springframework.security.context.HttpSessionContextIntegrationFilter"/>
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
|
Loading…
Reference in New Issue