diff --git a/config/src/main/java/org/springframework/security/config/web/server/AbstractServerWebExchangeMatcherRegistry.java b/config/src/main/java/org/springframework/security/config/web/server/AbstractServerWebExchangeMatcherRegistry.java index 08ace62247..1b9d6faf87 100644 --- a/config/src/main/java/org/springframework/security/config/web/server/AbstractServerWebExchangeMatcherRegistry.java +++ b/config/src/main/java/org/springframework/security/config/web/server/AbstractServerWebExchangeMatcherRegistry.java @@ -17,6 +17,7 @@ package org.springframework.security.config.web.server; import org.springframework.http.HttpMethod; import org.springframework.security.web.server.util.matcher.OrServerWebExchangeMatcher; +import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher; import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers; @@ -40,7 +41,7 @@ abstract class AbstractServerWebExchangeMatcherRegistry { /** * Maps a {@link List} of - * {@link org.springframework.security.web.server.util.matcher.PathMatcherServerWebExchangeMatcher} + * {@link PathPatternParserServerWebExchangeMatcher} * instances. * * @param method the {@link HttpMethod} to use for any @@ -54,13 +55,13 @@ abstract class AbstractServerWebExchangeMatcherRegistry { /** * Maps a {@link List} of - * {@link org.springframework.security.web.server.util.matcher.PathMatcherServerWebExchangeMatcher} + * {@link PathPatternParserServerWebExchangeMatcher} * instances. * * @param method the {@link HttpMethod} to use or {@code null} for any * {@link HttpMethod}. * @param antPatterns the ant patterns to create. If {@code null} or empty, then matches on nothing. - * {@link org.springframework.security.web.server.util.matcher.PathMatcherServerWebExchangeMatcher} from + * {@link PathPatternParserServerWebExchangeMatcher} from * * @return the object that is chained after creating the {@link ServerWebExchangeMatcher} */ @@ -70,11 +71,11 @@ abstract class AbstractServerWebExchangeMatcherRegistry { /** * Maps a {@link List} of - * {@link org.springframework.security.web.server.util.matcher.PathMatcherServerWebExchangeMatcher} + * {@link PathPatternParserServerWebExchangeMatcher} * instances that do not care which {@link HttpMethod} is used. * * @param antPatterns the ant patterns to create - * {@link org.springframework.security.web.server.util.matcher.PathMatcherServerWebExchangeMatcher} from + * {@link PathPatternParserServerWebExchangeMatcher} from * * @return the object that is chained after creating the {@link ServerWebExchangeMatcher} */ diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/reactive/EnableWebFluxSecurityTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/reactive/EnableWebFluxSecurityTests.java index 440f6044a5..74a05df865 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/reactive/EnableWebFluxSecurityTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/reactive/EnableWebFluxSecurityTests.java @@ -32,8 +32,7 @@ import org.springframework.security.core.userdetails.UserDetailsRepository; import org.springframework.security.test.web.reactive.server.WebTestClientBuilder; import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.WebFilterChainFilter; -import org.springframework.security.web.server.util.matcher.PathMatcherServerWebExchangeMatcher; -import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; +import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; @@ -101,7 +100,7 @@ public class EnableWebFluxSecurityTests { @Bean public SecurityWebFilterChain apiHttpSecurity(HttpSecurity http) { http - .securityMatcher(new PathMatcherServerWebExchangeMatcher("/api/**")) + .securityMatcher(new PathPatternParserServerWebExchangeMatcher("/api/**")) .authorizeExchange() .anyExchange().denyAll(); return http.build(); diff --git a/webflux/src/main/java/org/springframework/security/web/server/util/matcher/PathMatcherServerWebExchangeMatcher.java b/webflux/src/main/java/org/springframework/security/web/server/util/matcher/PathPatternParserServerWebExchangeMatcher.java similarity index 62% rename from webflux/src/main/java/org/springframework/security/web/server/util/matcher/PathMatcherServerWebExchangeMatcher.java rename to webflux/src/main/java/org/springframework/security/web/server/util/matcher/PathPatternParserServerWebExchangeMatcher.java index 33016cba2f..d3b80b623d 100644 --- a/webflux/src/main/java/org/springframework/security/web/server/util/matcher/PathMatcherServerWebExchangeMatcher.java +++ b/webflux/src/main/java/org/springframework/security/web/server/util/matcher/PathPatternParserServerWebExchangeMatcher.java @@ -17,60 +17,64 @@ */ package org.springframework.security.web.server.util.matcher; -import java.util.HashMap; -import java.util.Map; - import org.springframework.http.HttpMethod; +import org.springframework.http.server.PathContainer; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.util.Assert; -import org.springframework.util.PathMatcher; import org.springframework.web.server.ServerWebExchange; -import org.springframework.web.util.pattern.ParsingPathMatcher; +import org.springframework.web.util.pattern.PathPattern; +import org.springframework.web.util.pattern.PathPatternParser; import reactor.core.publisher.Mono; +import java.util.HashMap; +import java.util.Map; + /** * @author Rob Winch * @since 5.0 */ -public final class PathMatcherServerWebExchangeMatcher implements ServerWebExchangeMatcher { - private static final PathMatcher DEFAULT_PATH_MATCHER = new ParsingPathMatcher(); +public final class PathPatternParserServerWebExchangeMatcher implements ServerWebExchangeMatcher { + private static final PathPatternParser DEFAULT_PATTERN_PARSER = new PathPatternParser(); - private PathMatcher pathMatcher = DEFAULT_PATH_MATCHER; - - private final String pattern; + private final PathPattern pattern; private final HttpMethod method; - public PathMatcherServerWebExchangeMatcher(String pattern) { + public PathPatternParserServerWebExchangeMatcher(PathPattern pattern) { this(pattern, null); } - public PathMatcherServerWebExchangeMatcher(String pattern, HttpMethod method) { + public PathPatternParserServerWebExchangeMatcher(PathPattern pattern, HttpMethod method) { Assert.notNull(pattern, "pattern cannot be null"); this.pattern = pattern; this.method = method; } + public PathPatternParserServerWebExchangeMatcher(String pattern, HttpMethod method) { + Assert.notNull(pattern, "pattern cannot be null"); + this.pattern = DEFAULT_PATTERN_PARSER.parse(pattern); + this.method = method; + } + + public PathPatternParserServerWebExchangeMatcher(String pattern) { + this(pattern, null); + } + @Override public Mono matches(ServerWebExchange exchange) { ServerHttpRequest request = exchange.getRequest(); if(this.method != null && !this.method.equals(request.getMethod())) { return MatchResult.notMatch(); } - String path = request.getPath().pathWithinApplication().value(); - boolean match = pathMatcher.match(pattern, path); + PathContainer path = request.getPath().pathWithinApplication(); + boolean match = this.pattern.matches(path); if(!match) { return MatchResult.notMatch(); } - Map pathVariables = pathMatcher.extractUriTemplateVariables(pattern, path); + Map pathVariables = this.pattern.matchAndExtract(path).getUriVariables(); Map variables = new HashMap<>(pathVariables); return MatchResult.match(variables); } - public void setPathMatcher(PathMatcher pathMatcher) { - Assert.notNull(pathMatcher, "pathMatcher cannot be null"); - this.pathMatcher = pathMatcher; - } - @Override public String toString() { return "PathMatcherServerWebExchangeMatcher{" + diff --git a/webflux/src/main/java/org/springframework/security/web/server/util/matcher/ServerWebExchangeMatchers.java b/webflux/src/main/java/org/springframework/security/web/server/util/matcher/ServerWebExchangeMatchers.java index 63665f557a..cdabd9537c 100644 --- a/webflux/src/main/java/org/springframework/security/web/server/util/matcher/ServerWebExchangeMatchers.java +++ b/webflux/src/main/java/org/springframework/security/web/server/util/matcher/ServerWebExchangeMatchers.java @@ -33,7 +33,7 @@ public abstract class ServerWebExchangeMatchers { public static ServerWebExchangeMatcher pathMatchers(HttpMethod method, String... patterns) { List matchers = new ArrayList<>(patterns.length); for (String pattern : patterns) { - matchers.add(new PathMatcherServerWebExchangeMatcher(pattern, method)); + matchers.add(new PathPatternParserServerWebExchangeMatcher(pattern, method)); } return new OrServerWebExchangeMatcher(matchers); } diff --git a/webflux/src/test/java/org/springframework/security/web/server/util/matcher/PathMatcherServerWebExchangeMatcherTests.java b/webflux/src/test/java/org/springframework/security/web/server/util/matcher/PathMatcherServerWebExchangeMatcherTests.java index 36f0bdcbfb..f80582869f 100644 --- a/webflux/src/test/java/org/springframework/security/web/server/util/matcher/PathMatcherServerWebExchangeMatcherTests.java +++ b/webflux/src/test/java/org/springframework/security/web/server/util/matcher/PathMatcherServerWebExchangeMatcherTests.java @@ -17,11 +17,6 @@ */ package org.springframework.security.web.server.util.matcher; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.when; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -31,9 +26,15 @@ import org.springframework.http.HttpMethod; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.http.server.reactive.MockServerHttpResponse; import org.springframework.mock.http.server.reactive.MockServerWebExchange; -import org.springframework.util.PathMatcher; -import org.springframework.web.server.adapter.DefaultServerWebExchange; import org.springframework.web.server.session.DefaultWebSessionManager; +import org.springframework.web.util.pattern.PathPattern; + +import java.util.HashMap; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; /** * @author Rob Winch @@ -42,10 +43,11 @@ import org.springframework.web.server.session.DefaultWebSessionManager; @RunWith(MockitoJUnitRunner.class) public class PathMatcherServerWebExchangeMatcherTests { @Mock - PathMatcher pathMatcher; + PathPattern pattern; + @Mock + PathPattern.PathMatchResult pathMatchResult; MockServerWebExchange exchange; - PathMatcherServerWebExchangeMatcher matcher; - String pattern; + PathPatternParserServerWebExchangeMatcher matcher; String path; @Before @@ -54,44 +56,43 @@ public class PathMatcherServerWebExchangeMatcherTests { MockServerHttpResponse response = new MockServerHttpResponse(); DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); exchange = request.toExchange(); - pattern = "/pattern"; path = "/path"; - matcher = new PathMatcherServerWebExchangeMatcher(pattern); - matcher.setPathMatcher(pathMatcher); + matcher = new PathPatternParserServerWebExchangeMatcher(pattern); } @Test(expected = IllegalArgumentException.class) public void constructorPatternWhenPatternNullThenThrowsException() { - new PathMatcherServerWebExchangeMatcher(null); + new PathPatternParserServerWebExchangeMatcher((PathPattern) null); } @Test(expected = IllegalArgumentException.class) public void constructorPatternAndMethodWhenPatternNullThenThrowsException() { - new PathMatcherServerWebExchangeMatcher(null, HttpMethod.GET); + new PathPatternParserServerWebExchangeMatcher((PathPattern) null, HttpMethod.GET); } @Test public void matchesWhenPathMatcherTrueThenReturnTrue() { - when(pathMatcher.match(pattern, path)).thenReturn(true); + when(pattern.matches(any())).thenReturn(true); + when(pattern.matchAndExtract(any())).thenReturn(pathMatchResult); + when(pathMatchResult.getUriVariables()).thenReturn(new HashMap<>()); assertThat(matcher.matches(exchange).block().isMatch()).isTrue(); } @Test public void matchesWhenPathMatcherFalseThenReturnFalse() { - when(pathMatcher.match(pattern, path)).thenReturn(false); + when(pattern.matches(any())).thenReturn(false); assertThat(matcher.matches(exchange).block().isMatch()).isFalse(); - - verify(pathMatcher).match(pattern, path); } @Test public void matchesWhenPathMatcherTrueAndMethodTrueThenReturnTrue() { - matcher = new PathMatcherServerWebExchangeMatcher(pattern, exchange.getRequest().getMethod()); - matcher.setPathMatcher(pathMatcher); - when(pathMatcher.match(pattern, path)).thenReturn(true); + matcher = new PathPatternParserServerWebExchangeMatcher(pattern, exchange.getRequest().getMethod()); + when(pattern.matches(any())).thenReturn(true); + when(pattern.matchAndExtract(any())).thenReturn(pathMatchResult); + when(pathMatchResult.getUriVariables()).thenReturn(new HashMap<>()); assertThat(matcher.matches(exchange).block().isMatch()).isTrue(); } @@ -100,16 +101,10 @@ public class PathMatcherServerWebExchangeMatcherTests { public void matchesWhenPathMatcherTrueAndMethodFalseThenReturnFalse() { HttpMethod method = HttpMethod.OPTIONS; assertThat(exchange.getRequest().getMethod()).isNotEqualTo(method); - matcher = new PathMatcherServerWebExchangeMatcher(pattern, method); - matcher.setPathMatcher(pathMatcher); + matcher = new PathPatternParserServerWebExchangeMatcher(pattern, method); assertThat(matcher.matches(exchange).block().isMatch()).isFalse(); - verifyZeroInteractions(pathMatcher); - } - - @Test(expected = IllegalArgumentException.class) - public void setPathMatcherWhenNullThenThrowException() { - matcher.setPathMatcher(null); + verifyZeroInteractions(pattern); } }