Browse Source

Shis simplifies the class hieararchy significantly.EC-2366: Extract AbstractRequestMatcherRegistry from AbstractRequestMatcherConfigurer

This simplifies the class hierarchy significantly.
Rob Winch 12 năm trước cách đây
mục cha
commit
604c26eb0d
15 tập tin đã thay đổi với 347 bổ sung195 xóa
  1. 3 7
      config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java
  2. 6 6
      config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java
  3. 2 3
      config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurity.java
  4. 2 5
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractConfigAttributeRequestMatcherRegistry.java
  5. 45 44
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractInterceptUrlConfigurer.java
  6. 53 31
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/ChannelSecurityConfigurer.java
  7. 65 49
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java
  8. 2 2
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/PermitAllSupport.java
  9. 84 31
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/UrlAuthorizationConfigurer.java
  10. 1 1
      config/src/test/groovy/org/springframework/security/config/annotation/web/RequestMatchersTests.groovy
  11. 1 1
      config/src/test/groovy/org/springframework/security/config/annotation/web/builders/NamespaceHttpTests.groovy
  12. 9 14
      config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/AbstractConfigAttributeRequestMatcherRegistryTests.groovy
  13. 63 0
      config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurerConfigs.java
  14. 9 0
      config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationsTests.groovy
  15. 2 1
      config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/UrlAuthorizationsTests.groovy

+ 3 - 7
config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherConfigurer.java → config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java

@@ -20,9 +20,7 @@ import java.util.Arrays;
 import java.util.List;
 
 import org.springframework.http.HttpMethod;
-import org.springframework.security.config.annotation.SecurityBuilder;
-import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
-import org.springframework.security.config.annotation.web.configurers.AbstractRequestMatcherMappingConfigurer;
+import org.springframework.security.config.annotation.web.configurers.AbstractConfigAttributeRequestMatcherRegistry;
 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
 import org.springframework.security.web.util.matcher.AnyRequestMatcher;
 import org.springframework.security.web.util.matcher.RegexRequestMatcher;
@@ -33,14 +31,12 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
  * {@link RequestMatcher} require a certain level of authorization.
  *
  *
- * @param <B> The Builder that is building Object O and is configured by this {@link AbstractRequestMatcherMappingConfigurer}
  * @param <C> The object that is returned or Chained after creating the RequestMatcher
- * @param <O> The Object being built by Builder B
  *
  * @author Rob Winch
  * @since 3.2
  */
