Ver Fonte

SEC-640: Add namespace support for FilterInvocationDefinitionSource configuration
http://jira.springframework.org/browse/SEC-640

Luke Taylor há 17 anos atrás
pai
commit
33023565a8

+ 1 - 0
core/src/main/java/org/springframework/security/config/Elements.java

@@ -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";
 }

+ 54 - 0
core/src/main/java/org/springframework/security/config/FilterInvocationDefinitionSourceBeanDefinitionParser.java

@@ -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);
+    }
+}

+ 97 - 65
core/src/main/java/org/springframework/security/config/HttpSecurityBeanDefinitionParser.java

@@ -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,52 @@ 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()) {
@@ -298,19 +308,6 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
                 path = path.toLowerCase();
             }
 
-            String method = urlElt.getAttribute(ATT_HTTP_METHOD);
-            if (!StringUtils.hasText(method)) {
-                method = null;
-            }
-
-            String access = urlElt.getAttribute(ATT_ACCESS_CONFIG);
-
-            // 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)) {
@@ -327,9 +324,9 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
                 }
 
                 editor.setAsText(channelConfigAttribute);
-                channelFilterInvDefSource.addSecureUrl(path, (ConfigAttributeDefinition) editor.getValue());
+                channelRequestMap.put(new RequestKey(path), (ConfigAttributeDefinition) editor.getValue());
             }
-
+            
             String filters = urlElt.getAttribute(ATT_FILTERS);
 
             if (StringUtils.hasText(filters)) {
@@ -342,4 +339,39 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
             }
         }
     }
+    
+    static void parseInterceptUrlsForFilterInvocationRequestMap(List urlElts, Map filterInvocationDefinitionMap, 
+            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 method = urlElt.getAttribute(ATT_HTTP_METHOD);
+            if (!StringUtils.hasText(method)) {
+                method = null;
+            }
+
+            String access = urlElt.getAttribute(ATT_ACCESS_CONFIG);
+
+            // Convert the comma-separated list of access attributes to a ConfigAttributeDefinition
+            if (StringUtils.hasText(access)) {
+                editor.setAsText(access);
+                filterInvocationDefinitionMap.put(new RequestKey(path, method), editor.getValue());
+            }
+        }
+    }
+    
 }

+ 1 - 0
core/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java

@@ -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());

+ 14 - 5
core/src/main/java/org/springframework/security/intercept/web/DefaultFilterInvocationDefinitionSource.java

@@ -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);

+ 1 - 1
core/src/main/java/org/springframework/security/intercept/web/FilterInvocationDefinitionSourceEditor.java

@@ -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 =

+ 24 - 6
core/src/main/java/org/springframework/security/intercept/web/RequestKey.java

@@ -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);        
     }
 }

+ 1 - 1
core/src/main/java/org/springframework/security/util/UrlMatcher.java

@@ -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
  */

+ 13 - 1
core/src/main/resources/org/springframework/security/config/spring-security-2.0.rnc

@@ -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"}
 
-    
+
     
     

+ 40 - 0
core/src/main/resources/org/springframework/security/config/spring-security-2.0.xsd

@@ -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 &lt;http&gt; 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>

+ 1 - 1
core/src/test/java/org/springframework/security/config/HttpSecurityBeanDefinitionParserTests.java

@@ -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' />" +

+ 33 - 0
core/src/test/java/org/springframework/security/intercept/web/DefaultFilterInvocationDefinitionSourceTests.java

@@ -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();

+ 4 - 2
core/src/test/java/org/springframework/security/intercept/web/FilterSecurityInterceptorTests.java

@@ -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);

+ 5 - 2
core/src/test/java/org/springframework/security/util/FilterChainProxyTests.java

@@ -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);