SEC-1657: Corresponding namespace updates to use SecurityFilterChain list in place of filterChainMap.

This commit is contained in:
Luke Taylor 2011-04-23 22:28:19 +01:00
parent 37d0454fd7
commit 04dc65c8fe
17 changed files with 258 additions and 174 deletions

View File

@ -25,6 +25,7 @@ public abstract class BeanIds {
public static final String METHOD_ACCESS_MANAGER = PREFIX + "defaultMethodAccessManager";
public static final String FILTER_CHAIN_PROXY = PREFIX + "filterChainProxy";
public static final String FILTER_CHAINS = PREFIX + "filterChains";
public static final String METHOD_SECURITY_METADATA_SOURCE_ADVISOR = PREFIX + "methodSecurityMetadataSourceAdvisor";
public static final String EMBEDDED_APACHE_DS = PREFIX + "apacheDirectoryServerContainer";

View File

@ -1,8 +1,5 @@
package org.springframework.security.config;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.config.BeanDefinition;
@ -15,6 +12,7 @@ import org.springframework.security.config.authentication.AuthenticationManagerB
import org.springframework.security.config.authentication.AuthenticationProviderBeanDefinitionParser;
import org.springframework.security.config.authentication.JdbcUserServiceBeanDefinitionParser;
import org.springframework.security.config.authentication.UserServiceBeanDefinitionParser;
import org.springframework.security.config.http.FilterChainBeanDefinitionParser;
import org.springframework.security.config.http.FilterChainMapBeanDefinitionDecorator;
import org.springframework.security.config.http.FilterInvocationSecurityMetadataSourceParser;
import org.springframework.security.config.http.HttpFirewallBeanDefinitionParser;
@ -30,6 +28,8 @@ import org.springframework.util.ClassUtils;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import java.util.*;
/**
* Parses elements from the "security" namespace (http://www.springframework.org/schema/security).
*
@ -76,7 +76,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
if (parser == null) {
if (Elements.HTTP.equals(name) || Elements.FILTER_SECURITY_METADATA_SOURCE.equals(name) ||
Elements.FILTER_CHAIN_MAP.equals(name)) {
Elements.FILTER_CHAIN_MAP.equals(name) || Elements.FILTER_CHAIN.equals(name)) {
reportMissingWebClasses(name, pc, element);
} else {
reportUnsupportedNodeType(name, pc, element);
@ -147,6 +147,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
parsers.put(Elements.HTTP_FIREWALL, new HttpFirewallBeanDefinitionParser());
parsers.put(Elements.FILTER_INVOCATION_DEFINITION_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
parsers.put(Elements.FILTER_SECURITY_METADATA_SOURCE, new FilterInvocationSecurityMetadataSourceParser());
parsers.put(Elements.FILTER_CHAIN, new FilterChainBeanDefinitionParser());
filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator();
}
}

View File

@ -1,7 +1,7 @@
package org.springframework.security.config.debug;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.util.RequestMatcher;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.web.filter.OncePerRequestFilter;
@ -13,8 +13,7 @@ import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* Spring Security debugging filter.
@ -28,12 +27,10 @@ import java.util.Map;
*/
class DebugFilter extends OncePerRequestFilter {
private final FilterChainProxy fcp;
private final Map<RequestMatcher, List<Filter>> filterChainMap;
private final Logger logger = new Logger();
public DebugFilter(FilterChainProxy fcp) {
this.fcp = fcp;
this.filterChainMap = fcp.getFilterChainMap();
}
@Override
@ -67,11 +64,9 @@ class DebugFilter extends OncePerRequestFilter {
}
private List<Filter> getFilters(HttpServletRequest request) {
for (Map.Entry<RequestMatcher, List<Filter>> entry : filterChainMap.entrySet()) {
RequestMatcher matcher = entry.getKey();
if (matcher.matches(request)) {
return entry.getValue();
for (SecurityFilterChain chain : fcp.getFilterChains()) {
if (chain.matches(request)) {
return chain.getFilters();
}
}

View File

@ -1,7 +1,6 @@
package org.springframework.security.config.http;
import java.util.Collection;
import java.util.List;
import java.util.*;
import javax.servlet.Filter;
@ -12,6 +11,7 @@ import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
@ -29,9 +29,23 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
private final Log logger = LogFactory.getLog(getClass());
public void validate(FilterChainProxy fcp) {
for(List<Filter> filters : fcp.getFilterChainMap().values()) {
checkLoginPageIsntProtected(fcp, filters);
checkFilterStack(filters);
for(SecurityFilterChain filterChain : fcp.getFilterChains()) {
checkLoginPageIsntProtected(fcp, filterChain.getFilters());
checkFilterStack(filterChain.getFilters());
}
checkForDuplicateMatchers(new ArrayList<SecurityFilterChain>(fcp.getFilterChains()));
}
private void checkForDuplicateMatchers(List<SecurityFilterChain> chains) {
SecurityFilterChain chain = chains.remove(0);
for (SecurityFilterChain test : chains) {
if (chain.getRequestMatcher().equals(test.getRequestMatcher())) {
throw new IllegalArgumentException("The FilterChainProxy contains two filter chains using the" +
" matcher " + chain.getRequestMatcher() + ". If you are using multiple <http> namespace " +
"elements, you must use a 'pattern' attribute to define the request patterns to which they apply.");
}
}
}

View File

@ -0,0 +1,45 @@
package org.springframework.security.config.http;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
import java.util.*;
/**
* @author Luke Taylor
*/
public class FilterChainBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class getBeanClass(Element element) {
return SecurityFilterChain.class;
}
@Override
protected void doParse(Element elt, BeanDefinitionBuilder builder) {
MatcherType matcherType = MatcherType.fromElement(elt);
String path = elt.getAttribute(HttpSecurityBeanDefinitionParser.ATT_PATH_PATTERN);
String filters = elt.getAttribute(HttpSecurityBeanDefinitionParser.ATT_FILTERS);
builder.addConstructorArgValue(matcherType.createMatcher(path, null));
if (filters.equals(HttpSecurityBeanDefinitionParser.OPT_FILTERS_NONE)) {
builder.addConstructorArgValue(Collections.EMPTY_LIST);
} else {
String[] filterBeanNames = StringUtils.tokenizeToStringArray(filters, ",");
ManagedList<RuntimeBeanReference> filterChain = new ManagedList<RuntimeBeanReference>(filterBeanNames.length);
for (String name : filterBeanNames) {
filterChain.add(new RuntimeBeanReference(name));
}
builder.addConstructorArgValue(filterChain);
}
}
}

View File

@ -95,13 +95,13 @@ class HttpConfigurationBuilder {
private BeanReference fsi;
private BeanReference requestCache;
public HttpConfigurationBuilder(Element element, ParserContext pc, MatcherType matcherType,
public HttpConfigurationBuilder(Element element, ParserContext pc,
String portMapperName, BeanReference authenticationManager) {
this.httpElt = element;
this.pc = pc;
this.portMapperName = portMapperName;
this.matcherType = matcherType;
this.matcherType = MatcherType.fromElement(element);
interceptUrls = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_URL);
for (Element urlElt : interceptUrls) {
@ -339,7 +339,7 @@ class HttpConfigurationBuilder {
servApiFilter = new RootBeanDefinition(SecurityContextHolderAwareRequestFilter.class);
}
}
// Adds the jaas-api integration filter if required
private void createJaasApiFilter() {
final String ATT_JAAS_API_PROVISION = "jaas-api-provision";
@ -354,7 +354,7 @@ class HttpConfigurationBuilder {
jaasApiFilter = new RootBeanDefinition(JaasApiIntegrationFilter.class);
}
}
private void createChannelProcessingFilter() {
ManagedMap<BeanDefinition,BeanDefinition> channelRequestMap = parseInterceptUrlsForChannelSecurity();
@ -534,7 +534,7 @@ class HttpConfigurationBuilder {
if (jaasApiFilter != null) {
filters.add(new OrderDecorator(jaasApiFilter, JAAS_API_SUPPORT_FILTER));
}
if (sfpf != null) {
filters.add(new OrderDecorator(sfpf, SESSION_MANAGEMENT_FILTER));
}

View File

@ -1,18 +1,13 @@
package org.springframework.security.config.http;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.config.BeanIds;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
import java.util.*;
/**
* Injects the supplied {@code HttpFirewall} bean reference into the {@code FilterChainProxy}.
*
@ -28,9 +23,7 @@ public class HttpFirewallBeanDefinitionParser implements BeanDefinitionParser {
}
// Ensure the FCP is registered.
HttpSecurityBeanDefinitionParser.registerFilterChainProxy(pc,
new ManagedMap<BeanDefinition, BeanReference>(),
pc.extractSource(element));
HttpSecurityBeanDefinitionParser.registerFilterChainProxyIfNecessary(pc, pc.extractSource(element));
BeanDefinition filterChainProxy = pc.getRegistry().getBeanDefinition(BeanIds.FILTER_CHAIN_PROXY);
filterChainProxy.getPropertyValues().addPropertyValue("firewall", new RuntimeBeanReference(ref));

View File

@ -1,10 +1,5 @@
package org.springframework.security.config.http;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanMetadataElement;
@ -13,12 +8,10 @@ import org.springframework.beans.factory.config.BeanReference;
import org.springframework.beans.factory.config.ListFactoryBean;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
@ -29,11 +22,14 @@ import org.springframework.security.config.BeanIds;
import org.springframework.security.config.Elements;
import org.springframework.security.config.authentication.AuthenticationManagerFactoryBean;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.AnyRequestMatcher;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
import java.util.*;
/**
* Sets up HTTP security: filter stack and protected URLs.
*
@ -67,33 +63,29 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
* By the end of this method, the default <tt>FilterChainProxy</tt> bean should have been registered and will have
* the map of filter chains defined, with the "universal" match pattern mapped to the list of beans which have been parsed here.
*/
@SuppressWarnings({"unchecked"})
public BeanDefinition parse(Element element, ParserContext pc) {
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element));
pc.pushContainingComponent(compositeDef);
MatcherType matcherType = MatcherType.fromElement(element);
ManagedMap<BeanDefinition, BeanReference> filterChainMap = new ManagedMap<BeanDefinition, BeanReference>();
registerFilterChainProxyIfNecessary(pc, pc.extractSource(element));
String filterChainPattern = element.getAttribute(ATT_PATH_PATTERN);
// Obtain the filter chains and add the new chain to it
BeanDefinition listFactoryBean = pc.getRegistry().getBeanDefinition(BeanIds.FILTER_CHAINS);
List<BeanReference> filterChains = (List<BeanReference>)
listFactoryBean.getPropertyValues().getPropertyValue("sourceList").getValue();
BeanDefinition filterChainMatcher;
if (StringUtils.hasText(filterChainPattern)) {
filterChainMatcher = matcherType.createMatcher(filterChainPattern, null);
} else {
filterChainMatcher = new RootBeanDefinition(AnyRequestMatcher.class);
}
filterChainMap.put(filterChainMatcher, createFilterChain(element, pc, matcherType));
registerFilterChainProxy(pc, filterChainMap, pc.extractSource(element));
filterChains.add(createFilterChain(element, pc));
pc.popAndRegisterContainingComponent();
return null;
}
BeanReference createFilterChain(Element element, ParserContext pc, MatcherType matcherType) {
/**
* Creates the {@code SecurityFilterChain} bean from an &lt;http&gt; element.
*/
private BeanReference createFilterChain(Element element, ParserContext pc) {
boolean secured = !OPT_SECURITY_NONE.equals(element.getAttribute(ATT_SECURED));
if (!secured) {
@ -109,7 +101,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
}
}
return createFilterListBean(element, pc, Collections.emptyList());
return createSecurityFilterChainBean(element, pc, Collections.emptyList());
}
final String portMapperName = createPortMapper(element, pc);
@ -117,7 +109,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
ManagedList<BeanReference> authenticationProviders = new ManagedList<BeanReference>();
BeanReference authenticationManager = createAuthenticationManager(element, pc, authenticationProviders);
HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, pc, matcherType,
HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, pc,
portMapperName, authenticationManager);
AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, pc,
@ -135,27 +127,41 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
Collections.sort(unorderedFilterChain, new OrderComparator());
checkFilterChainOrder(unorderedFilterChain, pc, pc.extractSource(element));
// The list of filter beans
List<BeanMetadataElement> filterChain = new ManagedList<BeanMetadataElement>();
for (OrderDecorator od : unorderedFilterChain) {
filterChain.add(od.bean);
}
return createFilterListBean(element, pc, filterChain);
return createSecurityFilterChainBean(element, pc, filterChain);
}
private BeanReference createFilterListBean(Element element, ParserContext pc, List<?> filterChain) {
BeanDefinition listFactoryBean = new RootBeanDefinition(ListFactoryBean.class);
private BeanReference createSecurityFilterChainBean(Element element, ParserContext pc, List<?> filterChain) {
BeanDefinition filterChainMatcher;
String filterChainPattern = element.getAttribute(ATT_PATH_PATTERN);
if (StringUtils.hasText(filterChainPattern)) {
filterChainMatcher = MatcherType.fromElement(element).createMatcher(filterChainPattern, null);
} else {
filterChainMatcher = new RootBeanDefinition(AnyRequestMatcher.class);
}
BeanDefinitionBuilder filterChainBldr = BeanDefinitionBuilder.rootBeanDefinition(SecurityFilterChain.class);
filterChainBldr.addConstructorArgValue(filterChainMatcher);
filterChainBldr.addConstructorArgValue(filterChain);
BeanDefinition filterChainBean = filterChainBldr.getBeanDefinition();
String id = element.getAttribute("name");
if (!StringUtils.hasText(id)) {
id = element.getAttribute("id");
if (!StringUtils.hasText(id)) {
id = pc.getReaderContext().generateBeanName(listFactoryBean);
id = pc.getReaderContext().generateBeanName(filterChainBean);
}
}
listFactoryBean.getPropertyValues().add("sourceList", filterChain);
pc.registerBeanComponent(new BeanComponentDefinition(listFactoryBean, id));
pc.registerBeanComponent(new BeanComponentDefinition(filterChainBean, id));
return new RuntimeBeanReference(id);
}
@ -262,35 +268,22 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
return customFilters;
}
@SuppressWarnings("unchecked")
static void registerFilterChainProxy(ParserContext pc, Map<BeanDefinition, BeanReference> filterChainMap, Object source) {
static void registerFilterChainProxyIfNecessary(ParserContext pc, Object source) {
if (pc.getRegistry().containsBeanDefinition(BeanIds.FILTER_CHAIN_PROXY)) {
// Already registered. Obtain the filter chain map and add the new entries to it
BeanDefinition fcp = pc.getRegistry().getBeanDefinition(BeanIds.FILTER_CHAIN_PROXY);
Map existingFilterChainMap = (Map) fcp.getPropertyValues().getPropertyValue("filterChainMap").getValue();
for (BeanDefinition matcherBean : filterChainMap.keySet()) {
if (existingFilterChainMap.containsKey(matcherBean)) {
Map<Integer,ValueHolder> args = matcherBean.getConstructorArgumentValues().getIndexedArgumentValues();
String matcherError = args.size() == 2 ? args.get(0).getValue() + ", " +args.get(1).getValue() :
matcherBean.toString();
pc.getReaderContext().error("The filter chain map already contains this request matcher ["
+ matcherError + "]. If you are using multiple <http> namespace elements, you must use a 'pattern' attribute" +
" to define the request patterns to which they apply.", source);
}
}
existingFilterChainMap.putAll(filterChainMap);
} else {
// Not already registered, so register it
BeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder.rootBeanDefinition(FilterChainProxy.class);
fcpBldr.getRawBeanDefinition().setSource(source);
fcpBldr.addPropertyValue("filterChainMap", filterChainMap);
fcpBldr.addPropertyValue("filterChainValidator", new RootBeanDefinition(DefaultFilterChainValidator.class));
BeanDefinition fcpBean = fcpBldr.getBeanDefinition();
pc.registerBeanComponent(new BeanComponentDefinition(fcpBean, BeanIds.FILTER_CHAIN_PROXY));
pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);
return;
}
// Not already registered, so register the list of filter chains and the FilterChainProxy
BeanDefinition listFactoryBean = new RootBeanDefinition(ListFactoryBean.class);
listFactoryBean.getPropertyValues().add("sourceList", new ManagedList());
pc.registerBeanComponent(new BeanComponentDefinition(listFactoryBean, BeanIds.FILTER_CHAINS));
BeanDefinitionBuilder fcpBldr = BeanDefinitionBuilder.rootBeanDefinition(FilterChainProxy.class);
fcpBldr.getRawBeanDefinition().setSource(source);
fcpBldr.addConstructorArgReference(BeanIds.FILTER_CHAINS);
fcpBldr.addPropertyValue("filterChainValidator", new RootBeanDefinition(DefaultFilterChainValidator.class));
BeanDefinition fcpBean = fcpBldr.getBeanDefinition();
pc.registerBeanComponent(new BeanComponentDefinition(fcpBean, BeanIds.FILTER_CHAIN_PROXY));
pc.getRegistry().registerAlias(BeanIds.FILTER_CHAIN_PROXY, BeanIds.SPRING_SECURITY_FILTER_CHAIN);
}
}

View File

@ -443,7 +443,7 @@ filter-chain-map.attlist &=
request-matcher?
filter-chain =
## Used within filter-chain-map to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are used within a filter-chain-map element, the most specific patterns must be placed at the top of the list, with most general ones at the bottom.
## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with most general ones at the bottom.
element filter-chain {filter-chain.attlist, empty}
filter-chain.attlist &=
attribute pattern {xsd:token}

View File

@ -974,11 +974,7 @@
<xs:documentation>Used to explicitly configure a FilterChainProxy instance with a FilterChainMap</xs:documentation>
</xs:annotation><xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="filter-chain"><xs:annotation>
<xs:documentation>Used within filter-chain-map to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are used within a filter-chain-map element, the most specific patterns must be placed at the top of the list, with most general ones at the bottom.</xs:documentation>
</xs:annotation><xs:complexType>
<xs:attributeGroup ref="security:filter-chain.attlist"/>
</xs:complexType></xs:element>
<xs:element maxOccurs="unbounded" ref="security:filter-chain"/>
</xs:sequence>
<xs:attributeGroup ref="security:filter-chain-map.attlist"/>
</xs:complexType></xs:element>
@ -1004,7 +1000,11 @@
</xs:simpleType>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="filter-chain"><xs:annotation>
<xs:documentation>Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with most general ones at the bottom.</xs:documentation>
</xs:annotation><xs:complexType>
<xs:attributeGroup ref="security:filter-chain.attlist"/>
</xs:complexType></xs:element>
<xs:attributeGroup name="filter-chain.attlist">
<xs:attribute name="pattern" use="required" type="xs:token"/>
<xs:attribute name="filters" use="required" type="xs:token"/>

View File

@ -9,7 +9,7 @@
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="elts-to-inline">
<xsl:text>,access-denied-handler,anonymous,session-management,concurrency-control,after-invocation-provider,authentication-provider,ldap-authentication-provider,user,port-mapping,openid-login,expression-handler,filter-chain,form-login,http-basic,intercept-url,logout,password-encoder,port-mappings,port-mapper,password-compare,protect,protect-pointcut,pre-post-annotation-handling,pre-invocation-advice,post-invocation-advice,invocation-attribute-factory,remember-me,salt-source,x509,</xsl:text>
<xsl:text>,access-denied-handler,anonymous,session-management,concurrency-control,after-invocation-provider,authentication-provider,ldap-authentication-provider,user,port-mapping,openid-login,expression-handler,form-login,http-basic,intercept-url,logout,password-encoder,port-mappings,port-mapper,password-compare,protect,protect-pointcut,pre-post-annotation-handling,pre-invocation-advice,post-invocation-advice,invocation-attribute-factory,remember-me,salt-source,x509,</xsl:text>
</xsl:variable>
<xsl:template match="xs:element">

View File

@ -4,6 +4,8 @@ import org.springframework.beans.factory.parsing.BeanDefinitionParsingException
import org.springframework.security.config.BeanIds
import org.springframework.security.web.FilterChainProxy
import org.junit.Assert
import org.springframework.beans.factory.BeanCreationException
import org.springframework.security.web.SecurityFilterChain
/**
* Tests scenarios with multiple &lt;http&gt; elements.
@ -22,11 +24,11 @@ class MultiHttpBlockConfigTests extends AbstractHttpConfigTests {
}
createAppContext()
FilterChainProxy fcp = appContext.getBean(BeanIds.FILTER_CHAIN_PROXY)
Map filterChains = fcp.getFilterChainMap();
def filterChains = fcp.getFilterChains();
then:
filterChains.size() == 2
(filterChains.keySet() as List)[0].pattern == '/stateless/**'
filterChains[0].requestMatcher.pattern == '/stateless/**'
}
def duplicateHttpElementsAreRejected () {
@ -39,7 +41,8 @@ class MultiHttpBlockConfigTests extends AbstractHttpConfigTests {
}
createAppContext()
then:
thrown(BeanDefinitionParsingException)
BeanCreationException e = thrown()
e.cause.cause instanceof IllegalArgumentException
}
def duplicatePatternsAreRejected () {
@ -52,7 +55,8 @@ class MultiHttpBlockConfigTests extends AbstractHttpConfigTests {
}
createAppContext()
then:
thrown(BeanDefinitionParsingException)
BeanCreationException e = thrown()
e.cause instanceof IllegalArgumentException
}
@ -64,9 +68,8 @@ class MultiHttpBlockConfigTests extends AbstractHttpConfigTests {
'form-login'()
}
createAppContext()
def fcp = appContext.getBean(BeanIds.FILTER_CHAIN_PROXY)
List filterChains = fcp.getFilterChainMap().values() as List;
List basicChain = filterChains[0];
FilterChainProxy fcp = appContext.getBean(BeanIds.FILTER_CHAIN_PROXY)
SecurityFilterChain basicChain = fcp.filterChains[0];
expect:
Assert.assertSame (basicChain, appContext.getBean('basic'))

View File

@ -34,6 +34,7 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
@ -109,10 +110,10 @@ public class FilterChainProxyConfigTests {
public void mixingPatternsAndPlaceholdersDoesntCauseOrderingIssues() throws Exception {
FilterChainProxy fcp = appCtx.getBean("sec1235FilterChainProxy", FilterChainProxy.class);
RequestMatcher[] matchers = fcp.getFilterChainMap().keySet().toArray(new RequestMatcher[fcp.getFilterChainMap().keySet().size()]);
assertEquals("/login*", ((AntPathRequestMatcher)matchers[0]).getPattern());
assertEquals("/logout", ((AntPathRequestMatcher)matchers[1]).getPattern());
assertTrue(matchers[2] instanceof AnyRequestMatcher);
List<SecurityFilterChain> chains = fcp.getFilterChains();
assertEquals("/login*", ((AntPathRequestMatcher)chains.get(0).getRequestMatcher()).getPattern());
assertEquals("/logout", ((AntPathRequestMatcher)chains.get(1).getRequestMatcher()).getPattern());
assertTrue(chains.get(2).getRequestMatcher() instanceof AnyRequestMatcher);
}
private void checkPathAndFilterOrder(FilterChainProxy filterChainProxy) throws Exception {

View File

@ -43,13 +43,21 @@ public class InvalidConfigurationTests {
try {
setContext("<http auto-config='true' />");
} catch (BeanCreationException e) {
assertTrue(e.getCause().getCause() instanceof NoSuchBeanDefinitionException);
NoSuchBeanDefinitionException nsbe = (NoSuchBeanDefinitionException) e.getCause().getCause();
Throwable cause = ultimateCause(e);
assertTrue(cause instanceof NoSuchBeanDefinitionException);
NoSuchBeanDefinitionException nsbe = (NoSuchBeanDefinitionException) cause;
assertEquals(BeanIds.AUTHENTICATION_MANAGER, nsbe.getBeanName());
assertTrue(nsbe.getMessage().endsWith(AuthenticationManagerFactoryBean.MISSING_BEAN_ERROR_MESSAGE));
}
}
private Throwable ultimateCause(Throwable e) {
if (e.getCause() == null) {
return e;
}
return ultimateCause(e.getCause());
}
private void setContext(String context) {
appContext = new InMemoryXmlApplicationContext(context);
}

View File

@ -19,9 +19,12 @@
-->
<beans default-lazy-init="true" xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:util="http://www.springframework.org/schema/util"
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-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<bean id="mockFilter" class="org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter"/>
@ -45,44 +48,52 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc
<bean id="mockNotAFilter" class="org.springframework.security.web.util.AnyRequestMatcher"/>
<bean id="filterChain" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/foo/**" filters="mockFilter"/>
<sec:filter-chain pattern="/some/other/path/**" filters="mockFilter"/>
<sec:filter-chain pattern="/do/not/filter" filters="none"/>
</sec:filter-chain-map>
<constructor-arg>
<util:list>
<sec:filter-chain pattern="/foo/**" filters="mockFilter"/>
<sec:filter-chain pattern="/some/other/path/**" filters="mockFilter"/>
<sec:filter-chain pattern="/do/not/filter" filters="none"/>
</util:list>
</constructor-arg>
</bean>
<!-- TODO: Refactor to replace the above (SEC-1034: 'new' is now the only valid syntax) -->
<bean id="newFilterChainProxy" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="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 pattern="/**" filters="sif,apf,mockFilter"/>
</sec:filter-chain-map>
<constructor-arg>
<util:list>
<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 pattern="/**" filters="sif,apf,mockFilter"/>
</util:list>
</constructor-arg>
</bean>
<bean id="newFilterChainProxyNoDefaultPath" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/foo/**" filters="mockFilter"/>
<sec:filter-chain pattern="/*.bar" filters="mockFilter,mockFilter2"/>
</sec:filter-chain-map>
<constructor-arg>
<util:list>
<sec:filter-chain pattern="/foo/**" filters="mockFilter"/>
<sec:filter-chain pattern="/*.bar" filters="mockFilter,mockFilter2"/>
</util:list>
</constructor-arg>
</bean>
<bean id="newFilterChainProxyWrongPathOrder" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<constructor-arg>
<util:list>
<sec:filter-chain pattern="/foo/**" filters="mockFilter"/>
<sec:filter-chain pattern="/**" filters="
sif,
apf,
mockFilter"/>
<sec:filter-chain pattern="/some/other/path/**" filters="sif,mockFilter,mockFilter2"/>
</sec:filter-chain-map>
</util:list>
</constructor-arg>
</bean>
<bean id="newFilterChainProxyRegex" class="org.springframework.security.web.FilterChainProxy">
@ -97,60 +108,70 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />
<bean id="sec1235FilterChainProxy" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<sec:filter-chain pattern="${sec1235.pattern1}*" filters="sif,apf,mockFilter"/>
<sec:filter-chain pattern="${sec1235.pattern2}" filters="mockFilter2"/>
<sec:filter-chain pattern="/**" filters="sif"/>
</sec:filter-chain-map>
<constructor-arg>
<util:list>
<sec:filter-chain pattern="${sec1235.pattern1}*" filters="sif,apf,mockFilter"/>
<sec:filter-chain pattern="${sec1235.pattern2}" filters="mockFilter2"/>
<sec:filter-chain pattern="/**" filters="sif"/>
</util:list>
</constructor-arg>
</bean>
<bean id="newFilterChainProxyNonNamespace" class="org.springframework.security.web.FilterChainProxy">
<property name="filterChainMap">
<map>
<entry>
<key>
<constructor-arg>
<list>
<bean class="org.springframework.security.web.SecurityFilterChain">
<constructor-arg>
<bean class="org.springframework.security.web.util.AntPathRequestMatcher">
<constructor-arg value="/foo/**"/>
</bean>
</key>
<list>
<ref local="mockFilter"/>
</list>
</entry>
<entry>
<key>
</constructor-arg>
<constructor-arg>
<list>
<ref local="mockFilter"/>
</list>
</constructor-arg>
</bean>
<bean class="org.springframework.security.web.SecurityFilterChain">
<constructor-arg>
<bean class="org.springframework.security.web.util.AntPathRequestMatcher">
<constructor-arg value="/some/other/path/**"/>
</bean>
</key>
<list>
<ref local="sif"/>
<ref local="mockFilter"/>
<ref local="mockFilter2"/>
</list>
</entry>
<entry>
<key>
</constructor-arg>
<constructor-arg>
<list>
<ref local="sif"/>
<ref local="mockFilter"/>
<ref local="mockFilter2"/>
</list>
</constructor-arg>
</bean>
<bean class="org.springframework.security.web.SecurityFilterChain">
<constructor-arg>
<bean class="org.springframework.security.web.util.AntPathRequestMatcher">
<constructor-arg value="/do/not/filter*"/>
</bean>
</key>
<list/>
</entry>
<entry>
<key>
</constructor-arg>
<constructor-arg>
<list />
</constructor-arg>
</bean>
<bean class="org.springframework.security.web.SecurityFilterChain">
<constructor-arg>
<bean class="org.springframework.security.web.util.AntPathRequestMatcher">
<constructor-arg value="/**"/>
</bean>
</key>
<list>
<ref local="sif"/>
<ref local="apf"/>
<ref local="mockFilter"/>
</list>
</entry>
</map>
</property>
</constructor-arg>
<constructor-arg>
<list>
<ref local="sif"/>
<ref local="apf"/>
<ref local="mockFilter"/>
</list>
</constructor-arg>
</bean>
</list>
</constructor-arg>
</bean>
</beans>

View File

@ -32,7 +32,7 @@ public final class SecurityFilterChain {
public SecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) {
this.requestMatcher = requestMatcher;
this.filters = filters;
this.filters = new ArrayList<Filter>(filters);
}
public RequestMatcher getRequestMatcher() {

View File

@ -14,4 +14,13 @@ public final class AnyRequestMatcher implements RequestMatcher {
return true;
}
@Override
public boolean equals(Object obj) {
return obj instanceof AnyRequestMatcher;
}
@Override
public int hashCode() {
return 1;
}
}