diff --git a/config/src/main/java/org/springframework/security/config/http/FilterInvocationSecurityMetadataSourceParser.java b/config/src/main/java/org/springframework/security/config/http/FilterInvocationSecurityMetadataSourceParser.java index 955a4d6b6f..28194908ff 100644 --- a/config/src/main/java/org/springframework/security/config/http/FilterInvocationSecurityMetadataSourceParser.java +++ b/config/src/main/java/org/springframework/security/config/http/FilterInvocationSecurityMetadataSourceParser.java @@ -15,13 +15,16 @@ */ package org.springframework.security.config.http; +import static org.springframework.security.config.http.HttpSecurityBeanDefinitionParser.ATT_REQUEST_MATCHER_REF; + import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.w3c.dom.Element; - +import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.ManagedMap; @@ -99,7 +102,7 @@ public class FilterInvocationSecurityMetadataSourceParser implements BeanDefinit MatcherType matcherType = MatcherType.fromElement(httpElt); boolean useExpressions = isUseExpressions(httpElt); - ManagedMap requestToAttributesMap = parseInterceptUrlsForFilterInvocationRequestMap( + ManagedMap requestToAttributesMap = parseInterceptUrlsForFilterInvocationRequestMap( matcherType, interceptUrls, useExpressions, addAllAuth, pc); BeanDefinitionBuilder fidsBuilder; @@ -148,11 +151,11 @@ public class FilterInvocationSecurityMetadataSourceParser implements BeanDefinit return !StringUtils.hasText(useExpressions) || "true".equals(useExpressions); } - private static ManagedMap parseInterceptUrlsForFilterInvocationRequestMap( + private static ManagedMap parseInterceptUrlsForFilterInvocationRequestMap( MatcherType matcherType, List urlElts, boolean useExpressions, boolean addAuthenticatedAll, ParserContext parserContext) { - ManagedMap filterInvocationDefinitionMap = new ManagedMap(); + ManagedMap filterInvocationDefinitionMap = new ManagedMap(); for (Element urlElt : urlElts) { String access = urlElt.getAttribute(ATT_ACCESS); @@ -161,8 +164,10 @@ public class FilterInvocationSecurityMetadataSourceParser implements BeanDefinit } String path = urlElt.getAttribute(ATT_PATTERN); + String matcherRef = urlElt.getAttribute(ATT_REQUEST_MATCHER_REF); + boolean hasMatcherRef = StringUtils.hasText(matcherRef); - if (!StringUtils.hasText(path)) { + if (!hasMatcherRef && !StringUtils.hasText(path)) { parserContext.getReaderContext().error( "path attribute cannot be empty or null", urlElt); } @@ -180,7 +185,7 @@ public class FilterInvocationSecurityMetadataSourceParser implements BeanDefinit ATT_SERVLET_PATH + " is not applicable for request-matcher: '" + matcherType.name() + "'", urlElt); } - BeanDefinition matcher = matcherType.createMatcher(parserContext, path, + BeanMetadataElement matcher = hasMatcherRef ? new RuntimeBeanReference(matcherRef) : matcherType.createMatcher(parserContext, path, method, servletPath); BeanDefinitionBuilder attributeBuilder = BeanDefinitionBuilder .rootBeanDefinition(SecurityConfig.class); diff --git a/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java b/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java index acc9ff7129..dcfb6cd3e8 100644 --- a/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java +++ b/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java @@ -80,6 +80,7 @@ import org.springframework.util.xml.DomUtils; import static org.springframework.security.config.http.HttpSecurityBeanDefinitionParser.ATT_FILTERS; import static org.springframework.security.config.http.HttpSecurityBeanDefinitionParser.ATT_HTTP_METHOD; import static org.springframework.security.config.http.HttpSecurityBeanDefinitionParser.ATT_PATH_PATTERN; +import static org.springframework.security.config.http.HttpSecurityBeanDefinitionParser.ATT_REQUEST_MATCHER_REF; import static org.springframework.security.config.http.HttpSecurityBeanDefinitionParser.ATT_REQUIRES_CHANNEL; import static org.springframework.security.config.http.SecurityFilters.CHANNEL_FILTER; import static org.springframework.security.config.http.SecurityFilters.CONCURRENT_SESSION_FILTER; @@ -582,7 +583,7 @@ class HttpConfigurationBuilder { } private void createChannelProcessingFilter() { - ManagedMap channelRequestMap = parseInterceptUrlsForChannelSecurity(); + ManagedMap channelRequestMap = parseInterceptUrlsForChannelSecurity(); if (channelRequestMap.isEmpty()) { return; @@ -636,15 +637,17 @@ class HttpConfigurationBuilder { * will be empty unless the requires-channel attribute has been used on a URL * path. */ - private ManagedMap parseInterceptUrlsForChannelSecurity() { + private ManagedMap parseInterceptUrlsForChannelSecurity() { - ManagedMap channelRequestMap = new ManagedMap(); + ManagedMap channelRequestMap = new ManagedMap(); for (Element urlElt : interceptUrls) { String path = urlElt.getAttribute(ATT_PATH_PATTERN); String method = urlElt.getAttribute(ATT_HTTP_METHOD); + String matcherRef = urlElt.getAttribute(ATT_REQUEST_MATCHER_REF); + boolean hasMatcherRef = StringUtils.hasText(matcherRef); - if (!StringUtils.hasText(path)) { + if (!hasMatcherRef && !StringUtils.hasText(path)) { pc.getReaderContext().error("pattern attribute cannot be empty or null", urlElt); } @@ -652,7 +655,7 @@ class HttpConfigurationBuilder { String requiredChannel = urlElt.getAttribute(ATT_REQUIRES_CHANNEL); if (StringUtils.hasText(requiredChannel)) { - BeanDefinition matcher = matcherType.createMatcher(pc, path, method); + BeanMetadataElement matcher = hasMatcherRef ? new RuntimeBeanReference(matcherRef) : matcherType.createMatcher(pc, path, method); RootBeanDefinition channelAttributes = new RootBeanDefinition( ChannelAttributeFactory.class); diff --git a/config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java index 4f8796746f..ad83f098ae 100644 --- a/config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java @@ -60,7 +60,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { .getLog(HttpSecurityBeanDefinitionParser.class); private static final String ATT_AUTHENTICATION_MANAGER_REF = "authentication-manager-ref"; - private static final String ATT_REQUEST_MATCHER_REF = "request-matcher-ref"; + static final String ATT_REQUEST_MATCHER_REF = "request-matcher-ref"; static final String ATT_PATH_PATTERN = "pattern"; static final String ATT_HTTP_METHOD = "method"; diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-4.2.rnc b/config/src/main/resources/org/springframework/security/config/spring-security-4.2.rnc index 7a90ec0fd0..3e1ec9ce67 100644 --- a/config/src/main/resources/org/springframework/security/config/spring-security-4.2.rnc +++ b/config/src/main/resources/org/springframework/security/config/spring-security-4.2.rnc @@ -366,8 +366,7 @@ intercept-url = ## Specifies the access attributes and/or filter list for a particular set of URLs. element intercept-url {intercept-url.attlist, empty} intercept-url.attlist &= - ## The pattern which defines the URL path. The content will depend on the type set in the containing http element, so will default to ant path syntax. - attribute pattern {xsd:token} + (pattern | request-matcher-ref) intercept-url.attlist &= ## The access configuration attributes that apply for the configured path. attribute access {xsd:token}? diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-4.2.xsd b/config/src/main/resources/org/springframework/security/config/spring-security-4.2.xsd index af391a9356..0a6a27f22a 100644 --- a/config/src/main/resources/org/springframework/security/config/spring-security-4.2.xsd +++ b/config/src/main/resources/org/springframework/security/config/spring-security-4.2.xsd @@ -1292,10 +1292,15 @@ - + - The pattern which defines the URL path. The content will depend on the type set in the - containing http element, so will default to ant path syntax. + The request URL pattern which will be mapped to the FilterChain. + + + + + + Allows a RequestMatcher instance to be used, as an alternative to pattern-matching. diff --git a/config/src/test/java/org/springframework/security/config/http/HttpInterceptUrlTests.java b/config/src/test/java/org/springframework/security/config/http/HttpInterceptUrlTests.java new file mode 100644 index 0000000000..e8c2a0c027 --- /dev/null +++ b/config/src/test/java/org/springframework/security/config/http/HttpInterceptUrlTests.java @@ -0,0 +1,85 @@ +/* + * Copyright 2002-2016 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.http; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import javax.servlet.Filter; + +import org.junit.After; +import org.junit.Test; +import org.springframework.mock.web.MockServletContext; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.ConfigurableWebApplicationContext; +import org.springframework.web.context.support.XmlWebApplicationContext; + +public class HttpInterceptUrlTests { + ConfigurableWebApplicationContext context; + + MockMvc mockMvc; + + @After + public void close() { + if(context != null) { + context.close(); + } + } + + @Test + public void interceptUrlWhenRequestMatcherRefThenWorks() throws Exception { + loadConfig("interceptUrlWhenRequestMatcherRefThenWorks.xml"); + + mockMvc.perform(get("/foo")) + .andExpect(status().isUnauthorized()); + + mockMvc.perform(get("/FOO")) + .andExpect(status().isUnauthorized()); + + mockMvc.perform(get("/other")) + .andExpect(status().isOk()); + } + + private void loadConfig(String... configLocations) { + for(int i=0;i + + + + + + + + + + + + + diff --git a/docs/manual/src/docs/asciidoc/index.adoc b/docs/manual/src/docs/asciidoc/index.adoc index e2cd544c45..485a6519d8 100644 --- a/docs/manual/src/docs/asciidoc/index.adoc +++ b/docs/manual/src/docs/asciidoc/index.adoc @@ -8197,6 +8197,11 @@ The HTTP Method which will be used in combination with the pattern and servlet p The pattern which defines the URL path. The content will depend on the `request-matcher` attribute from the containing http element, so will default to ant path syntax. +[[nsa-intercept-url-request-matcher-ref]] +* **request-matcher-ref** +A reference to a `RequestMatcher` that will be used to determine if this `` is used. + + [[nsa-intercept-url-requires-channel]] * **requires-channel** Can be "http" or "https" depending on whether a particular URL pattern should be accessed over HTTP or HTTPS respectively. Alternatively the value "any" can be used when there is no preference. If this attribute is present on any `` element, then a `ChannelProcessingFilter` will be added to the filter stack and its additional dependencies added to the application context.