Skip to content

Commit 7813a9b

Browse files
Use default PathPatternParser instance
1 parent c5da886 commit 7813a9b

File tree

5 files changed

+132
-9
lines changed

5 files changed

+132
-9
lines changed

config/src/main/java/org/springframework/security/config/web/server/AbstractServerWebExchangeMatcherRegistry.java

+23-3
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-2023 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.
@@ -16,13 +16,16 @@
1616

1717
package org.springframework.security.config.web.server;
1818

19+
import java.util.ArrayList;
1920
import java.util.List;
2021

2122
import org.springframework.http.HttpMethod;
2223
import org.springframework.security.web.server.util.matcher.OrServerWebExchangeMatcher;
2324
import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
2425
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
2526
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
27+
import org.springframework.web.util.pattern.PathPattern;
28+
import org.springframework.web.util.pattern.PathPatternParser;
2629

2730
/**
2831
* @author Rob Winch
@@ -62,7 +65,8 @@ public T pathMatchers(HttpMethod method) {
6265
* {@link ServerWebExchangeMatcher}
6366
*/
6467
public T pathMatchers(HttpMethod method, String... antPatterns) {
65-
return matcher(ServerWebExchangeMatchers.pathMatchers(method, antPatterns));
68+
List<PathPattern> pathPatterns = parsePatterns(antPatterns);
69+
return matcher(ServerWebExchangeMatchers.pathMatchers(method, pathPatterns.toArray(new PathPattern[0])));
6670
}
6771

6872
/**
@@ -74,7 +78,19 @@ public T pathMatchers(HttpMethod method, String... antPatterns) {
7478
* {@link ServerWebExchangeMatcher}
7579
*/
7680
public T pathMatchers(String... antPatterns) {
77-
return matcher(ServerWebExchangeMatchers.pathMatchers(antPatterns));
81+
List<PathPattern> pathPatterns = parsePatterns(antPatterns);
82+
return matcher(ServerWebExchangeMatchers.pathMatchers(pathPatterns.toArray(new PathPattern[0])));
83+
}
84+
85+
private List<PathPattern> parsePatterns(String[] antPatterns) {
86+
PathPatternParser parser = getPathPatternParser();
87+
List<PathPattern> pathPatterns = new ArrayList<>(antPatterns.length);
88+
for (String pattern : antPatterns) {
89+
pattern = parser.initFullPathPattern(pattern);
90+
PathPattern pathPattern = parser.parse(pattern);
91+
pathPatterns.add(pathPattern);
92+
}
93+
return pathPatterns;
7894
}
7995

8096
/**
@@ -96,6 +112,10 @@ public T matchers(ServerWebExchangeMatcher... matchers) {
96112
*/
97113
protected abstract T registerMatcher(ServerWebExchangeMatcher matcher);
98114

