SEC-1657: Corresponding namespace updates to use SecurityFilterChain list in place of filterChainMap.
This commit is contained in:
parent
37d0454fd7
commit
04dc65c8fe
|
@ -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";
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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 <http> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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 <http> 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'))
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue