mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-05-31 01:02:14 +00:00
Add intercept-url@request-matcher-ref
Fixes gh-4097
This commit is contained in:
parent
f019ea89e7
commit
af9139b613
@ -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<BeanDefinition, BeanDefinition> requestToAttributesMap = parseInterceptUrlsForFilterInvocationRequestMap(
|
||||
ManagedMap<BeanMetadataElement, BeanDefinition> 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<BeanDefinition, BeanDefinition> parseInterceptUrlsForFilterInvocationRequestMap(
|
||||
private static ManagedMap<BeanMetadataElement, BeanDefinition> parseInterceptUrlsForFilterInvocationRequestMap(
|
||||
MatcherType matcherType, List<Element> urlElts, boolean useExpressions,
|
||||
boolean addAuthenticatedAll, ParserContext parserContext) {
|
||||
|
||||
ManagedMap<BeanDefinition, BeanDefinition> filterInvocationDefinitionMap = new ManagedMap<BeanDefinition, BeanDefinition>();
|
||||
ManagedMap<BeanMetadataElement, BeanDefinition> filterInvocationDefinitionMap = new ManagedMap<BeanMetadataElement, BeanDefinition>();
|
||||
|
||||
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);
|
||||
|
@ -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<BeanDefinition, BeanDefinition> channelRequestMap = parseInterceptUrlsForChannelSecurity();
|
||||
ManagedMap<BeanMetadataElement, BeanDefinition> channelRequestMap = parseInterceptUrlsForChannelSecurity();
|
||||
|
||||
if (channelRequestMap.isEmpty()) {
|
||||
return;
|
||||
@ -636,15 +637,17 @@ class HttpConfigurationBuilder {
|
||||
* will be empty unless the <tt>requires-channel</tt> attribute has been used on a URL
|
||||
* path.
|
||||
*/
|
||||
private ManagedMap<BeanDefinition, BeanDefinition> parseInterceptUrlsForChannelSecurity() {
|
||||
private ManagedMap<BeanMetadataElement, BeanDefinition> parseInterceptUrlsForChannelSecurity() {
|
||||
|
||||
ManagedMap<BeanDefinition, BeanDefinition> channelRequestMap = new ManagedMap<BeanDefinition, BeanDefinition>();
|
||||
ManagedMap<BeanMetadataElement, BeanDefinition> channelRequestMap = new ManagedMap<BeanMetadataElement, BeanDefinition>();
|
||||
|
||||
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);
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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}?
|
||||
|
@ -1292,10 +1292,15 @@
|
||||
</xs:attributeGroup>
|
||||
|
||||
<xs:attributeGroup name="intercept-url.attlist">
|
||||
<xs:attribute name="pattern" use="required" type="xs:token">
|
||||
<xs:attribute name="pattern" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>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.
|
||||
<xs:documentation>The request URL pattern which will be mapped to the FilterChain.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="request-matcher-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Allows a RequestMatcher instance to be used, as an alternative to pattern-matching.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
|
@ -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<configLocations.length;i++) {
|
||||
configLocations[i] = getClass().getName().replaceAll("\\.", "/") + "-" + configLocations[i];
|
||||
}
|
||||
XmlWebApplicationContext context = new XmlWebApplicationContext();
|
||||
context.setConfigLocations(configLocations);
|
||||
context.setServletContext(new MockServletContext());
|
||||
context.refresh();
|
||||
this.context = context;
|
||||
|
||||
context.getAutowireCapableBeanFactory().autowireBean(this);
|
||||
|
||||
Filter springSecurityFilterChain = context.getBean("springSecurityFilterChain", Filter.class);
|
||||
mockMvc = MockMvcBuilders
|
||||
.standaloneSetup(new FooController())
|
||||
.addFilters(springSecurityFilterChain)
|
||||
.build();
|
||||
}
|
||||
|
||||
@RestController
|
||||
static class FooController {
|
||||
@GetMapping("/*")
|
||||
String foo() {
|
||||
return "foo";
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://www.springframework.org/schema/security"
|
||||
xmlns:c="http://www.springframework.org/schema/c"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<http>
|
||||
<http-basic/>
|
||||
<intercept-url request-matcher-ref="matcherRef" access="denyAll"/>
|
||||
</http>
|
||||
|
||||
<user-service>
|
||||
<user name="user" password="password" authorities="ROLE_USER"/>
|
||||
</user-service>
|
||||
|
||||
<b:bean id="matcherRef" class="org.springframework.security.web.util.matcher.AntPathRequestMatcher"
|
||||
c:pattern="/foo"
|
||||
c:httpMethod="GET"
|
||||
c:caseSensitive="false"/>
|
||||
</b:beans>
|
@ -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 `<intercept-url>` 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 `<intercept-url>` element, then a `ChannelProcessingFilter` will be added to the filter stack and its additional dependencies added to the application context.
|
||||
|
Loading…
x
Reference in New Issue
Block a user