115+
protected PathPatternParser getPathPatternParser() {
116+
return PathPatternParser.defaultInstance;
117+
}
118+
99119
/**
100120
* Associates a {@link ServerWebExchangeMatcher} instances
101121
* @param matcher the {@link ServerWebExchangeMatcher} instance

config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java

+35-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2023 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.
@@ -178,9 +178,11 @@
178178
import org.springframework.web.cors.reactive.CorsProcessor;
179179
import org.springframework.web.cors.reactive.CorsWebFilter;
180180
import org.springframework.web.cors.reactive.DefaultCorsProcessor;
181+
import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping;
181182
import org.springframework.web.server.ServerWebExchange;
182183
import org.springframework.web.server.WebFilter;
183184
import org.springframework.web.server.WebFilterChain;
185+
import org.springframework.web.util.pattern.PathPatternParser;
184186

185187
/**
186188
* A {@link ServerHttpSecurity} is similar to Spring Security's {@code HttpSecurity} but
@@ -1550,6 +1552,18 @@ private <T> T getBeanOrNull(ResolvableType type) {
15501552
return null;
15511553
}
15521554

1555+
private <T> T getBeanOrNull(String beanName, Class<T> requiredClass) {
1556+
if (this.context == null) {
1557+
return null;
1558+
}
1559+
try {
1560+
return this.context.getBean(beanName, requiredClass);
1561+
}
1562+
catch (Exception ex) {
1563+
return null;
1564+
}
1565+
}
1566+
15531567
private <T> String[] getBeanNamesForTypeOrEmpty(Class<T> beanClass) {
15541568
if (this.context == null) {
15551569
return new String[0];
@@ -1570,13 +1584,17 @@ protected void setApplicationContext(ApplicationContext applicationContext) thro
15701584
*/
15711585
public class AuthorizeExchangeSpec extends AbstractServerWebExchangeMatcherRegistry<AuthorizeExchangeSpec.Access> {
15721586

1587+
private static final String REQUEST_MAPPING_HANDLER_MAPPING_BEAN_NAME = "requestMappingHandlerMapping";
1588+
15731589
private DelegatingReactiveAuthorizationManager.Builder managerBldr = DelegatingReactiveAuthorizationManager
15741590
.builder();
15751591

15761592
private ServerWebExchangeMatcher matcher;
15771593

15781594
private boolean anyExchangeRegistered;
15791595

1596+
private PathPatternParser pathPatternParser;
1597+
15801598
/**
15811599
* Allows method chaining to continue configuring the {@link ServerHttpSecurity}
15821600
* @return the {@link ServerHttpSecurity} to continue configuring
@@ -1596,6 +1614,22 @@ public Access anyExchange() {
15961614
return result;
15971615
}
15981616

1617+
@Override
1618+
protected PathPatternParser getPathPatternParser() {
1619+
if (this.pathPatternParser != null) {
1620+
return this.pathPatternParser;
1621+
}
1622+
RequestMappingHandlerMapping requestMappingHandlerMapping = getBeanOrNull(
1623+
REQUEST_MAPPING_HANDLER_MAPPING_BEAN_NAME, RequestMappingHandlerMapping.class);
1624+
if (requestMappingHandlerMapping != null) {
1625+
this.pathPatternParser = requestMappingHandlerMapping.getPathPatternParser();
1626+
}
1627+
if (this.pathPatternParser == null) {
1628+
this.pathPatternParser = PathPatternParser.defaultInstance;
1629+
}
1630+
return this.pathPatternParser;
1631+
}
1632+
15991633
@Override
16001634
protected Access registerMatcher(ServerWebExchangeMatcher matcher) {
16011635
Assert.state(!this.anyExchangeRegistered, () -> "Cannot register " + matcher

web/src/main/java/org/springframework/security/web/server/util/matcher/PathPatternParserServerWebExchangeMatcher.java

+8-4
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-2023 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.
@@ -42,8 +42,6 @@ public final class PathPatternParserServerWebExchangeMatcher implements ServerWe
4242

4343
private static final Log logger = LogFactory.getLog(PathPatternParserServerWebExchangeMatcher.class);
4444

45-
private static final PathPatternParser DEFAULT_PATTERN_PARSER = new PathPatternParser();
46-
4745
private final PathPattern pattern;
4846

4947
private final HttpMethod method;
@@ -60,14 +58,20 @@ public PathPatternParserServerWebExchangeMatcher(PathPattern pattern, HttpMethod
6058

6159
public PathPatternParserServerWebExchangeMatcher(String pattern, HttpMethod method) {
6260
Assert.notNull(pattern, "pattern cannot be null");
63-
this.pattern = DEFAULT_PATTERN_PARSER.parse(pattern);
61+
this.pattern = parse(pattern);
6462
this.method = method;
6563
}
6664

6765
public PathPatternParserServerWebExchangeMatcher(String pattern) {
6866
this(pattern, null);
6967
}
7068

69+
private PathPattern parse(String pattern) {
70+
PathPatternParser parser = PathPatternParser.defaultInstance;
71+
pattern = parser.initFullPathPattern(pattern);
72+
return parser.parse(pattern);
73+
}
74+
7175
@Override
7276
public Mono<MatchResult> matches(ServerWebExchange exchange) {
7377
ServerHttpRequest request = exchange.getRequest();

web/src/main/java/org/springframework/security/web/server/util/matcher/ServerWebExchangeMatchers.java

+26-1
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-2023 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.
@@ -23,6 +23,7 @@
2323

2424
import org.springframework.http.HttpMethod;
2525
import org.springframework.web.server.ServerWebExchange;
26+
import org.springframework.web.util.pattern.PathPattern;
2627

2728
/**
2829
* Provides factory methods for creating common {@link ServerWebExchangeMatcher}
@@ -59,6 +60,30 @@ public static ServerWebExchangeMatcher pathMatchers(String... patterns) {
5960
return pathMatchers(null, patterns);
6061
}
6162

63+
/**
64+
* Creates a matcher that matches on any of the provided {@link PathPattern}s.
65+
* @param pathPatterns the {@link PathPattern}s to match on
66+
* @return the matcher to use
67+
*/
68+
public static ServerWebExchangeMatcher pathMatchers(PathPattern... pathPatterns) {
69+
return pathMatchers(null, pathPatterns);
70+
}
71+
72+
/**
73+
* Creates a matcher that matches on the specific method and any of the provided
74+
* {@link PathPattern}s.
75+
* @param method the method to match on. If null, any method will be matched.
76+
* @param pathPatterns the {@link PathPattern}s to match on
77+
* @return the matcher to use
78+
*/
79+
public static ServerWebExchangeMatcher pathMatchers(HttpMethod method, PathPattern... pathPatterns) {
80+
List<ServerWebExchangeMatcher> matchers = new ArrayList<>(pathPatterns.length);
81+
for (PathPattern pathPattern : pathPatterns) {
82+
matchers.add(new PathPatternParserServerWebExchangeMatcher(pathPattern, method));
83+
}
84+
return new OrServerWebExchangeMatcher(matchers);
85+
}
86+
6287
/**
6388
* Creates a matcher that will match on any of the provided matchers
6489
* @param matchers the matchers to match on
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2002-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.web.server.util.matcher;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
22+
import org.springframework.mock.web.server.MockServerWebExchange;
23+
24+
import static org.assertj.core.api.Assertions.assertThat;
25+
26+
/**
27+
* Tests for {@link PathPatternParserServerWebExchangeMatcher}
28+
*
29+
* @author Marcus da Coregio
30+
*/
31+
class PathPatternParserServerWebExchangeMatcherTests {
32+
33+
@Test
34+
void matchesWhenConfiguredWithNoTrailingSlashAndPathContainsSlashThenMatches() {
35+
PathPatternParserServerWebExchangeMatcher matcher = new PathPatternParserServerWebExchangeMatcher("user/**");
36+
MockServerHttpRequest request = MockServerHttpRequest.get("/user/test").build();
37+
assertThat(matcher.matches(MockServerWebExchange.from(request)).block().isMatch()).isTrue();
38+
}
39+
40+
}

0 commit comments

Comments
 (0)