Skip to content

Commit c4d23f2

Browse files
Use MvcRequestMatcher by default if Spring MVC is present
Closes gh-11899
1 parent 353ca76 commit c4d23f2

File tree

82 files changed

+391
-177
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+391
-177
lines changed

config/src/main/java/org/springframework/security/config/http/AuthorizationFilterParser.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -108,7 +108,7 @@ private String createAuthorizationManager(Element element, ParserContext parserC
108108
if (expressionHandlerRef == null) {
109109
expressionHandlerRef = registerDefaultExpressionHandler(parserContext);
110110
}
111-
MatcherType matcherType = MatcherType.fromElement(element);
111+
MatcherType matcherType = MatcherType.fromElementOrMvc(element);
112112
ManagedMap<BeanMetadataElement, BeanDefinition> matcherToExpression = new ManagedMap<>();
113113
List<Element> interceptMessages = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_URL);
114114
for (Element interceptMessage : interceptMessages) {

config/src/main/java/org/springframework/security/config/http/FilterChainBeanDefinitionParser.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -39,7 +39,7 @@ public class FilterChainBeanDefinitionParser implements BeanDefinitionParser {
3939

4040
@Override
4141
public BeanDefinition parse(Element elt, ParserContext pc) {
42-
MatcherType matcherType = MatcherType.fromElement(elt);
42+
MatcherType matcherType = MatcherType.fromElementOrMvc(elt);
4343
String path = elt.getAttribute(HttpSecurityBeanDefinitionParser.ATT_PATH_PATTERN);
4444
String requestMatcher = elt.getAttribute(ATT_REQUEST_MATCHER_REF);
4545
String filters = elt.getAttribute(HttpSecurityBeanDefinitionParser.ATT_FILTERS);

config/src/main/java/org/springframework/security/config/http/FilterChainMapBeanDefinitionDecorator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -47,7 +47,7 @@ public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder holder, Par
4747
BeanDefinition filterChainProxy = holder.getBeanDefinition();
4848
ManagedList<BeanMetadataElement> securityFilterChains = new ManagedList<>();
4949
Element elt = (Element) node;
50-
MatcherType matcherType = MatcherType.fromElement(elt);
50+
MatcherType matcherType = MatcherType.fromElementOrMvc(elt);
5151
List<Element> filterChainElts = DomUtils.getChildElementsByTagName(elt, Elements.FILTER_CHAIN);
5252
for (Element chain : filterChainElts) {
5353
String path = chain.getAttribute(HttpSecurityBeanDefinitionParser.ATT_PATH_PATTERN);

config/src/main/java/org/springframework/security/config/http/FilterInvocationSecurityMetadataSourceParser.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public BeanDefinition parse(Element element, ParserContext parserContext) {
9393

9494
static RootBeanDefinition createSecurityMetadataSource(List<Element> interceptUrls, boolean addAllAuth,
9595
Element httpElt, ParserContext pc) {
96-
MatcherType matcherType = MatcherType.fromElement(httpElt);
96+
MatcherType matcherType = MatcherType.fromElementOrMvc(httpElt);
9797
boolean useExpressions = isUseExpressions(httpElt);
9898
ManagedMap<BeanMetadataElement, BeanDefinition> requestToAttributesMap = parseInterceptUrlsForFilterInvocationRequestMap(
9999
matcherType, interceptUrls, useExpressions, addAllAuth, pc);

config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ class HttpConfigurationBuilder {
217217
this.pc = pc;
218218
this.portMapper = portMapper;
219219
this.portResolver = portResolver;
220-
this.matcherType = MatcherType.fromElement(element);
220+
this.matcherType = MatcherType.fromElementOrMvc(element);
221221
this.interceptUrls = DomUtils.getChildElementsByTagName(element, Elements.INTERCEPT_URL);
222222
validateInterceptUrls(pc);
223223
String createSession = element.getAttribute(ATT_CREATE_SESSION);

config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -196,7 +196,7 @@ private BeanReference createSecurityFilterChainBean(Element element, ParserConte
196196

197197
}
198198
else if (StringUtils.hasText(filterChainPattern)) {
199-
filterChainMatcher = MatcherType.fromElement(element).createMatcher(pc, filterChainPattern, null);
199+
filterChainMatcher = MatcherType.fromElementOrMvc(element).createMatcher(pc, filterChainPattern, null);
200200
}
201201
else {
202202
filterChainMatcher = new RootBeanDefinition(AnyRequestMatcher.class);

config/src/main/java/org/springframework/security/config/http/MatcherType.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,11 +22,13 @@
2222
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
2323
import org.springframework.beans.factory.support.RootBeanDefinition;
2424
import org.springframework.beans.factory.xml.ParserContext;
25+
import org.springframework.http.HttpMethod;
2526
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
2627
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
2728
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
2829
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
2930
import org.springframework.security.web.util.matcher.RequestMatcher;
31+
import org.springframework.util.ClassUtils;
3032
import org.springframework.util.StringUtils;
3133

3234
/**
@@ -40,12 +42,18 @@ public enum MatcherType {
4042
ant(AntPathRequestMatcher.class), regex(RegexRequestMatcher.class), ciRegex(RegexRequestMatcher.class), mvc(
4143
MvcRequestMatcher.class);
4244

43-
private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
45+
private static final String HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector";
46+
47+
private static final boolean mvcPresent;
4448

4549
private static final String ATT_MATCHER_TYPE = "request-matcher";
4650

4751
final Class<? extends RequestMatcher> type;
4852

53+
static {
54+
mvcPresent = ClassUtils.isPresent(HANDLER_MAPPING_INTROSPECTOR, MatcherType.class.getClassLoader());
55+
}
56+
4957
MatcherType(Class<? extends RequestMatcher> type) {
5058
this.type = type;
5159
}
@@ -64,7 +72,7 @@ public BeanDefinition createMatcher(ParserContext pc, String path, String method
6472
}
6573
matcherBldr.addConstructorArgValue(path);
6674
if (this == mvc) {
67-
matcherBldr.addPropertyValue("method", method);
75+
matcherBldr.addPropertyValue("method", (StringUtils.hasText(method) ? HttpMethod.valueOf(method) : null));
6876
matcherBldr.addPropertyValue("servletPath", servletPath);
6977
}
7078
else {
@@ -84,4 +92,12 @@ static MatcherType fromElement(Element elt) {
8492
return ant;
8593
}
8694

95+
static MatcherType fromElementOrMvc(Element elt) {
96+
String matcherTypeName = elt.getAttribute(ATT_MATCHER_TYPE);
97+
if (!StringUtils.hasText(matcherTypeName) && mvcPresent) {
98+
return MatcherType.mvc;
99+
}
100+
return MatcherType.fromElement(elt);
101+
}
102+
87103
}

config/src/test/java/org/springframework/security/config/FilterChainProxyConfigTests.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -36,8 +36,9 @@
3636
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
3737
import org.springframework.security.web.firewall.DefaultHttpFirewall;
3838
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
39-
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
4039
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
40+
import org.springframework.security.web.util.matcher.RequestMatcher;
41+
import org.springframework.test.util.ReflectionTestUtils;
4142

4243
import static org.assertj.core.api.Assertions.assertThat;
4344
import static org.mockito.ArgumentMatchers.any;
@@ -113,12 +114,13 @@ public void mixingPatternsAndPlaceholdersDoesntCauseOrderingIssues() {
113114
List<SecurityFilterChain> chains = fcp.getFilterChains();
114115
assertThat(getPattern(chains.get(0))).isEqualTo("/login*");
115116
assertThat(getPattern(chains.get(1))).isEqualTo("/logout");
116-
assertThat(((DefaultSecurityFilterChain) chains.get(2)).getRequestMatcher() instanceof AnyRequestMatcher)
117-
.isTrue();
117+
assertThat(((DefaultSecurityFilterChain) chains.get(2)).getRequestMatcher())
118+
.isInstanceOf(AnyRequestMatcher.class);
118119
}
119120

120121
private String getPattern(SecurityFilterChain chain) {
121-
return ((AntPathRequestMatcher) ((DefaultSecurityFilterChain) chain).getRequestMatcher()).getPattern();
122+
RequestMatcher requestMatcher = ((DefaultSecurityFilterChain) chain).getRequestMatcher();
123+
return (String) ReflectionTestUtils.getField(requestMatcher, "pattern");
122124
}
123125

124126
private void checkPathAndFilterOrder(FilterChainProxy filterChainProxy) {

config/src/test/java/org/springframework/security/config/http/FilterSecurityMetadataSourceBeanDefinitionParserTests.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -92,10 +92,12 @@ public void expressionsAreSupported() {
9292
public void interceptUrlsSupportPropertyPlaceholders() {
9393
System.setProperty("secure.url", "/secure");
9494
System.setProperty("secure.role", "ROLE_A");
95-
setContext("<b:bean class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer'/>"
96-
+ "<filter-security-metadata-source id='fids' use-expressions='false'>"
97-
+ " <intercept-url pattern='${secure.url}' access='${secure.role}'/>"
98-
+ "</filter-security-metadata-source>");
95+
setContext(
96+
"<b:bean class=\"org.springframework.web.servlet.handler.HandlerMappingIntrospector\" name=\"mvcHandlerMappingIntrospector\"/>"
97+
+ "<b:bean class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer'/>"
98+
+ "<filter-security-metadata-source id='fids' use-expressions='false'>"
99+
+ " <intercept-url pattern='${secure.url}' access='${secure.role}'/>"
100+
+ "</filter-security-metadata-source>");
99101
DefaultFilterInvocationSecurityMetadataSource fids = (DefaultFilterInvocationSecurityMetadataSource) this.appContext
100102
.getBean("fids");
101103
Collection<ConfigAttribute> cad = fids.getAttributes(createFilterInvocation("/secure", "GET"));
@@ -105,7 +107,8 @@ public void interceptUrlsSupportPropertyPlaceholders() {
105107
@Test
106108
public void parsingWithinFilterSecurityInterceptorIsSuccessful() {
107109
// @formatter:off
108-
setContext("<http auto-config='true' use-expressions='false'/>"
110+
setContext("<b:bean class=\"org.springframework.web.servlet.handler.HandlerMappingIntrospector\" name=\"mvcHandlerMappingIntrospector\"/>" +
111+
"<http auto-config='true' use-expressions='false'/>"
109112
+ "<b:bean id='fsi' class='org.springframework.security.web.access.intercept.FilterSecurityInterceptor' autowire='byType'>"
110113
+ " <b:property name='securityMetadataSource'>"
111114
+ " <filter-security-metadata-source use-expressions='false'>"
@@ -130,9 +133,8 @@ public void parsingInterceptUrlServletPathFails() {
130133

131134
private FilterInvocation createFilterInvocation(String path, String method) {
132135
MockHttpServletRequest request = new MockHttpServletRequest("GET", "");
133-
request.setRequestURI(null);
136+
request.setRequestURI(path);
134137
request.setMethod(method);
135-
request.setServletPath(path);
136138
return new FilterInvocation(request, new MockHttpServletResponse(), new MockFilterChain());
137139
}
138140

config/src/test/java/org/springframework/security/config/http/InterceptUrlConfigTests.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
2424
import org.junit.jupiter.api.extension.ExtendWith;
2525
import org.mockito.stubbing.Answer;
2626

27+
import org.springframework.beans.factory.BeanCreationException;
2728
import org.springframework.beans.factory.annotation.Autowired;
2829
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
2930
import org.springframework.mock.web.MockServletContext;
@@ -39,6 +40,7 @@
3940

4041
import static org.assertj.core.api.Assertions.assertThat;
4142
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
43+
import static org.assertj.core.api.Assertions.assertThatNoException;
4244
import static org.mockito.BDDMockito.given;
4345
import static org.mockito.Mockito.mock;
4446
import static org.mockito.Mockito.spy;
@@ -361,14 +363,20 @@ public void configureWhenUsingCiRegexMatcherAndServletPathAndAuthorizationManage
361363
}
362364

363365
@Test
364-
public void configureWhenUsingDefaultMatcherAndServletPathThenThrowsException() {
365-
assertThatExceptionOfType(BeanDefinitionParsingException.class)
366+
public void configureWhenUsingDefaultMatcherAndServletPathThenNoException() {
367+
assertThatNoException()
366368
.isThrownBy(() -> this.spring.configLocations(this.xml("DefaultMatcherServletPath")).autowire());
367369
}
368370

369371
@Test
370-
public void configureWhenUsingDefaultMatcherAndServletPathAndAuthorizationManagerThenThrowsException() {
371-
assertThatExceptionOfType(BeanDefinitionParsingException.class).isThrownBy(() -> this.spring
372+
public void configureWhenUsingDefaultMatcherAndNoIntrospectorBeanThenException() {
373+
assertThatExceptionOfType(BeanCreationException.class)
374+
.isThrownBy(() -> this.spring.configLocations(this.xml("DefaultMatcherNoIntrospectorBean")).autowire());
375+
}
376+
377+
@Test
378+
public void configureWhenUsingDefaultMatcherAndServletPathAndAuthorizationManagerThenNoException() {
379+
assertThatNoException().isThrownBy(() -> this.spring
372380
.configLocations(this.xml("DefaultMatcherServletPathAuthorizationManager")).autowire());
373381
}
374382

0 commit comments

Comments
 (0)