-public abstract class AbstractRequestMatcherConfigurer<B extends SecurityBuilder<O>,C,O> extends SecurityConfigurerAdapter<O,B> {
+public abstract class AbstractRequestMatcherRegistry<C> {
     private static final RequestMatcher ANY_REQUEST = AnyRequestMatcher.INSTANCE;
     /**
      * Maps any request.
@@ -109,7 +105,7 @@ public abstract class AbstractRequestMatcherConfigurer<B extends SecurityBuilder
     }
 
     /**
-     * Associates a list of {@link RequestMatcher} instances with the {@link AbstractRequestMatcherMappingConfigurer}
+     * Associates a list of {@link RequestMatcher} instances with the {@link AbstractConfigAttributeRequestMatcherRegistry}
      *
      * @param requestMatchers the {@link RequestMatcher} instances
      *

+ 6 - 6
config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java

@@ -31,7 +31,7 @@ import org.springframework.security.config.annotation.SecurityBuilder;
 import org.springframework.security.config.annotation.SecurityConfigurer;
 import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.web.AbstractRequestMatcherConfigurer;
+import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
 import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
@@ -680,8 +680,8 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
      * @return
      * @throws Exception
      */
-    public ExpressionUrlAuthorizationConfigurer<HttpSecurity> authorizeRequests() throws Exception {
-        return getOrApply(new ExpressionUrlAuthorizationConfigurer<HttpSecurity>());
+    public ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests() throws Exception {
+        return getOrApply(new ExpressionUrlAuthorizationConfigurer<HttpSecurity>()).getRegistry();
     }
 
     /**
@@ -1020,8 +1020,8 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
      * @return the {@link ChannelSecurityConfigurer} for further customizations
      * @throws Exception
      */
-    public ChannelSecurityConfigurer<HttpSecurity> requiresChannel() throws Exception {
-        return getOrApply(new ChannelSecurityConfigurer<HttpSecurity>());
+    public ChannelSecurityConfigurer<HttpSecurity>.ChannelRequestMatcherRegistry requiresChannel() throws Exception {
+        return getOrApply(new ChannelSecurityConfigurer<HttpSecurity>()).getRegistry();
     }
 
     /**
@@ -1312,7 +1312,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
      * @author Rob Winch
      * @since 3.2
      */
-    public final class RequestMatcherConfigurer extends AbstractRequestMatcherConfigurer<HttpSecurity,RequestMatcherConfigurer,DefaultSecurityFilterChain> {
+    public final class RequestMatcherConfigurer extends AbstractRequestMatcherRegistry<RequestMatcherConfigurer> {
 
         protected RequestMatcherConfigurer chainRequestMatchers(List<RequestMatcher> requestMatchers) {
             requestMatcher(new OrRequestMatcher(requestMatchers));

+ 2 - 3
config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurity.java

@@ -29,7 +29,7 @@ import org.springframework.context.ApplicationContextAware;
 import org.springframework.security.access.expression.SecurityExpressionHandler;
 import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
 import org.springframework.security.config.annotation.SecurityBuilder;
-import org.springframework.security.config.annotation.web.AbstractRequestMatcherConfigurer;
+import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
 import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
@@ -313,7 +313,7 @@ public final class WebSecurity extends
      * @author Rob Winch
      * @since 3.2
      */
-    public final class IgnoredRequestConfigurer extends AbstractRequestMatcherConfigurer<WebSecurity,IgnoredRequestConfigurer,Filter> {
+    public final class IgnoredRequestConfigurer extends AbstractRequestMatcherRegistry<IgnoredRequestConfigurer> {
 
         @Override
         protected IgnoredRequestConfigurer chainRequestMatchers(List<RequestMatcher> requestMatchers) {
@@ -324,7 +324,6 @@ public final class WebSecurity extends
         /**
          * Returns the {@link WebSecurity} to be returned for chaining.
          */
-        @Override
         public WebSecurity and() {
             return WebSecurity.this;
         }

+ 2 - 5
config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractRequestMatcherMappingConfigurer.java → config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractConfigAttributeRequestMatcherRegistry.java

@@ -21,8 +21,7 @@ import java.util.LinkedHashMap;
 import java.util.List;
 
 import org.springframework.security.access.ConfigAttribute;
-import org.springframework.security.config.annotation.SecurityBuilder;
-import org.springframework.security.config.annotation.web.AbstractRequestMatcherConfigurer;
+import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
 import org.springframework.security.web.util.matcher.RequestMatcher;
 
 /**
@@ -32,15 +31,13 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
  * @author Rob Winch
  * @since 3.2
  *
- * @param <B> The Builder that is building Object O and is configured by this {@link AbstractRequestMatcherMappingConfigurer}
  * @param <C> The object that is returned or Chained after creating the RequestMatcher
- * @param <O> The Object being built by Builder B
  *
  * @see ChannelSecurityConfigurer
  * @see UrlAuthorizationConfigurer
  * @see ExpressionUrlAuthorizationConfigurer
  */
-public abstract class AbstractRequestMatcherMappingConfigurer<B extends SecurityBuilder<O>,C,O> extends AbstractRequestMatcherConfigurer<B,C,O> {
+public abstract class AbstractConfigAttributeRequestMatcherRegistry<C> extends AbstractRequestMatcherRegistry<C> {
     private List<UrlMapping> urlMappings = new ArrayList<UrlMapping>();
     private List<RequestMatcher> unmappedMatchers;
 

+ 45 - 44
config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractInterceptUrlConfigurer.java

@@ -23,7 +23,6 @@ import org.springframework.security.access.vote.AffirmativeBased;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.config.annotation.SecurityConfigurer;
 import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
-import org.springframework.security.web.DefaultSecurityFilterChain;
 import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
 import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
 
@@ -53,49 +52,21 @@ import org.springframework.security.web.access.intercept.FilterSecurityIntercept
  *     <li>{@link org.springframework.security.config.annotation.web.builders.HttpSecurity#getAuthenticationManager()}</li>
  * </ul>
  *
+ *
+ * @param <C> the AbstractInterceptUrlConfigurer
  * @param <H> the type of {@link HttpSecurityBuilder} that is being configured
- * @param <C> the type of object that is changed
- * @param <R> the type of object that is changed for the {@link AbstractRequestMatcherMappingConfigurer}
  *
  * @author Rob Winch
  * @since 3.2
  * @see ExpressionUrlAuthorizationConfigurer
  * @see UrlAuthorizationConfigurer
  */
-abstract class AbstractInterceptUrlConfigurer<H extends HttpSecurityBuilder<H>,C,R> extends
-        AbstractRequestMatcherMappingConfigurer<H,R,DefaultSecurityFilterChain> implements
-        SecurityConfigurer<DefaultSecurityFilterChain,H> {
+abstract class AbstractInterceptUrlConfigurer<C extends AbstractInterceptUrlConfigurer<C,H>, H extends HttpSecurityBuilder<H>> extends
+        AbstractHttpConfigurer<C, H>{
     private Boolean filterSecurityInterceptorOncePerRequest;
 
     private AccessDecisionManager accessDecisionManager;
 
-    /**
-     * Allows setting the {@link AccessDecisionManager}. If none is provided, a default {@l AccessDecisionManager} is
-     * created.
-     *
-     * @param accessDecisionManager the {@link AccessDecisionManager} to use
-     * @return  the {@link AbstractInterceptUrlConfigurer} for further customization
-     */
-    public C accessDecisionManager(
-            AccessDecisionManager accessDecisionManager) {
-        this.accessDecisionManager = accessDecisionManager;
-        return getSelf();
-    }
-
-    /**
-     * Allows setting if the {@link FilterSecurityInterceptor} should be only applied once per request (i.e. if the
-     * filter intercepts on a forward, should it be applied again).
-     *
-     * @param filterSecurityInterceptorOncePerRequest if the {@link FilterSecurityInterceptor} should be only applied
-     *                                                once per request
-     * @return  the {@link AbstractInterceptUrlConfigurer} for further customization
-     */
-    public C filterSecurityInterceptorOncePerRequest(
-            boolean filterSecurityInterceptorOncePerRequest) {
-        this.filterSecurityInterceptorOncePerRequest = filterSecurityInterceptorOncePerRequest;
-        return getSelf();
-    }
-
     @Override
     public void configure(H http) throws Exception {
         FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http);
@@ -134,6 +105,47 @@ abstract class AbstractInterceptUrlConfigurer<H extends HttpSecurityBuilder<H>,C
     @SuppressWarnings("rawtypes")
     abstract List<AccessDecisionVoter> getDecisionVoters(H http);
 
+    abstract class AbstractInterceptUrlRegistry<R extends AbstractInterceptUrlRegistry<R,T>,T> extends AbstractConfigAttributeRequestMatcherRegistry<T> {
+
+        /**
+         * Allows setting the {@link AccessDecisionManager}. If none is provided, a default {@l AccessDecisionManager} is
+         * created.
+         *
+         * @param accessDecisionManager the {@link AccessDecisionManager} to use
+         * @return  the {@link AbstractInterceptUrlConfigurer} for further customization
+         */
+        public R accessDecisionManager(
+                AccessDecisionManager accessDecisionManager) {
+            AbstractInterceptUrlConfigurer.this.accessDecisionManager = accessDecisionManager;
+            return getSelf();
+        }
+
+        /**
+         * Allows setting if the {@link FilterSecurityInterceptor} should be only applied once per request (i.e. if the
+         * filter intercepts on a forward, should it be applied again).
+         *
+         * @param filterSecurityInterceptorOncePerRequest if the {@link FilterSecurityInterceptor} should be only applied
+         *                                                once per request
+         * @return  the {@link AbstractInterceptUrlConfigurer} for further customization
+         */
+        public R filterSecurityInterceptorOncePerRequest(
+                boolean filterSecurityInterceptorOncePerRequest) {
+            AbstractInterceptUrlConfigurer.this.filterSecurityInterceptorOncePerRequest = filterSecurityInterceptorOncePerRequest;
+            return getSelf();
+        }
+
+        /**
+         * Returns a reference to the current object with a single suppression of
+         * the type
+         *
+         * @return a reference to the current object
+         */
+        @SuppressWarnings("unchecked")
+        private R getSelf() {
+            return (R) this;
+        }
+    }
+
     /**
      * Creates the default {@code AccessDecisionManager}
      * @return the default {@code AccessDecisionManager}
@@ -175,15 +187,4 @@ abstract class AbstractInterceptUrlConfigurer<H extends HttpSecurityBuilder<H>,C
         securityInterceptor.afterPropertiesSet();
         return securityInterceptor;
     }
-
-    /**
-     * Returns a reference to the current object with a single suppression of
-     * the type
-     *
-     * @return a reference to the current object
-     */
-    @SuppressWarnings("unchecked")
-    private C getSelf() {
-        return (C) this;
-    }
 }

+ 53 - 31
config/src/main/java/org/springframework/security/config/annotation/web/configurers/ChannelSecurityConfigurer.java

@@ -23,9 +23,10 @@ import java.util.List;
 import org.springframework.security.access.ConfigAttribute;
 import org.springframework.security.access.SecurityConfig;
 import org.springframework.security.config.annotation.ObjectPostProcessor;
+import org.springframework.security.config.annotation.SecurityBuilder;
+import org.springframework.security.config.annotation.SecurityConfigurer;
 import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.web.DefaultSecurityFilterChain;
 import org.springframework.security.web.PortMapper;
 import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
 import org.springframework.security.web.access.channel.ChannelProcessingFilter;
@@ -72,11 +73,13 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
  * @since 3.2
  */
 public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> extends
-        AbstractRequestMatcherMappingConfigurer<H,ChannelSecurityConfigurer<H>.RequiresChannelUrl,DefaultSecurityFilterChain> {
+        AbstractHttpConfigurer<ChannelSecurityConfigurer<H>, H> {
     private ChannelProcessingFilter channelFilter = new ChannelProcessingFilter();
     private LinkedHashMap<RequestMatcher,Collection<ConfigAttribute>> requestMap = new LinkedHashMap<RequestMatcher,Collection<ConfigAttribute>>();
     private List<ChannelProcessor> channelProcessors;
 
+    private final ChannelRequestMatcherRegistry REGISTRY = new ChannelRequestMatcherRegistry();
+
     /**
      * Creates a new instance
      * @see HttpSecurity#requiresChannel()
@@ -84,6 +87,10 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> e
     public ChannelSecurityConfigurer() {
     }
 
+    public ChannelRequestMatcherRegistry getRegistry() {
+        return REGISTRY;
+    }
+
     @Override
     public void configure(H http) throws Exception {
         ChannelDecisionManagerImpl channelDecisionManager = new ChannelDecisionManagerImpl();
@@ -100,27 +107,6 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> e
         http.addFilter(channelFilter);
     }
 
-    /**
-     * Adds an {@link ObjectPostProcessor} for this class.
-     *
-     * @param objectPostProcessor
-     * @return the {@link ChannelSecurityConfigurer} for further customizations
-     */
-    public ChannelSecurityConfigurer<H> withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
-        addObjectPostProcessor(objectPostProcessor);
-        return this;
-    }
-
-    /**
-     * Sets the {@link ChannelProcessor} instances to use in  {@link ChannelDecisionManagerImpl}
-     * @param channelProcessors
-     * @return
-     */
-    public ChannelSecurityConfigurer<H> channelProcessors(List<ChannelProcessor> channelProcessors) {
-        this.channelProcessors = channelProcessors;
-        return this;
-    }
-
     private List<ChannelProcessor> getChannelProcessors(H http) {
         if(channelProcessors != null) {
             return channelProcessors;
@@ -145,17 +131,53 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> e
     }
 
 
-    private ChannelSecurityConfigurer<H> addAttribute(String attribute, List<RequestMatcher> matchers) {
+    private ChannelRequestMatcherRegistry addAttribute(String attribute, List<RequestMatcher> matchers) {
         for(RequestMatcher matcher : matchers) {
             Collection<ConfigAttribute> attrs = Arrays.<ConfigAttribute>asList(new SecurityConfig(attribute));
             requestMap.put(matcher, attrs);
         }
-        return this;
+        return REGISTRY;
     }
 
-    @Override
-    protected RequiresChannelUrl chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {
-        return new RequiresChannelUrl(requestMatchers);
+    public final class ChannelRequestMatcherRegistry extends AbstractConfigAttributeRequestMatcherRegistry<RequiresChannelUrl> {
+
+        @Override
+        protected RequiresChannelUrl chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {
+            return new RequiresChannelUrl(requestMatchers);
+        }
+
+        /**
+         * Adds an {@link ObjectPostProcessor} for this class.
+         *
+         * @param objectPostProcessor
+         * @return the {@link ChannelSecurityConfigurer} for further customizations
+         */
+        public ChannelRequestMatcherRegistry withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
+            addObjectPostProcessor(objectPostProcessor);
+            return this;
+        }
+
+        /**
+         * Sets the {@link ChannelProcessor} instances to use in  {@link ChannelDecisionManagerImpl}
+         * @param channelProcessors
+         * @return
+         */
+        public ChannelRequestMatcherRegistry channelProcessors(List<ChannelProcessor> channelProcessors) {
+            ChannelSecurityConfigurer.this.channelProcessors = channelProcessors;
+            return this;
+        }
+
+        /**
+         * Return the {@link SecurityBuilder} when done using the
+         * {@link SecurityConfigurer}. This is useful for method chaining.
+         *
+         * @return
+         */
+        public H and() {
+            return ChannelSecurityConfigurer.this.and();
+        }
+
+        private ChannelRequestMatcherRegistry() {}
     }
 
     public final class RequiresChannelUrl {
@@ -165,15 +187,15 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>> e
             this.requestMatchers = requestMatchers;
         }
 
-        public ChannelSecurityConfigurer<H> requiresSecure() {
+        public ChannelRequestMatcherRegistry requiresSecure() {
             return requires("REQUIRES_SECURE_CHANNEL");
         }
 
-        public ChannelSecurityConfigurer<H> requiresInsecure() {
+        public ChannelRequestMatcherRegistry requiresInsecure() {
             return requires("REQUIRES_INSECURE_CHANNEL");
         }
 
-        public ChannelSecurityConfigurer<H> requires(String attribute) {
+        public ChannelRequestMatcherRegistry requires(String attribute) {
             return addAttribute(attribute, requestMatchers);
         }
     }

+ 65 - 49
config/src/main/java/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurer.java

@@ -67,7 +67,7 @@ import org.springframework.util.StringUtils;
  * @since 3.2
  * @see {@link org.springframework.security.config.annotation.web.builders.HttpSecurity#authorizeRequests()}
  */
-public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractInterceptUrlConfigurer<H,ExpressionUrlAuthorizationConfigurer<H>,ExpressionUrlAuthorizationConfigurer<H>.AuthorizedUrl> {
+public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractInterceptUrlConfigurer<ExpressionUrlAuthorizationConfigurer<H>,H> {
     static final String permitAll = "permitAll";
     private static final String denyAll = "denyAll";
     private static final String anonymous = "anonymous";
@@ -75,6 +75,8 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
     private static final String fullyAuthenticated = "fullyAuthenticated";
     private static final String rememberMe = "rememberMe";
 
+    private final ExpressionInterceptUrlRegistry REGISTRY = new ExpressionInterceptUrlRegistry();
+
     private SecurityExpressionHandler<FilterInvocation> expressionHandler;
 
     /**
@@ -84,31 +86,59 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
     public ExpressionUrlAuthorizationConfigurer() {
     }
 
-    /**
-     * Allows customization of the {@link SecurityExpressionHandler} to be used. The default is {@link DefaultWebSecurityExpressionHandler}
-     *
-     * @param expressionHandler the {@link SecurityExpressionHandler} to be used
-     * @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization.
-     */
-    public ExpressionUrlAuthorizationConfigurer<H> expressionHandler(SecurityExpressionHandler<FilterInvocation> expressionHandler) {
-        this.expressionHandler = expressionHandler;
-        return this;
+    public ExpressionInterceptUrlRegistry getRegistry() {
+        return REGISTRY;
+    }
+
+    public class ExpressionInterceptUrlRegistry extends ExpressionUrlAuthorizationConfigurer<H>.AbstractInterceptUrlRegistry<ExpressionInterceptUrlRegistry,AuthorizedUrl> {
+
+        @Override
+        protected final AuthorizedUrl chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {
+            return new AuthorizedUrl(requestMatchers);
+        }
+
+
+        /**
+         * Allows customization of the {@link SecurityExpressionHandler} to be used. The default is {@link DefaultWebSecurityExpressionHandler}
+         *
+         * @param expressionHandler the {@link SecurityExpressionHandler} to be used
+         * @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization.
+         */
+        public ExpressionInterceptUrlRegistry expressionHandler(SecurityExpressionHandler<FilterInvocation> expressionHandler) {
+            ExpressionUrlAuthorizationConfigurer.this.expressionHandler = expressionHandler;
+            return this;
+        }
+
+        /**
+         * Adds an {@link ObjectPostProcessor} for this class.
+         *
+         * @param objectPostProcessor
+         * @return the {@link ExpressionUrlAuthorizationConfigurer} for further customizations
+         */
+        public ExpressionInterceptUrlRegistry withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
+            addObjectPostProcessor(objectPostProcessor);
+            return this;
+        }
+
+        public H and() {
+            return ExpressionUrlAuthorizationConfigurer.this.and();
+        }
+
     }
 
+
     /**
-     * Adds an {@link ObjectPostProcessor} for this class.
+     * Allows registering multiple {@link RequestMatcher} instances to a collection of {@link ConfigAttribute} instances
      *
-     * @param objectPostProcessor
-     * @return the {@link ExpressionUrlAuthorizationConfigurer} for further customizations
+     * @param requestMatchers the {@link RequestMatcher} instances to register to the {@link ConfigAttribute} instances
+     * @param configAttributes the {@link ConfigAttribute} to be mapped by the {@link RequestMatcher} instances
+     * @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization.
      */
-    public ExpressionUrlAuthorizationConfigurer<H> withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
-        addObjectPostProcessor(objectPostProcessor);
-        return this;
-    }
-
-    @Override
-    protected final AuthorizedUrl chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {
-        return new AuthorizedUrl(requestMatchers);
+    private ExpressionUrlAuthorizationConfigurer<H> interceptUrl(Iterable<? extends RequestMatcher> requestMatchers, Collection<ConfigAttribute> configAttributes) {
+        for(RequestMatcher requestMatcher : requestMatchers) {
+            REGISTRY.addMapping(new AbstractConfigAttributeRequestMatcherRegistry.UrlMapping(requestMatcher, configAttributes));
+        }
+        return null;
     }
 
     @Override
@@ -123,27 +153,13 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
 
     @Override
     final ExpressionBasedFilterInvocationSecurityMetadataSource createMetadataSource(H http) {
-        LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = createRequestMap();
+        LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = REGISTRY.createRequestMap();
         if(requestMap.isEmpty()) {
             throw new IllegalStateException("At least one mapping is required (i.e. authorizeRequests().anyRequest.authenticated())");
         }
         return new ExpressionBasedFilterInvocationSecurityMetadataSource(requestMap, getExpressionHandler(http));
     }
 
-    /**
-     * Allows registering multiple {@link RequestMatcher} instances to a collection of {@link ConfigAttribute} instances
-     *
-     * @param requestMatchers the {@link RequestMatcher} instances to register to the {@link ConfigAttribute} instances
-     * @param configAttributes the {@link ConfigAttribute} to be mapped by the {@link RequestMatcher} instances
-     * @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization.
-     */
-    private ExpressionUrlAuthorizationConfigurer<H> interceptUrl(Iterable<? extends RequestMatcher> requestMatchers, Collection<ConfigAttribute> configAttributes) {
-        for(RequestMatcher requestMatcher : requestMatchers) {
-            addMapping(new UrlMapping(requestMatcher, configAttributes));
-        }
-        return this;
-    }
-
     private SecurityExpressionHandler<FilterInvocation> getExpressionHandler(H http) {
         if(expressionHandler == null) {
             DefaultWebSecurityExpressionHandler defaultHandler = new DefaultWebSecurityExpressionHandler();
@@ -216,7 +232,7 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
          *             this is automatically inserted.
          * @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization
          */
-        public ExpressionUrlAuthorizationConfigurer<H> hasRole(String role) {
+        public ExpressionInterceptUrlRegistry hasRole(String role) {
             return access(ExpressionUrlAuthorizationConfigurer.hasRole(role));
         }
 
@@ -232,7 +248,7 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
          * @return the {@link ExpressionUrlAuthorizationConfigurer} for further
          *         customization
          */
-        public ExpressionUrlAuthorizationConfigurer<H> hasAnyRole(String... roles) {
+        public ExpressionInterceptUrlRegistry hasAnyRole(String... roles) {
             return access(ExpressionUrlAuthorizationConfigurer.hasAnyRole(roles));
         }
 
@@ -242,7 +258,7 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
          * @param authority the authority to require (i.e. ROLE_USER, ROLE_ADMIN, etc).
          * @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization
          */
-        public ExpressionUrlAuthorizationConfigurer<H> hasAuthority(String authority) {
+        public ExpressionInterceptUrlRegistry hasAuthority(String authority) {
             return access(ExpressionUrlAuthorizationConfigurer.hasAuthority(authority));
         }
 
@@ -253,7 +269,7 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
          *                    mean either "ROLE_USER" or "ROLE_ADMIN" is required).
          * @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization
          */
-        public ExpressionUrlAuthorizationConfigurer<H> hasAnyAuthority(String... authorities) {
+        public ExpressionInterceptUrlRegistry hasAnyAuthority(String... authorities) {
             return access(ExpressionUrlAuthorizationConfigurer.hasAnyAuthority(authorities));
         }
 
@@ -264,7 +280,7 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
          * @param ipaddressExpression the ipaddress (i.e. 192.168.1.79) or local subnet (i.e. 192.168.0/24)
          * @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization
          */
-        public ExpressionUrlAuthorizationConfigurer<H> hasIpAddress(String ipaddressExpression) {
+        public ExpressionInterceptUrlRegistry hasIpAddress(String ipaddressExpression) {
             return access(ExpressionUrlAuthorizationConfigurer.hasIpAddress(ipaddressExpression));
         }
 
@@ -273,7 +289,7 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
          *
          * @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization
          */
-        public ExpressionUrlAuthorizationConfigurer<H> permitAll() {
+        public ExpressionInterceptUrlRegistry permitAll() {
             return access(permitAll);
         }
 
@@ -282,7 +298,7 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
          *
          * @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization
          */
-        public ExpressionUrlAuthorizationConfigurer<H> anonymous() {
+        public ExpressionInterceptUrlRegistry anonymous() {
             return access(anonymous);
         }
 
@@ -292,7 +308,7 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
          * @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization
          * @see {@link RememberMeConfigurer}
          */
-        public ExpressionUrlAuthorizationConfigurer<H> rememberMe() {
+        public ExpressionInterceptUrlRegistry rememberMe() {
             return access(rememberMe);
         }
 
@@ -301,7 +317,7 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
          *
          * @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization
          */
-        public ExpressionUrlAuthorizationConfigurer<H> denyAll() {
+        public ExpressionInterceptUrlRegistry denyAll() {
             return access(denyAll);
         }
 
@@ -310,7 +326,7 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
          *
          * @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization
          */
-        public ExpressionUrlAuthorizationConfigurer<H> authenticated() {
+        public ExpressionInterceptUrlRegistry authenticated() {
             return access(authenticated);
         }
 
@@ -320,7 +336,7 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
          * @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization
          * @see {@link RememberMeConfigurer}
          */
-        public ExpressionUrlAuthorizationConfigurer<H> fullyAuthenticated() {
+        public ExpressionInterceptUrlRegistry fullyAuthenticated() {
             return access(fullyAuthenticated);
         }
 
@@ -330,12 +346,12 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
          * @param attribute the expression to secure the URLs (i.e. "hasRole('ROLE_USER') and hasRole('ROLE_SUPER')")
          * @return the {@link ExpressionUrlAuthorizationConfigurer} for further customization
          */
-        public ExpressionUrlAuthorizationConfigurer<H> access(String attribute) {
+        public ExpressionInterceptUrlRegistry access(String attribute) {
             if(not) {
                 attribute = "!" + attribute;
             }
             interceptUrl(requestMatchers, SecurityConfig.createList(attribute));
-            return ExpressionUrlAuthorizationConfigurer.this;
+            return ExpressionUrlAuthorizationConfigurer.this.REGISTRY;
         }
     }
 }

+ 2 - 2
config/src/main/java/org/springframework/security/config/annotation/web/configurers/PermitAllSupport.java

@@ -20,7 +20,7 @@ import javax.servlet.http.HttpServletRequest;
 
 import org.springframework.security.access.SecurityConfig;
 import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
-import org.springframework.security.config.annotation.web.configurers.AbstractRequestMatcherMappingConfigurer.UrlMapping;
+import org.springframework.security.config.annotation.web.configurers.AbstractConfigAttributeRequestMatcherRegistry.UrlMapping;
 import org.springframework.security.web.util.matcher.RequestMatcher;
 
 
@@ -49,7 +49,7 @@ final class PermitAllSupport {
 
         for(RequestMatcher matcher : requestMatchers) {
             if(matcher != null) {
-                configurer.addMapping(0, new UrlMapping(matcher, SecurityConfig.createList(ExpressionUrlAuthorizationConfigurer.permitAll)));
+                configurer.getRegistry().addMapping(0, new UrlMapping(matcher, SecurityConfig.createList(ExpressionUrlAuthorizationConfigurer.permitAll)));
             }
         }
     }

+ 84 - 31
config/src/main/java/org/springframework/security/config/annotation/web/configurers/UrlAuthorizationConfigurer.java

@@ -34,22 +34,44 @@ import org.springframework.util.Assert;
 
 
 /**
- * Adds URL based authorization using {@link DefaultFilterInvocationSecurityMetadataSource}. At least one
- * {@link org.springframework.web.bind.annotation.RequestMapping} needs to be mapped to {@link ConfigAttribute}'s for
- * this {@link SecurityContextConfigurer} to have meaning.
- * <h2>Security Filters</h2>
+ * Adds URL based authorization using
+ * {@link DefaultFilterInvocationSecurityMetadataSource}. At least one
+ * {@link org.springframework.web.bind.annotation.RequestMapping} needs to be
+ * mapped to {@link ConfigAttribute}'s for this
+ * {@link SecurityContextConfigurer} to have meaning. <h2>Security Filters</h2>
+ *
+ * <p>
+ * Usage includes applying the {@link UrlAuthorizationConfigurer} and then
+ * modifying the StandardInterceptUrlRegistry. For example:
+ * </p>
+ *
+ * <pre>
+ * protected void configure(HttpSecurity http) throws Exception {
+ *     http
+ *         .apply(new UrlAuthorizationConfigurer()).getRegistry()
+ *             .antMatchers("/users**","/sessions/**").hasRole("USER")
+ *             .antMatchers("/signup").hasRole("ANONYMOUS")
+ *             .anyRequest().hasRole("USER")
+ * }
+ * </pre>
  *
  * The following Filters are populated
  *
  * <ul>
- *     <li>{@link org.springframework.security.web.access.intercept.FilterSecurityInterceptor}</li>
+ * <li>
+ * {@link org.springframework.security.web.access.intercept.FilterSecurityInterceptor}
+ * </li>
  * </ul>
  *
  * <h2>Shared Objects Created</h2>
  *
- * The following shared objects are populated to allow other {@link org.springframework.security.config.annotation.SecurityConfigurer}'s to customize:
+ * The following shared objects are populated to allow other
+ * {@link org.springframework.security.config.annotation.SecurityConfigurer}'s
+ * to customize:
  * <ul>
- *     <li>{@link org.springframework.security.web.access.intercept.FilterSecurityInterceptor}</li>
+ * <li>
+ * {@link org.springframework.security.web.access.intercept.FilterSecurityInterceptor}
+ * </li>
  * </ul>
  *
  * <h2>Shared Objects Used</h2>
@@ -57,17 +79,32 @@ import org.springframework.util.Assert;
  * The following shared objects are used:
  *
  * <ul>
- *     <li>{@link org.springframework.security.config.annotation.web.builders.HttpSecurity#getAuthenticationManager()}</li>
+ * <li>
+ * {@link org.springframework.security.config.annotation.web.builders.HttpSecurity#getAuthenticationManager()}
+ * </li>
  * </ul>
  *
- * @param <H> the type of {@link HttpSecurityBuilder} that is being configured
- * @param <C> the type of object that is being chained
+ * @param <H>
+ *            the type of {@link HttpSecurityBuilder} that is being configured
+ * @param <C>
+ *            the type of object that is being chained
  *
  * @author Rob Winch
  * @since 3.2
  * @see ExpressionUrlAuthorizationConfigurer
  */
-public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>, C> extends AbstractInterceptUrlConfigurer<H,C,UrlAuthorizationConfigurer<H,C>.AuthorizedUrl> {
+public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractInterceptUrlConfigurer<UrlAuthorizationConfigurer<H>,H> {
+    private final StandardInterceptUrlRegistry REGISTRY = new StandardInterceptUrlRegistry();
+
+    /**
+     * The StandardInterceptUrlRegistry is what users will interact with after
+     * applying the {@link UrlAuthorizationConfigurer}.
+     *
+     * @return
+     */
+    public StandardInterceptUrlRegistry getRegistry() {
+        return REGISTRY;
+    }
 
     /**
      * Adds an {@link ObjectPostProcessor} for this class.
@@ -75,11 +112,35 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>,
      * @param objectPostProcessor
      * @return the {@link UrlAuthorizationConfigurer} for further customizations
      */
-    public UrlAuthorizationConfigurer<H,C> withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
+    public UrlAuthorizationConfigurer<H> withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
         addObjectPostProcessor(objectPostProcessor);
         return this;
     }
 
+    public class StandardInterceptUrlRegistry extends ExpressionUrlAuthorizationConfigurer<H>.AbstractInterceptUrlRegistry<StandardInterceptUrlRegistry,AuthorizedUrl> {
+
+        @Override
+        protected final AuthorizedUrl chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {
+            return new AuthorizedUrl(requestMatchers);
+        }
+
+        /**
+         * Adds an {@link ObjectPostProcessor} for this class.
+         *
+         * @param objectPostProcessor
+         * @return the {@link ExpressionUrlAuthorizationConfigurer} for further customizations
+         */
+        public StandardInterceptUrlRegistry withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
+            addObjectPostProcessor(objectPostProcessor);
+            return this;
+        }
+
+        public H and() {
+            return UrlAuthorizationConfigurer.this.and();
+        }
+
+    }
+
     /**
      * Creates the default {@link AccessDecisionVoter} instances used if an
      * {@link AccessDecisionManager} was not specified using
@@ -104,15 +165,7 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>,
      */
     @Override
     FilterInvocationSecurityMetadataSource createMetadataSource(H http) {
-        return new DefaultFilterInvocationSecurityMetadataSource(createRequestMap());
-    }
-
-    /**
-     * Chains the {@link RequestMatcher} creation to the {@link AuthorizedUrl} class.
-     */
-    @Override
-    protected AuthorizedUrl chainRequestMatchersInternal(List<RequestMatcher> requestMatchers) {
-        return new AuthorizedUrl(requestMatchers);
+        return new DefaultFilterInvocationSecurityMetadataSource(REGISTRY.createRequestMap());
     }
 
     /**
@@ -121,11 +174,11 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>,
      * @param configAttributes the {@link ConfigAttribute} instances that should be mapped by the {@link RequestMatcher} instances
      * @return the {@link UrlAuthorizationConfigurer} for further customizations
      */
-    private UrlAuthorizationConfigurer<H,C> addMapping(Iterable<? extends RequestMatcher> requestMatchers, Collection<ConfigAttribute> configAttributes) {
+    private StandardInterceptUrlRegistry addMapping(Iterable<? extends RequestMatcher> requestMatchers, Collection<ConfigAttribute> configAttributes) {
         for(RequestMatcher requestMatcher : requestMatchers) {
-            addMapping(new UrlMapping(requestMatcher, configAttributes));
+            REGISTRY.addMapping(new AbstractConfigAttributeRequestMatcherRegistry.UrlMapping(requestMatcher, configAttributes));
         }
-        return this;
+        return REGISTRY;
     }
 
     /**
@@ -198,7 +251,7 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>,
          *            with ROLE_
          * the {@link UrlAuthorizationConfigurer} for further customization
          */
-        public UrlAuthorizationConfigurer<H,C> hasRole(String role) {
+        public StandardInterceptUrlRegistry hasRole(String role) {
             return access(UrlAuthorizationConfigurer.hasRole(role));
         }
 
@@ -211,7 +264,7 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>,
          *            it is automatically prepended already.
          * @return the {@link UrlAuthorizationConfigurer} for further customization
          */
-        public UrlAuthorizationConfigurer<H,C> hasAnyRole(String... roles) {
+        public StandardInterceptUrlRegistry hasAnyRole(String... roles) {
             return access(UrlAuthorizationConfigurer.hasAnyRole(roles));
         }
 
@@ -222,7 +275,7 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>,
          *            the authority that should be required
          * @return the {@link UrlAuthorizationConfigurer} for further customization
          */
-        public UrlAuthorizationConfigurer<H,C> hasAuthority(String authority) {
+        public StandardInterceptUrlRegistry hasAuthority(String authority) {
             return access(authority);
         }
 
@@ -231,7 +284,7 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>,
          * @param authorities the authorities that the user should have at least one of (i.e. ROLE_USER, ROLE_ADMIN, etc).
          * @return the {@link UrlAuthorizationConfigurer} for further customization
          */
-        public UrlAuthorizationConfigurer<H,C> hasAnyAuthority(String... authorities) {
+        public StandardInterceptUrlRegistry hasAnyAuthority(String... authorities) {
             return access(UrlAuthorizationConfigurer.hasAnyAuthority(authorities));
         }
 
@@ -239,7 +292,7 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>,
          * Specifies that an anonymous user is allowed access
          * @return the {@link UrlAuthorizationConfigurer} for further customization
          */
-        public UrlAuthorizationConfigurer<H,C> anonymous() {
+        public StandardInterceptUrlRegistry anonymous() {
             return hasRole("ROLE_ANONYMOUS");
         }
 
@@ -248,9 +301,9 @@ public final class UrlAuthorizationConfigurer<H extends HttpSecurityBuilder<H>,
          * @param attributes the {@link ConfigAttribute}'s that restrict access to a URL
          * @return the {@link UrlAuthorizationConfigurer} for further customization
          */
-        public UrlAuthorizationConfigurer<H,C> access(String... attributes) {
+        public StandardInterceptUrlRegistry access(String... attributes) {
             addMapping(requestMatchers, SecurityConfig.createList(attributes));
-            return UrlAuthorizationConfigurer.this;
+            return UrlAuthorizationConfigurer.this.REGISTRY;
         }
     }
 }

+ 1 - 1
config/src/test/groovy/org/springframework/security/config/annotation/web/RequestMatchersTests.groovy

@@ -15,7 +15,7 @@
  */
 package org.springframework.security.config.annotation.web;
 
-import static org.springframework.security.config.annotation.web.AbstractRequestMatcherConfigurer.RequestMatchers.*
+import static org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.RequestMatchers.*
 
 import org.springframework.http.HttpMethod;
 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

+ 1 - 1
config/src/test/groovy/org/springframework/security/config/annotation/web/builders/NamespaceHttpTests.groovy

@@ -507,7 +507,7 @@ public class NamespaceHttpTests extends BaseSpringSpec {
     static class DisableUseExpressionsConfig extends BaseWebConfig {
         protected void configure(HttpSecurity http) throws Exception {
             http
-                .apply(new UrlAuthorizationConfigurer())
+                .apply(new UrlAuthorizationConfigurer()).getRegistry()
                     .antMatchers("/users**","/sessions/**").hasRole("USER")
                     .antMatchers("/signup").hasRole("ANONYMOUS")
                     .anyRequest().hasRole("USER")

+ 9 - 14
config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/AbstractRequestMatcherMappingConfigurerTests.groovy → config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/AbstractConfigAttributeRequestMatcherRegistryTests.groovy

@@ -15,25 +15,20 @@
  */
 package org.springframework.security.config.annotation.web.configurers;
 
-import java.util.List;
+import org.springframework.http.HttpMethod
+import org.springframework.security.access.AccessDecisionVoter
+import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher
+import org.springframework.security.web.util.matcher.RegexRequestMatcher
+import org.springframework.security.web.util.matcher.RequestMatcher
 
-import org.springframework.http.HttpMethod;
-import org.springframework.security.access.AccessDecisionVoter;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.configurers.AbstractRequestMatcherMappingConfigurer;
-import org.springframework.security.web.DefaultSecurityFilterChain;
-import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
-import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
-import org.springframework.security.web.util.matcher.RegexRequestMatcher;
-import org.springframework.security.web.util.matcher.RequestMatcher;
-
-import spock.lang.Specification;
+import spock.lang.Specification
 
 /**
  * @author Rob Winch
  *
  */
-class AbstractRequestMatcherMappingConfigurerTests extends Specification {
+class AbstractConfigAttributeRequestMatcherRegistryTests extends Specification {
     ConcreteAbstractRequestMatcherMappingConfigurer registry = new ConcreteAbstractRequestMatcherMappingConfigurer()
 
     def "regexMatchers(GET,'/a.*') uses RegexRequestMatcher"() {
@@ -64,7 +59,7 @@ class AbstractRequestMatcherMappingConfigurerTests extends Specification {
         matchers.collect {it.class } == [AntPathRequestMatcher]
     }
 
-    static class ConcreteAbstractRequestMatcherMappingConfigurer extends AbstractRequestMatcherMappingConfigurer<HttpSecurity,List<RequestMatcher>,DefaultSecurityFilterChain> {
+    static class ConcreteAbstractRequestMatcherMappingConfigurer extends AbstractConfigAttributeRequestMatcherRegistry<List<RequestMatcher>> {
         List<AccessDecisionVoter> decisionVoters() {
             return null;
         }

+ 63 - 0
config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationConfigurerConfigs.java

@@ -0,0 +1,63 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.config.annotation.web.configurers;
+
+
+import java.util.Arrays;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.access.AccessDecisionVoter;
+import org.springframework.security.access.expression.SecurityExpressionHandler;
+import org.springframework.security.access.vote.AffirmativeBased;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.web.FilterInvocation;
+import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
+import org.springframework.security.web.access.expression.WebExpressionVoter;
+
+/**
+ *
+ * @author Rob Winch
+ *
+ */
+public class ExpressionUrlAuthorizationConfigurerConfigs {
+
+    /**
+     * Ensure that All additional properties properly compile and chain properly
+     */
+    @EnableWebSecurity
+    @Configuration
+    static class AllPropertiesWorkConfig extends WebSecurityConfigurerAdapter {
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        protected void configure(HttpSecurity http) throws Exception {
+            SecurityExpressionHandler<FilterInvocation> handler = new DefaultWebSecurityExpressionHandler();
+            WebExpressionVoter expressionVoter = new WebExpressionVoter();
+            AffirmativeBased adm = new AffirmativeBased(Arrays.<AccessDecisionVoter>asList(expressionVoter));
+            http
+                .authorizeRequests()
+                    .expressionHandler(handler)
+                    .accessDecisionManager(adm)
+                    .filterSecurityInterceptorOncePerRequest(true)
+                    .antMatchers("/a","/b").hasRole("ADMIN")
+                    .anyRequest().permitAll()
+                    .and()
+                .formLogin();
+        }
+    }
+}

+ 9 - 0
config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/ExpressionUrlAuthorizationsTests.groovy

@@ -15,6 +15,8 @@
  */
 package org.springframework.security.config.annotation.web.configurers;
 
+import static org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurerConfigs.*
+
 import javax.servlet.http.HttpServletResponse
 
 import org.springframework.beans.factory.BeanCreationException
@@ -453,4 +455,11 @@ public class ExpressionUrlAuthorizationConfigurerTests extends BaseSpringSpec {
                 .inMemoryAuthentication()
         }
     }
+
+    def "All Properties are accessible and chain properly"() {
+        when:
+            loadConfig(AllPropertiesWorkConfig)
+        then:
+            noExceptionThrown()
+    }
 }

+ 2 - 1
config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/UrlAuthorizationsTests.groovy

@@ -72,7 +72,8 @@ public class UrlAuthorizationsTests extends BaseSpringSpec {
     static class NoSpecificAccessDecessionManagerConfig extends WebSecurityConfigurerAdapter {
         protected void configure(HttpSecurity http) throws Exception {
             http
-                .apply(new UrlAuthorizationConfigurer())
+                .apply(new UrlAuthorizationConfigurer()).getRegistry()
+                    .antMatchers("/a").hasRole("ADMIN")
                     .anyRequest().hasRole("USER")
         }
     }