SEC-640: Add namespace support for FilterInvocationDefinitionSource configuration
http://jira.springframework.org/browse/SEC-640
This commit is contained in:
parent
1bc863fce1
commit
33023565a8
|
@ -34,4 +34,5 @@ abstract class Elements {
|
|||
public static final String CUSTOM_FILTER = "custom-filter";
|
||||
public static final String CUSTOM_AUTH_RPOVIDER = "custom-authentication-provider";
|
||||
public static final String X509 = "x509";
|
||||
public static final String FILTER_INVOCATION_DEFINITION_SOURCE = "filter-invocation-definition-source";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package org.springframework.security.config;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.security.util.AntUrlPathMatcher;
|
||||
import org.springframework.security.util.UrlMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Allows for convenient creation of a {@link FilterInvocationDefinitionSource} bean for use with a FilterSecurityInterceptor.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class FilterInvocationDefinitionSourceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
protected String getBeanClassName(Element element) {
|
||||
return "org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource";
|
||||
}
|
||||
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
List interceptUrls = DomUtils.getChildElementsByTagName(element, "intercept-url");
|
||||
|
||||
// Check for attributes that aren't allowed in this context
|
||||
Iterator interceptUrlElts = interceptUrls.iterator();
|
||||
while(interceptUrlElts.hasNext()) {
|
||||
Element elt = (Element) interceptUrlElts.next();
|
||||
if (StringUtils.hasLength(elt.getAttribute(HttpSecurityBeanDefinitionParser.ATT_REQUIRES_CHANNEL))) {
|
||||
parserContext.getReaderContext().error("The attribute '" + HttpSecurityBeanDefinitionParser.ATT_REQUIRES_CHANNEL + "' isn't allowed here.", elt);
|
||||
}
|
||||
|
||||
if (StringUtils.hasLength(elt.getAttribute(HttpSecurityBeanDefinitionParser.ATT_FILTERS))) {
|
||||
parserContext.getReaderContext().error("The attribute '" + HttpSecurityBeanDefinitionParser.ATT_FILTERS + "' isn't allowed here.", elt);
|
||||
}
|
||||
}
|
||||
|
||||
UrlMatcher matcher = HttpSecurityBeanDefinitionParser.createUrlMatcher(element);
|
||||
boolean convertPathsToLowerCase = (matcher instanceof AntUrlPathMatcher) && matcher.requiresLowerCaseUrl();
|
||||
|
||||
LinkedHashMap requestMap = new LinkedHashMap();
|
||||
HttpSecurityBeanDefinitionParser.parseInterceptUrlsForFilterInvocationRequestMap(interceptUrls, requestMap,
|
||||
convertPathsToLowerCase, parserContext);
|
||||
|
||||
builder.addConstructorArg(matcher);
|
||||
builder.addConstructorArg(requestMap);
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import org.springframework.security.wrapper.SecurityContextHolderAwareRequestFil
|
|||
import org.springframework.security.context.HttpSessionContextIntegrationFilter;
|
||||
import org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource;
|
||||
import org.springframework.security.intercept.web.FilterSecurityInterceptor;
|
||||
import org.springframework.security.intercept.web.RequestKey;
|
||||
import org.springframework.security.securechannel.ChannelDecisionManagerImpl;
|
||||
import org.springframework.security.securechannel.ChannelProcessingFilter;
|
||||
import org.springframework.security.securechannel.InsecureChannelProcessor;
|
||||
|
@ -111,45 +112,9 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
|||
= BeanDefinitionBuilder.rootBeanDefinition(ExceptionTranslationFilter.class);
|
||||
|
||||
Map filterChainMap = new LinkedHashMap();
|
||||
|
||||
String patternType = element.getAttribute(ATT_PATH_TYPE);
|
||||
if (!StringUtils.hasText(patternType)) {
|
||||
patternType = DEF_PATH_TYPE_ANT;
|
||||
}
|
||||
|
||||
boolean useRegex = patternType.equals(OPT_PATH_TYPE_REGEX);
|
||||
|
||||
UrlMatcher matcher = new AntUrlPathMatcher();
|
||||
|
||||
if (useRegex) {
|
||||
matcher = new RegexUrlPathMatcher();
|
||||
}
|
||||
|
||||
// Deal with lowercase conversion requests
|
||||
String lowercaseComparisons = element.getAttribute(ATT_LOWERCASE_COMPARISONS);
|
||||
if (!StringUtils.hasText(lowercaseComparisons)) {
|
||||
lowercaseComparisons = null;
|
||||
}
|
||||
|
||||
|
||||
// Only change from the defaults if the attribute has been set
|
||||
if ("true".equals(lowercaseComparisons)) {
|
||||
if (useRegex) {
|
||||
((RegexUrlPathMatcher)matcher).setRequiresLowerCaseUrl(true);
|
||||
}
|
||||
// Default for ant is already to force lower case
|
||||
} else if ("false".equals(lowercaseComparisons)) {
|
||||
if (!useRegex) {
|
||||
((AntUrlPathMatcher)matcher).setRequiresLowerCaseUrl(false);
|
||||
}
|
||||
// Default for regex is no change
|
||||
}
|
||||
|
||||
DefaultFilterInvocationDefinitionSource interceptorFilterInvDefSource =
|
||||
new DefaultFilterInvocationDefinitionSource(matcher);
|
||||
DefaultFilterInvocationDefinitionSource channelFilterInvDefSource =
|
||||
new DefaultFilterInvocationDefinitionSource(matcher);
|
||||
|
||||
|
||||
UrlMatcher matcher = createUrlMatcher(element);
|
||||
|
||||
filterChainProxy.getPropertyValues().addPropertyValue("matcher", matcher);
|
||||
|
||||
// Add servlet-api integration filter if required
|
||||
|
@ -164,8 +129,6 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
|||
|
||||
filterChainProxy.getPropertyValues().addPropertyValue("filterChainMap", filterChainMap);
|
||||
|
||||
filterSecurityInterceptorBuilder.addPropertyValue("objectDefinitionSource", interceptorFilterInvDefSource);
|
||||
|
||||
// Set up the access manager and authentication mananger references for http
|
||||
String accessManagerId = element.getAttribute(ATT_ACCESS_MGR);
|
||||
|
||||
|
@ -182,13 +145,26 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
|||
// SEC-501 - should paths stored in request maps be converted to lower case
|
||||
// true if Ant path and using lower case
|
||||
boolean convertPathsToLowerCase = (matcher instanceof AntUrlPathMatcher) && matcher.requiresLowerCaseUrl();
|
||||
|
||||
parseInterceptUrls(DomUtils.getChildElementsByTagName(element, "intercept-url"),
|
||||
filterChainMap, interceptorFilterInvDefSource, channelFilterInvDefSource,
|
||||
|
||||
LinkedHashMap channelRequestMap = new LinkedHashMap();
|
||||
LinkedHashMap filterInvocationDefinitionMap = new LinkedHashMap();
|
||||
|
||||
List interceptUrlElts = DomUtils.getChildElementsByTagName(element, "intercept-url");
|
||||
parseInterceptUrlsForChannelSecurityAndFilterChain(interceptUrlElts, filterChainMap, channelRequestMap,
|
||||
convertPathsToLowerCase, parserContext);
|
||||
parseInterceptUrlsForFilterInvocationRequestMap(interceptUrlElts, filterInvocationDefinitionMap,
|
||||
convertPathsToLowerCase, parserContext);
|
||||
|
||||
DefaultFilterInvocationDefinitionSource interceptorFilterInvDefSource =
|
||||
new DefaultFilterInvocationDefinitionSource(matcher, filterInvocationDefinitionMap);
|
||||
DefaultFilterInvocationDefinitionSource channelFilterInvDefSource =
|
||||
new DefaultFilterInvocationDefinitionSource(matcher, channelRequestMap);
|
||||
|
||||
filterSecurityInterceptorBuilder.addPropertyValue("objectDefinitionSource", interceptorFilterInvDefSource);
|
||||
|
||||
|
||||
// Check if we need to register the channel processing beans
|
||||
if (((DefaultFilterInvocationDefinitionSource)channelFilterInvDefSource).getMapSize() > 0) {
|
||||
if (channelRequestMap.size() > 0) {
|
||||
// At least one channel requirement has been specified
|
||||
RootBeanDefinition channelFilter = new RootBeanDefinition(ChannelProcessingFilter.class);
|
||||
channelFilter.getPropertyValues().addPropertyValue("channelDecisionManager",
|
||||
|
@ -271,18 +247,103 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
static UrlMatcher createUrlMatcher(Element element) {
|
||||
String patternType = element.getAttribute(ATT_PATH_TYPE);
|
||||
if (!StringUtils.hasText(patternType)) {
|
||||
patternType = DEF_PATH_TYPE_ANT;
|
||||
}
|
||||
|
||||
boolean useRegex = patternType.equals(OPT_PATH_TYPE_REGEX);
|
||||
|
||||
UrlMatcher matcher = new AntUrlPathMatcher();
|
||||
|
||||
if (useRegex) {
|
||||
matcher = new RegexUrlPathMatcher();
|
||||
}
|
||||
|
||||
// Deal with lowercase conversion requests
|
||||
String lowercaseComparisons = element.getAttribute(ATT_LOWERCASE_COMPARISONS);
|
||||
if (!StringUtils.hasText(lowercaseComparisons)) {
|
||||
lowercaseComparisons = null;
|
||||
}
|
||||
|
||||
|
||||
// Only change from the defaults if the attribute has been set
|
||||
if ("true".equals(lowercaseComparisons)) {
|
||||
if (useRegex) {
|
||||
((RegexUrlPathMatcher)matcher).setRequiresLowerCaseUrl(true);
|
||||
}
|
||||
// Default for ant is already to force lower case
|
||||
} else if ("false".equals(lowercaseComparisons)) {
|
||||
if (!useRegex) {
|
||||
((AntUrlPathMatcher)matcher).setRequiresLowerCaseUrl(false);
|
||||
}
|
||||
// Default for regex is no change
|
||||
}
|
||||
|
||||
return matcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the intercept-url elements and populates the FilterChainProxy's filter chain Map and the
|
||||
* FilterInvocationDefinitionSource used in FilterSecurityInterceptor.
|
||||
* map used to create the FilterInvocationDefintionSource for the FilterSecurityInterceptor.
|
||||
*/
|
||||
private void parseInterceptUrls(List urlElts, Map filterChainMap,
|
||||
DefaultFilterInvocationDefinitionSource interceptorFilterInvDefSource,
|
||||
DefaultFilterInvocationDefinitionSource channelFilterInvDefSource,
|
||||
void parseInterceptUrlsForChannelSecurityAndFilterChain(List urlElts, Map filterChainMap, Map channelRequestMap,
|
||||
boolean useLowerCasePaths, ParserContext parserContext) {
|
||||
|
||||
Iterator urlEltsIterator = urlElts.iterator();
|
||||
ConfigAttributeEditor editor = new ConfigAttributeEditor();
|
||||
|
||||
while (urlEltsIterator.hasNext()) {
|
||||
Element urlElt = (Element) urlEltsIterator.next();
|
||||
|
||||
String path = urlElt.getAttribute(ATT_PATH_PATTERN);
|
||||
|
||||
if(!StringUtils.hasText(path)) {
|
||||
parserContext.getReaderContext().error("path attribute cannot be empty or null", urlElt);
|
||||
}
|
||||
|
||||
if (useLowerCasePaths) {
|
||||
path = path.toLowerCase();
|
||||
}
|
||||
|
||||
String requiredChannel = urlElt.getAttribute(ATT_REQUIRES_CHANNEL);
|
||||
|
||||
if (StringUtils.hasText(requiredChannel)) {
|
||||
String channelConfigAttribute = null;
|
||||
|
||||
if (requiredChannel.equals(OPT_REQUIRES_HTTPS)) {
|
||||
channelConfigAttribute = "REQUIRES_SECURE_CHANNEL";
|
||||
} else if (requiredChannel.equals(OPT_REQUIRES_HTTP)) {
|
||||
channelConfigAttribute = "REQUIRES_INSECURE_CHANNEL";
|
||||
} else if (requiredChannel.equals(OPT_ANY_CHANNEL)) {
|
||||
channelConfigAttribute = ChannelDecisionManagerImpl.ANY_CHANNEL;
|
||||
} else {
|
||||
parserContext.getReaderContext().error("Unsupported channel " + requiredChannel, urlElt);
|
||||
}
|
||||
|
||||
editor.setAsText(channelConfigAttribute);
|
||||
channelRequestMap.put(new RequestKey(path), (ConfigAttributeDefinition) editor.getValue());
|
||||
}
|
||||
|
||||
String filters = urlElt.getAttribute(ATT_FILTERS);
|
||||
|
||||
if (StringUtils.hasText(filters)) {
|
||||
if (!filters.equals(OPT_FILTERS_NONE)) {
|
||||
parserContext.getReaderContext().error("Currently only 'none' is supported as the custom " +
|
||||
"filters attribute", urlElt);
|
||||
}
|
||||
|
||||
filterChainMap.put(path, Collections.EMPTY_LIST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void parseInterceptUrlsForFilterInvocationRequestMap(List urlElts, Map filterInvocationDefinitionMap,
|
||||
boolean useLowerCasePaths, ParserContext parserContext) {
|
||||
|
||||
Iterator urlEltsIterator = urlElts.iterator();
|
||||
ConfigAttributeEditor editor = new ConfigAttributeEditor();
|
||||
|
||||
while (urlEltsIterator.hasNext()) {
|
||||
|
@ -308,38 +369,9 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
|||
// Convert the comma-separated list of access attributes to a ConfigAttributeDefinition
|
||||
if (StringUtils.hasText(access)) {
|
||||
editor.setAsText(access);
|
||||
interceptorFilterInvDefSource.addSecureUrl(path, method, (ConfigAttributeDefinition) editor.getValue());
|
||||
}
|
||||
|
||||
String requiredChannel = urlElt.getAttribute(ATT_REQUIRES_CHANNEL);
|
||||
|
||||
if (StringUtils.hasText(requiredChannel)) {
|
||||
String channelConfigAttribute = null;
|
||||
|
||||
if (requiredChannel.equals(OPT_REQUIRES_HTTPS)) {
|
||||
channelConfigAttribute = "REQUIRES_SECURE_CHANNEL";
|
||||
} else if (requiredChannel.equals(OPT_REQUIRES_HTTP)) {
|
||||
channelConfigAttribute = "REQUIRES_INSECURE_CHANNEL";
|
||||
} else if (requiredChannel.equals(OPT_ANY_CHANNEL)) {
|
||||
channelConfigAttribute = ChannelDecisionManagerImpl.ANY_CHANNEL;
|
||||
} else {
|
||||
parserContext.getReaderContext().error("Unsupported channel " + requiredChannel, urlElt);
|
||||
}
|
||||
|
||||
editor.setAsText(channelConfigAttribute);
|
||||
channelFilterInvDefSource.addSecureUrl(path, (ConfigAttributeDefinition) editor.getValue());
|
||||
}
|
||||
|
||||
String filters = urlElt.getAttribute(ATT_FILTERS);
|
||||
|
||||
if (StringUtils.hasText(filters)) {
|
||||
if (!filters.equals(OPT_FILTERS_NONE)) {
|
||||
parserContext.getReaderContext().error("Currently only 'none' is supported as the custom " +
|
||||
"filters attribute", urlElt);
|
||||
}
|
||||
|
||||
filterChainMap.put(path, Collections.EMPTY_LIST);
|
||||
filterInvocationDefinitionMap.put(new RequestKey(path, method), editor.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ public class SecurityNamespaceHandler extends NamespaceHandlerSupport {
|
|||
registerBeanDefinitionParser(Elements.AUTHENTICATION_PROVIDER, new AuthenticationProviderBeanDefinitionParser());
|
||||
registerBeanDefinitionParser(Elements.ANNOTATION_DRIVEN, new AnnotationDrivenBeanDefinitionParser());
|
||||
registerBeanDefinitionParser(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser());
|
||||
registerBeanDefinitionParser(Elements.FILTER_INVOCATION_DEFINITION_SOURCE, new FilterInvocationDefinitionSourceBeanDefinitionParser());
|
||||
|
||||
// Decorators
|
||||
registerBeanDefinitionDecorator(Elements.INTERCEPT_METHODS, new InterceptMethodsBeanDefinitionDecorator());
|
||||
|
|
|
@ -61,7 +61,7 @@ public class DefaultFilterInvocationDefinitionSource implements FilterInvocation
|
|||
|
||||
/**
|
||||
* Non method-specific map of URL patterns to <tt>ConfigAttributeDefinition</tt>s
|
||||
* TODO: Store in the httpMethod map with null key.
|
||||
* TODO: Store in the httpMethod map with null key.
|
||||
*/
|
||||
private Map requestMap = new LinkedHashMap();
|
||||
/** Stores request maps keyed by specific HTTP methods */
|
||||
|
@ -75,10 +75,18 @@ public class DefaultFilterInvocationDefinitionSource implements FilterInvocation
|
|||
* Creates a FilterInvocationDefinitionSource with the supplied URL matching strategy.
|
||||
* @param urlMatcher
|
||||
*/
|
||||
public DefaultFilterInvocationDefinitionSource(UrlMatcher urlMatcher) {
|
||||
DefaultFilterInvocationDefinitionSource(UrlMatcher urlMatcher) {
|
||||
this.urlMatcher = urlMatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the internal request map from the supplied map. The key elements should be of type {@link RequestKey},
|
||||
* which contains a URL path and an optional HTTP method (may be null). The path stored in the key will depend on
|
||||
* the type of the supplied UrlMatcher.
|
||||
*
|
||||
* @param urlMatcher typically an ant or regular expression matcher.
|
||||
* @param requestMap order-preserving map of <RequestKey, ConfigAttributeDefinition>.
|
||||
*/
|
||||
public DefaultFilterInvocationDefinitionSource(UrlMatcher urlMatcher, LinkedHashMap requestMap) {
|
||||
this.urlMatcher = urlMatcher;
|
||||
|
||||
|
@ -86,13 +94,14 @@ public class DefaultFilterInvocationDefinitionSource implements FilterInvocation
|
|||
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry entry = (Map.Entry) iterator.next();
|
||||
addSecureUrl((String)entry.getKey(), (ConfigAttributeDefinition)entry.getValue());
|
||||
RequestKey reqKey = (RequestKey) entry.getKey();
|
||||
addSecureUrl(reqKey.getUrl(), reqKey.getMethod(), (ConfigAttributeDefinition) entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public void addSecureUrl(String pattern, ConfigAttributeDefinition attr) {
|
||||
void addSecureUrl(String pattern, ConfigAttributeDefinition attr) {
|
||||
addSecureUrl(pattern, null, attr);
|
||||
}
|
||||
|
||||
|
@ -102,7 +111,7 @@ public class DefaultFilterInvocationDefinitionSource implements FilterInvocation
|
|||
* to the request map and will be passed back to the <tt>UrlMatcher</tt> when iterating through the map to find
|
||||
* a match for a particular URL.
|
||||
*/
|
||||
public void addSecureUrl(String pattern, String method, ConfigAttributeDefinition attr) {
|
||||
void addSecureUrl(String pattern, String method, ConfigAttributeDefinition attr) {
|
||||
Map mapToUse = getRequestMapForHttpMethod(method);
|
||||
|
||||
mapToUse.put(urlMatcher.compile(pattern), attr);
|
||||
|
|
|
@ -167,7 +167,7 @@ public class FilterInvocationDefinitionSourceEditor extends PropertyEditorSuppor
|
|||
|
||||
String[] tokens = StringUtils.commaDelimitedListToStringArray(value);
|
||||
|
||||
urlMap.put(name, new ConfigAttributeDefinition(tokens));
|
||||
urlMap.put(new RequestKey(name), new ConfigAttributeDefinition(tokens));
|
||||
}
|
||||
|
||||
DefaultFilterInvocationDefinitionSource fids =
|
||||
|
|
|
@ -5,25 +5,35 @@ package org.springframework.security.intercept.web;
|
|||
* @version $Id$
|
||||
*/
|
||||
public class RequestKey {
|
||||
String url;
|
||||
String method;
|
||||
private String url;
|
||||
private String method;
|
||||
|
||||
public RequestKey(String url) {
|
||||
this(url, "");
|
||||
this(url, null);
|
||||
}
|
||||
|
||||
public RequestKey(String url, String method) {
|
||||
this.url = url;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int code = 31;
|
||||
code ^= url.hashCode();
|
||||
code ^= method.hashCode();
|
||||
|
||||
if (method != null) {
|
||||
code ^= method.hashCode();
|
||||
}
|
||||
|
||||
return code;
|
||||
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
|
@ -33,6 +43,14 @@ public class RequestKey {
|
|||
|
||||
RequestKey key = (RequestKey) obj;
|
||||
|
||||
return url.equals(key.url) && method.equals(key.method);
|
||||
if (!url.equals(key.url)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (method == null && key.method != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return method.equals(key.method);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package org.springframework.security.util;
|
|||
/**
|
||||
* Strategy for deciding whether configured path matches a submitted candidate URL.
|
||||
*
|
||||
* @author luke
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.0
|
||||
*/
|
||||
|
|
|
@ -212,6 +212,18 @@ filter-chain.attlist &=
|
|||
filter-chain.attlist &=
|
||||
attribute filters {xsd:string}
|
||||
|
||||
filter-invocation-definition-source =
|
||||
## Used to explicitly configure a FilterInvocationDefinitionSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error.
|
||||
element filter-invocation-definition-source {fids.attlist, intercept-url+}
|
||||
fids.attlist &=
|
||||
id?
|
||||
fids.attlist &=
|
||||
## as for http element
|
||||
attribute lowercase-comparisons {"true" | "false"}?
|
||||
fids.attlist &=
|
||||
## as for http element
|
||||
path-type?
|
||||
|
||||
http-basic =
|
||||
## Adds support for basic authentication (this is an element to permit future expansion, such as supporting an "ignoreFailure" attribute)
|
||||
element http-basic {empty}
|
||||
|
@ -328,6 +340,6 @@ before =
|
|||
## The filter immediately before which the custom-filter should be placed in the chain
|
||||
attribute before {"FIRST" | "CHANNEL_FILTER" | "CONCURRENT_SESSION_FILTER" | "SESSION_CONTEXT_INTEGRATION_FILTER" | "LOGOUT_FILTER" | "X509_FILTER" | "PRE_AUTH_FILTER" | "CAS_PROCESSING_FILTER" | "AUTHENTICATION_PROCESSING_FILTER" | "BASIC_PROCESSING_FILTER" | "SERVLET_API_SUPPORT_FILTER" | "REMEMBER_ME_FILTER" | "ANONYMOUS_FILTER" | "EXCEPTION_TRANSLATION_FILTER" | "NTLM_FILTER" | "FILTER_SECURITY_INTERCEPTOR" | "SWITCH_USER_FILTER"}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -527,6 +527,46 @@
|
|||
<xs:attribute name="pattern" use="required" type="xs:string"/>
|
||||
<xs:attribute name="filters" use="required" type="xs:string"/>
|
||||
</xs:attributeGroup>
|
||||
<xs:element name="filter-invocation-definition-source">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Used to explicitly configure a FilterInvocationDefinitionSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the <http> element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error. </xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element maxOccurs="unbounded" ref="security:intercept-url"/>
|
||||
</xs:sequence>
|
||||
<xs:attributeGroup ref="security:fids.attlist"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:attributeGroup name="fids.attlist">
|
||||
<xs:attribute name="id" type="xs:ID">
|
||||
<xs:annotation>
|
||||
<xs:documentation>A bean identifier, used for referring to the bean elsewhere in the context.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="lowercase-comparisons">
|
||||
<xs:annotation>
|
||||
<xs:documentation>as for http element</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="true"/>
|
||||
<xs:enumeration value="false"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="path-type">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Defines the type of pattern used to specify URL paths (either JDK 1.4-compatible regular expressions, or Apache Ant expressions). Defaults to "ant" if unspecified.</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="ant"/>
|
||||
<xs:enumeration value="regex"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
<xs:element name="http-basic">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Adds support for basic authentication (this is an element to permit future expansion, such as supporting an "ignoreFailure" attribute)</xs:documentation>
|
||||
|
|
|
@ -38,7 +38,7 @@ import java.util.List;
|
|||
*/
|
||||
public class HttpSecurityBeanDefinitionParserTests {
|
||||
private AbstractXmlApplicationContext appContext;
|
||||
private static final String AUTH_PROVIDER_XML =
|
||||
static final String AUTH_PROVIDER_XML =
|
||||
" <authentication-provider>" +
|
||||
" <user-service>" +
|
||||
" <user name='bob' password='bobspassword' authorities='ROLE_A,ROLE_B' />" +
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.springframework.security.ConfigAttributeDefinition;
|
|||
import org.springframework.security.MockFilterChain;
|
||||
import org.springframework.security.SecurityConfig;
|
||||
import org.springframework.security.util.AntUrlPathMatcher;
|
||||
import org.springframework.security.util.InMemoryXmlApplicationContext;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
|
@ -169,6 +170,38 @@ public class DefaultFilterInvocationDefinitionSourceTests {
|
|||
response = map.lookupAttributes(fi.getRequestUrl());
|
||||
assertEquals(def, response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void xmlMapConfigurationIsSuccessful() {
|
||||
InMemoryXmlApplicationContext context = new InMemoryXmlApplicationContext(
|
||||
"<b:bean id='fids' class='org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource'>" +
|
||||
" <b:constructor-arg>" +
|
||||
" <b:bean class='org.springframework.security.util.AntUrlPathMatcher'/>" +
|
||||
" </b:constructor-arg>" +
|
||||
" <b:constructor-arg>" +
|
||||
" <b:map>" +
|
||||
" <b:entry>" +
|
||||
" <b:key>" +
|
||||
" <b:bean class='org.springframework.security.intercept.web.RequestKey'>" +
|
||||
" <b:constructor-arg index='0' value='/**'/>" +
|
||||
" <b:constructor-arg index='1' value='GET'/>" +
|
||||
" </b:bean>" +
|
||||
" </b:key>" +
|
||||
" <b:bean class='org.springframework.security.ConfigAttributeDefinition'>" +
|
||||
" <b:constructor-arg value='ROLE_A'/>" +
|
||||
" </b:bean>" +
|
||||
" </b:entry>" +
|
||||
" </b:map>" +
|
||||
" </b:constructor-arg>" +
|
||||
"</b:bean>"
|
||||
);
|
||||
|
||||
DefaultFilterInvocationDefinitionSource fids = (DefaultFilterInvocationDefinitionSource) context.getBean("fids");
|
||||
ConfigAttributeDefinition cad = fids.lookupAttributes("/anything", "GET");
|
||||
assertNotNull(cad);
|
||||
assertEquals(1, cad.getConfigAttributes().size());
|
||||
context.close();
|
||||
}
|
||||
|
||||
private FilterInvocation createFilterInvocation(String path, String method) {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.springframework.mock.web.MockHttpServletResponse;
|
|||
import java.io.IOException;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
|
@ -220,9 +221,10 @@ public class FilterSecurityInterceptorTests extends TestCase {
|
|||
}
|
||||
|
||||
public void testNotLoadedFromApplicationContext() throws Exception {
|
||||
LinkedHashMap reqMap = new LinkedHashMap();
|
||||
reqMap.put(new RequestKey("/secure/**", null), new ConfigAttributeDefinition(new String[] {"ROLE_USER"}));
|
||||
DefaultFilterInvocationDefinitionSource fids
|
||||
= new DefaultFilterInvocationDefinitionSource(new AntUrlPathMatcher());
|
||||
fids.addSecureUrl("/secure/**", null, new ConfigAttributeDefinition(new String[] {"ROLE_USER"}));
|
||||
= new DefaultFilterInvocationDefinitionSource(new AntUrlPathMatcher());
|
||||
|
||||
FilterSecurityInterceptor filter = new FilterSecurityInterceptor();
|
||||
filter.setObjectDefinitionSource(fids);
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.springframework.security.MockFilterConfig;
|
|||
import org.springframework.security.context.HttpSessionContextIntegrationFilter;
|
||||
import org.springframework.security.intercept.web.MockFilterInvocationDefinitionSource;
|
||||
import org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource;
|
||||
import org.springframework.security.intercept.web.RequestKey;
|
||||
import org.springframework.security.ui.webapp.AuthenticationProcessingFilter;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
|
@ -34,6 +35,7 @@ import static org.junit.Assert.*;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -80,9 +82,10 @@ public class FilterChainProxyTests {
|
|||
|
||||
ConfigAttributeDefinition cad = new ConfigAttributeDefinition(new MockConfigAttribute());
|
||||
|
||||
LinkedHashMap map = new LinkedHashMap();
|
||||
map.put(new RequestKey("/**"), cad);
|
||||
DefaultFilterInvocationDefinitionSource fids =
|
||||
new DefaultFilterInvocationDefinitionSource(new AntUrlPathMatcher());
|
||||
fids.addSecureUrl("/**", cad);
|
||||
new DefaultFilterInvocationDefinitionSource(new AntUrlPathMatcher(), map);
|
||||
|
||||
filterChainProxy.setFilterInvocationDefinitionSource(fids);
|
||||
|
||||
|
|
Loading…
Reference in New Issue