Use PathPatternRequestMatcher in web

Issue gh-16887
This commit is contained in:
Josh Cummings 2025-07-02 16:41:36 -06:00
parent 3e53cc2c4a
commit e8ed0f1b03
No known key found for this signature in database
GPG Key ID: 869B37A20E876129
21 changed files with 79 additions and 387 deletions

View File

@ -1,98 +0,0 @@
/*
* Copyright 2002-2023 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
*
* https://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.web.access;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import org.springframework.util.Assert;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
/**
* Transforms by passing it into
* {@link HandlerMappingIntrospector#setCache(HttpServletRequest)}. Before, it wraps the
* {@link HttpServletRequest} to ensure that the methods needed work since some methods by
* default throw {@link UnsupportedOperationException}.
*
* @author Rob Winch
* @deprecated please use {@link PathPatternRequestTransformer} instead
*/
@Deprecated(forRemoval = true)
public class HandlerMappingIntrospectorRequestTransformer
implements AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer {
private final HandlerMappingIntrospector introspector;
public HandlerMappingIntrospectorRequestTransformer(HandlerMappingIntrospector introspector) {
Assert.notNull(introspector, "introspector canot be null");
this.introspector = introspector;
}
@Override
public HttpServletRequest transform(HttpServletRequest request) {
CacheableRequestWrapper cacheableRequest = new CacheableRequestWrapper(request);
this.introspector.setCache(cacheableRequest);
return cacheableRequest;
}
static final class CacheableRequestWrapper extends HttpServletRequestWrapper {
private final Map<String, Object> attributes = new HashMap<>();
/**
* Constructs a request object wrapping the given request.
* @param request the {@link HttpServletRequest} to be wrapped.
* @throws IllegalArgumentException if the request is null
*/
CacheableRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public DispatcherType getDispatcherType() {
return DispatcherType.REQUEST;
}
@Override
public Enumeration<String> getAttributeNames() {
return Collections.enumeration(this.attributes.keySet());
}
@Override
public Object getAttribute(String name) {
return this.attributes.get(name);
}
@Override
public void setAttribute(String name, Object o) {
this.attributes.put(name, o);
}
@Override
public void removeAttribute(String name) {
this.attributes.remove(name);
}
}
}

View File

@ -46,11 +46,12 @@ import org.springframework.security.web.authentication.session.NullAuthenticated
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.filter.GenericFilterBean;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
/**
* Abstract processor of browser-based HTTP-based authentication requests.
*
@ -395,7 +396,7 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
* @param filterProcessesUrl
*/
public void setFilterProcessesUrl(String filterProcessesUrl) {
setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(filterProcessesUrl));
setRequiresAuthenticationRequestMatcher(pathPattern(filterProcessesUrl));
}
public final void setRequiresAuthenticationRequestMatcher(RequestMatcher requestMatcher) {

View File

@ -29,13 +29,14 @@ import org.springframework.core.log.LogMessage;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.GenericFilterBean;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
/**
* Logs a principal out.
* <p>
@ -140,7 +141,7 @@ public class LogoutFilter extends GenericFilterBean {
}
public void setFilterProcessesUrl(String filterProcessesUrl) {
this.logoutRequestMatcher = PathPatternRequestMatcher.withDefaults().matcher(filterProcessesUrl);
this.logoutRequestMatcher = pathPattern(filterProcessesUrl);
}
}

View File

@ -18,7 +18,8 @@ package org.springframework.security.web.authentication.ott;
import org.springframework.http.HttpMethod;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
/**
* Filter that processes a one-time token for log in.
@ -34,7 +35,7 @@ public final class OneTimeTokenAuthenticationFilter extends AbstractAuthenticati
public static final String DEFAULT_LOGIN_PROCESSING_URL = "/login/ott";
public OneTimeTokenAuthenticationFilter() {
super(PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, DEFAULT_LOGIN_PROCESSING_URL));
super(pathPattern(HttpMethod.POST, DEFAULT_LOGIN_PROCESSING_URL));
setAuthenticationConverter(new OneTimeTokenAuthenticationConverter());
}

View File

@ -63,13 +63,12 @@ import org.springframework.security.web.authentication.WebAuthenticationDetailsS
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.filter.GenericFilterBean;
import org.springframework.web.util.UrlPathHelper;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
/**
* Switch User processing filter responsible for user context switching.
@ -129,9 +128,9 @@ public class SwitchUserFilter extends GenericFilterBean implements ApplicationEv
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private RequestMatcher exitUserMatcher = createMatcher("/logout/impersonate", true);
private RequestMatcher exitUserMatcher = createMatcher("/logout/impersonate");
private RequestMatcher switchUserMatcher = createMatcher("/login/impersonate", true);
private RequestMatcher switchUserMatcher = createMatcher("/login/impersonate");
private String targetUrl;
@ -408,7 +407,7 @@ public class SwitchUserFilter extends GenericFilterBean implements ApplicationEv
public void setExitUserUrl(String exitUserUrl) {
Assert.isTrue(UrlUtils.isValidRedirectUrl(exitUserUrl),
"exitUserUrl cannot be empty and must be a valid redirect URL");
this.exitUserMatcher = createMatcher(exitUserUrl, false);
this.exitUserMatcher = createMatcher(exitUserUrl);
}
/**
@ -428,7 +427,7 @@ public class SwitchUserFilter extends GenericFilterBean implements ApplicationEv
public void setSwitchUserUrl(String switchUserUrl) {
Assert.isTrue(UrlUtils.isValidRedirectUrl(switchUserUrl),
"switchUserUrl cannot be empty and must be a valid redirect URL");
this.switchUserMatcher = createMatcher(switchUserUrl, false);
this.switchUserMatcher = createMatcher(switchUserUrl);
}
/**
@ -547,11 +546,8 @@ public class SwitchUserFilter extends GenericFilterBean implements ApplicationEv
this.securityContextRepository = securityContextRepository;
}
private static RequestMatcher createMatcher(String pattern, boolean usePathPatterns) {
if (usePathPatterns) {
return PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, pattern);
}
return new AntPathRequestMatcher(pattern, "POST", true, new UrlPathHelper());
private static RequestMatcher createMatcher(String pattern) {
return pathPattern(HttpMethod.POST, pattern);
}
}

View File

@ -28,11 +28,12 @@ import jakarta.servlet.http.HttpServletResponse;
import org.springframework.core.log.LogMessage;
import org.springframework.http.HttpMethod;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.filter.OncePerRequestFilter;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
/**
* Generates a default log out page.
*
@ -41,7 +42,7 @@ import org.springframework.web.filter.OncePerRequestFilter;
*/
public class DefaultLogoutPageGeneratingFilter extends OncePerRequestFilter {
private RequestMatcher matcher = PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.GET, "/logout");
private RequestMatcher matcher = pathPattern(HttpMethod.GET, "/logout");
private Function<HttpServletRequest, Map<String, String>> resolveHiddenInputs = (request) -> Collections.emptyMap();

View File

@ -28,11 +28,12 @@ import jakarta.servlet.http.HttpServletRequest;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.filter.GenericFilterBean;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
/**
* Serve common static assets used in default UIs, such as CSS or Javascript files. For
* internal use only.
@ -89,8 +90,7 @@ public final class DefaultResourcesFilter extends GenericFilterBean {
* @return -
*/
public static DefaultResourcesFilter css() {
return new DefaultResourcesFilter(
PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.GET, "/default-ui.css"),
return new DefaultResourcesFilter(pathPattern(HttpMethod.GET, "/default-ui.css"),
new ClassPathResource("org/springframework/security/default-ui.css"),
new MediaType("text", "css", StandardCharsets.UTF_8));
}
@ -107,8 +107,7 @@ public final class DefaultResourcesFilter extends GenericFilterBean {
* @return -
*/
public static DefaultResourcesFilter webauthn() {
return new DefaultResourcesFilter(
PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.GET, "/login/webauthn.js"),
return new DefaultResourcesFilter(pathPattern(HttpMethod.GET, "/login/webauthn.js"),
new ClassPathResource("org/springframework/security/spring-security-webauthn.js"),
new MediaType("text", "javascript", StandardCharsets.UTF_8));
}

View File

@ -202,7 +202,7 @@ public final class PathPatternRequestMatcher implements RequestMatcher {
* <p>
* To match a request URI like {@code /app/servlet/my/resource/**} where {@code /app}
* is the context path, you can do
* {@code PathPatternRequestMatcher.withDefaults().matcher("/servlet/my/resource/**")}
* {@code PathPatternRequestMatcher.pathPattern("/servlet/my/resource/**")}
*
* <p>
* If you have many paths that have a common path prefix, you can use

View File

@ -37,7 +37,6 @@ import org.springframework.security.web.authentication.AuthenticationEntryPointF
import org.springframework.security.web.authentication.HttpMessageConverterAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;
import org.springframework.security.web.webauthn.api.PublicKeyCredential;
import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestOptions;
@ -45,6 +44,8 @@ import org.springframework.security.web.webauthn.jackson.WebauthnJackson2Module;
import org.springframework.security.web.webauthn.management.RelyingPartyAuthenticationRequest;
import org.springframework.util.Assert;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
/**
* Authenticates {@code PublicKeyCredential<AuthenticatorAssertionResponse>} that is
* parsed from the body of the {@link HttpServletRequest} using the
@ -77,7 +78,7 @@ public class WebAuthnAuthenticationFilter extends AbstractAuthenticationProcessi
private PublicKeyCredentialRequestOptionsRepository requestOptionsRepository = new HttpSessionPublicKeyCredentialRequestOptionsRepository();
public WebAuthnAuthenticationFilter() {
super(PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.POST, "/login/webauthn"));
super(pathPattern(HttpMethod.POST, "/login/webauthn"));
setSecurityContextRepository(new HttpSessionSecurityContextRepository());
setAuthenticationFailureHandler(
new AuthenticationEntryPointFailureHandler(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)));

View File

@ -22,7 +22,6 @@ import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
@ -30,6 +29,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
/**
* Tests for {@link RequestMatcherRedirectFilter}.
@ -40,8 +40,7 @@ public class RequestMatcherRedirectFilterTests {
@Test
public void doFilterWhenRequestMatchThenRedirectToSpecifiedUrl() throws Exception {
RequestMatcherRedirectFilter filter = new RequestMatcherRedirectFilter(new AntPathRequestMatcher("/context"),
"/test");
RequestMatcherRedirectFilter filter = new RequestMatcherRedirectFilter(pathPattern("/context"), "/test");
MockHttpServletRequest request = get("/context").build();
MockHttpServletResponse response = new MockHttpServletResponse();
@ -57,8 +56,7 @@ public class RequestMatcherRedirectFilterTests {
@Test
public void doFilterWhenRequestNotMatchThenNextFilter() throws Exception {
RequestMatcherRedirectFilter filter = new RequestMatcherRedirectFilter(new AntPathRequestMatcher("/context"),
"/test");
RequestMatcherRedirectFilter filter = new RequestMatcherRedirectFilter(pathPattern("/context"), "/test");
MockHttpServletRequest request = get("/test").build();
@ -81,21 +79,19 @@ public class RequestMatcherRedirectFilterTests {
@Test
public void constructWhenRedirectUrlNull() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new RequestMatcherRedirectFilter(new AntPathRequestMatcher("/**"), null))
.isThrownBy(() -> new RequestMatcherRedirectFilter(pathPattern("/**"), null))
.withMessage("redirectUrl cannot be empty");
}
@Test
public void constructWhenRedirectUrlEmpty() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new RequestMatcherRedirectFilter(new AntPathRequestMatcher("/**"), ""))
assertThatIllegalArgumentException().isThrownBy(() -> new RequestMatcherRedirectFilter(pathPattern("/**"), ""))
.withMessage("redirectUrl cannot be empty");
}
@Test
public void constructWhenRedirectUrlBlank() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new RequestMatcherRedirectFilter(new AntPathRequestMatcher("/**"), " "))
assertThatIllegalArgumentException().isThrownBy(() -> new RequestMatcherRedirectFilter(pathPattern("/**"), " "))
.withMessage("redirectUrl cannot be empty");
}

View File

@ -32,7 +32,6 @@ import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer;
import org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
@ -40,6 +39,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verify;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
@ExtendWith(MockitoExtension.class)
class AuthorizationManagerWebInvocationPrivilegeEvaluatorTests {
@ -118,7 +118,7 @@ class AuthorizationManagerWebInvocationPrivilegeEvaluatorTests {
void isAllowedWhenInvokesDelegateThenCachesRequestPath() {
RequestMatcherDelegatingAuthorizationManager authorizationManager = RequestMatcherDelegatingAuthorizationManager
.builder()
.add(PathPatternRequestMatcher.withDefaults().matcher("/test/**"),
.add(pathPattern("/test/**"),
(authentication, context) -> this.authorizationManager.check(authentication, context.getRequest()))
.build();
AuthorizationManagerWebInvocationPrivilegeEvaluator privilegeEvaluator = new AuthorizationManagerWebInvocationPrivilegeEvaluator(

View File

@ -1,206 +0,0 @@
/*
* Copyright 2002-2023 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
*
* https://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.web.access;
import java.util.Collections;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.http.HttpServletRequest;
import org.assertj.core.api.AssertionsForClassTypes;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
/**
* @author Rob Winch
*/
@ExtendWith(MockitoExtension.class)
class HandlerMappingIntrospectorRequestTransformerTests {
@Mock
HandlerMappingIntrospector hmi;
HandlerMappingIntrospectorRequestTransformer transformer;
@BeforeEach
void setup() {
this.transformer = new HandlerMappingIntrospectorRequestTransformer(this.hmi);
}
@Test
void constructorWhenHmiIsNullThenIllegalArgumentException() {
AssertionsForClassTypes.assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> new HandlerMappingIntrospectorRequestTransformer(null));
}
@Test
void transformThenNewRequestPassedToSetCache() {
MockHttpServletRequest request = new MockHttpServletRequest();
HttpServletRequest transformedRequest = this.transformer.transform(request);
ArgumentCaptor<HttpServletRequest> requestArg = ArgumentCaptor.forClass(HttpServletRequest.class);
verify(this.hmi).setCache(requestArg.capture());
assertThat(transformedRequest).isNotEqualTo(request);
}
@Test
void transformThenResultPassedToSetCache() {
MockHttpServletRequest request = new MockHttpServletRequest();
HttpServletRequest transformedRequest = this.transformer.transform(request);
ArgumentCaptor<HttpServletRequest> requestArg = ArgumentCaptor.forClass(HttpServletRequest.class);
verify(this.hmi).setCache(requestArg.capture());
assertThat(requestArg.getValue()).isEqualTo(transformedRequest);
}
/**
* The request passed into the transformer does not allow interactions on certain
* methods, we need to ensure that the methods used by
* {@link HandlerMappingIntrospector#setCache(HttpServletRequest)} are overridden.
*/
@Test
void transformThenResultDoesNotDelegateToSetAttribute() {
HttpServletRequest request = mock(HttpServletRequest.class);
this.transformer.transform(request);
ArgumentCaptor<HttpServletRequest> requestArg = ArgumentCaptor.forClass(HttpServletRequest.class);
verify(this.hmi).setCache(requestArg.capture());
HttpServletRequest transformedRequest = requestArg.getValue();
String attrName = "any";
String attrValue = "value";
transformedRequest.setAttribute(attrName, attrValue);
verifyNoInteractions(request);
assertThat(transformedRequest.getAttribute(attrName)).isEqualTo(attrValue);
}
@Test
void transformThenSetAttributeWorks() {
HttpServletRequest request = mock(HttpServletRequest.class);
this.transformer.transform(request);
ArgumentCaptor<HttpServletRequest> requestArg = ArgumentCaptor.forClass(HttpServletRequest.class);
verify(this.hmi).setCache(requestArg.capture());
HttpServletRequest transformedRequest = requestArg.getValue();
String attrName = "any";
String attrValue = "value";
transformedRequest.setAttribute(attrName, attrValue);
assertThat(transformedRequest.getAttribute(attrName)).isEqualTo(attrValue);
}
/**
* The request passed into the transformer does not allow interactions on certain
* methods, we need to ensure that the methods used by
* {@link HandlerMappingIntrospector#setCache(HttpServletRequest)} are overridden.
*/
@Test
void transformThenResultDoesNotDelegateToGetAttribute() {
HttpServletRequest request = mock(HttpServletRequest.class);
this.transformer.transform(request);
ArgumentCaptor<HttpServletRequest> requestArg = ArgumentCaptor.forClass(HttpServletRequest.class);
verify(this.hmi).setCache(requestArg.capture());
HttpServletRequest transformedRequest = requestArg.getValue();
transformedRequest.getAttribute("any");
verifyNoInteractions(request);
}
/**
* The request passed into the transformer does not allow interactions on certain
* methods, we need to ensure that the methods used by
* {@link HandlerMappingIntrospector#setCache(HttpServletRequest)} are overridden.
*/
@Test
void transformThenResultDoesNotDelegateToGetAttributeNames() {
HttpServletRequest request = mock(HttpServletRequest.class);
this.transformer.transform(request);
ArgumentCaptor<HttpServletRequest> requestArg = ArgumentCaptor.forClass(HttpServletRequest.class);
verify(this.hmi).setCache(requestArg.capture());
HttpServletRequest transformedRequest = requestArg.getValue();
transformedRequest.getAttributeNames();
verifyNoInteractions(request);
}
@Test
void transformThenGetAttributeNamesWorks() {
HttpServletRequest request = mock(HttpServletRequest.class);
this.transformer.transform(request);
ArgumentCaptor<HttpServletRequest> requestArg = ArgumentCaptor.forClass(HttpServletRequest.class);
verify(this.hmi).setCache(requestArg.capture());
HttpServletRequest transformedRequest = requestArg.getValue();
String attrName = "any";
String attrValue = "value";
transformedRequest.setAttribute(attrName, attrValue);
assertThat(Collections.list(transformedRequest.getAttributeNames())).containsExactly(attrName);
}
/**
* The request passed into the transformer does not allow interactions on certain
* methods, we need to ensure that the methods used by
* {@link HandlerMappingIntrospector#setCache(HttpServletRequest)} are overridden.
*/
@Test
void transformThenResultDoesNotDelegateToRemoveAttribute() {
HttpServletRequest request = mock(HttpServletRequest.class);
this.transformer.transform(request);
ArgumentCaptor<HttpServletRequest> requestArg = ArgumentCaptor.forClass(HttpServletRequest.class);
verify(this.hmi).setCache(requestArg.capture());
HttpServletRequest transformedRequest = requestArg.getValue();
transformedRequest.removeAttribute("any");
verifyNoInteractions(request);
}
/**
* The request passed into the transformer does not allow interactions on certain
* methods, we need to ensure that the methods used by
* {@link HandlerMappingIntrospector#setCache(HttpServletRequest)} are overridden.
*/
@Test
void transformThenResultDoesNotDelegateToGetDispatcherType() {
HttpServletRequest request = mock(HttpServletRequest.class);
this.transformer.transform(request);
ArgumentCaptor<HttpServletRequest> requestArg = ArgumentCaptor.forClass(HttpServletRequest.class);
verify(this.hmi).setCache(requestArg.capture());
HttpServletRequest transformedRequest = requestArg.getValue();
assertThat(transformedRequest.getDispatcherType()).isEqualTo(DispatcherType.REQUEST);
verifyNoInteractions(request);
}
}

View File

@ -22,17 +22,18 @@ import java.util.LinkedHashMap;
import jakarta.servlet.FilterChain;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpMethod;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.springframework.security.web.servlet.TestMockHttpServletRequests.request;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
/**
* Tests {@link DefaultFilterInvocationSecurityMetadataSource}.
@ -45,9 +46,9 @@ public class DefaultFilterInvocationSecurityMetadataSourceTests {
private Collection<ConfigAttribute> def = SecurityConfig.createList("ROLE_ONE");
private void createFids(String pattern, String method) {
private void createFids(String pattern, HttpMethod method) {
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<>();
requestMap.put(new AntPathRequestMatcher(pattern, method), this.def);
requestMap.put(pathPattern(method, pattern), this.def);
this.fids = new DefaultFilterInvocationSecurityMetadataSource(requestMap);
}
@ -89,7 +90,7 @@ public class DefaultFilterInvocationSecurityMetadataSourceTests {
@Test
public void httpMethodLookupSucceeds() {
createFids("/somepage**", "GET");
createFids("/somepage**", HttpMethod.GET);
FilterInvocation fi = createFilterInvocation("/somepage", null, null, "GET");
Collection<ConfigAttribute> attrs = this.fids.getAttributes(fi);
assertThat(attrs).isEqualTo(this.def);
@ -105,7 +106,7 @@ public class DefaultFilterInvocationSecurityMetadataSourceTests {
@Test
public void requestWithDifferentHttpMethodDoesntMatch() {
createFids("/somepage**", "GET");
createFids("/somepage**", HttpMethod.GET);
FilterInvocation fi = createFilterInvocation("/somepage", null, null, "POST");
Collection<ConfigAttribute> attrs = this.fids.getAttributes(fi);
assertThat(attrs).isNull();
@ -116,8 +117,8 @@ public class DefaultFilterInvocationSecurityMetadataSourceTests {
public void mixingPatternsWithAndWithoutHttpMethodsIsSupported() {
LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> requestMap = new LinkedHashMap<>();
Collection<ConfigAttribute> userAttrs = SecurityConfig.createList("A");
requestMap.put(new AntPathRequestMatcher("/user/**", null), userAttrs);
requestMap.put(new AntPathRequestMatcher("/teller/**", "GET"), SecurityConfig.createList("B"));
requestMap.put(pathPattern("/user/**"), userAttrs);
requestMap.put(pathPattern(HttpMethod.GET, "/teller/**"), SecurityConfig.createList("B"));
this.fids = new DefaultFilterInvocationSecurityMetadataSource(requestMap);
FilterInvocation fi = createFilterInvocation("/user", null, null, "GET");
Collection<ConfigAttribute> attrs = this.fids.getAttributes(fi);

View File

@ -28,14 +28,13 @@ import org.springframework.security.authorization.AuthorityAuthorizationManager;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.SingleResultAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
/**
* Tests for {@link RequestMatcherDelegatingAuthorizationManager}.
@ -63,18 +62,16 @@ public class RequestMatcherDelegatingAuthorizationManagerTests {
@Test
public void addWhenManagerNullThenException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> RequestMatcherDelegatingAuthorizationManager.builder()
.add(new MvcRequestMatcher(null, "/grant"), null)
.build())
assertThatIllegalArgumentException().isThrownBy(
() -> RequestMatcherDelegatingAuthorizationManager.builder().add(pathPattern("/grant"), null).build())
.withMessage("manager cannot be null");
}
@Test
public void checkWhenMultipleMappingsConfiguredThenDelegatesMatchingManager() {
RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()
.add(new MvcRequestMatcher(null, "/grant"), SingleResultAuthorizationManager.permitAll())
.add(new MvcRequestMatcher(null, "/deny"), SingleResultAuthorizationManager.denyAll())
.add(pathPattern(null, "/grant"), SingleResultAuthorizationManager.permitAll())
.add(pathPattern(null, "/deny"), SingleResultAuthorizationManager.denyAll())
.build();
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
@ -97,11 +94,10 @@ public class RequestMatcherDelegatingAuthorizationManagerTests {
public void checkWhenMultipleMappingsConfiguredWithConsumerThenDelegatesMatchingManager() {
RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()
.mappings((m) -> {
m.add(new RequestMatcherEntry<>(new MvcRequestMatcher(null, "/grant"),
SingleResultAuthorizationManager.permitAll()));
m.add(new RequestMatcherEntry<>(pathPattern("/grant"), SingleResultAuthorizationManager.permitAll()));
m.add(new RequestMatcherEntry<>(AnyRequestMatcher.INSTANCE,
AuthorityAuthorizationManager.hasRole("ADMIN")));
m.add(new RequestMatcherEntry<>(new MvcRequestMatcher(null, "/afterAny"),
m.add(new RequestMatcherEntry<>(pathPattern("/afterAny"),
SingleResultAuthorizationManager.permitAll()));
})
.build();
@ -156,7 +152,7 @@ public class RequestMatcherDelegatingAuthorizationManagerTests {
.isThrownBy(() -> RequestMatcherDelegatingAuthorizationManager.builder()
.anyRequest()
.authenticated()
.requestMatchers(new AntPathRequestMatcher("/authenticated"))
.requestMatchers(pathPattern("/authenticated"))
.authenticated()
.build())
.withMessage("Can't configure requestMatchers after anyRequest");

View File

@ -47,7 +47,6 @@ import org.springframework.security.web.authentication.session.SessionAuthentica
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.firewall.DefaultHttpFirewall;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.test.util.ReflectionTestUtils;
@ -62,6 +61,7 @@ import static org.mockito.Mockito.verify;
import static org.springframework.security.web.servlet.TestMockHttpServletRequests.Builder;
import static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;
import static org.springframework.security.web.servlet.TestMockHttpServletRequests.post;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
/**
* Tests {@link AbstractAuthenticationProcessingFilter}.
@ -238,8 +238,8 @@ public class AbstractAuthenticationProcessingFilterTests {
MockFilterChain chain = new MockFilterChain(false);
MockHttpServletResponse response = new MockHttpServletResponse();
// Setup our test object, to grant access
MockAuthenticationFilter filter = new MockAuthenticationFilter(
new AntPathRequestMatcher("/j_eradicate_corona_virus"), mock(AuthenticationManager.class));
MockAuthenticationFilter filter = new MockAuthenticationFilter(pathPattern("/j_eradicate_corona_virus"),
mock(AuthenticationManager.class));
filter.setSessionAuthenticationStrategy(mock(SessionAuthenticationStrategy.class));
filter.setAuthenticationSuccessHandler(this.successHandler);
filter.setAuthenticationFailureHandler(this.failureHandler);
@ -273,7 +273,7 @@ public class AbstractAuthenticationProcessingFilterTests {
filter.setAuthenticationManager(mock(AuthenticationManager.class));
filter.setAuthenticationSuccessHandler(this.successHandler);
assertThatIllegalArgumentException().isThrownBy(() -> filter.setFilterProcessesUrl(null))
.withMessage("Pattern cannot be null or empty");
.withMessage("pattern cannot be null");
}
@Test

View File

@ -23,12 +23,12 @@ import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.Mockito.mock;
import static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
/**
* Tests for {@link RequestMatcherDelegatingAuthenticationManagerResolverTests}
@ -45,8 +45,8 @@ public class RequestMatcherDelegatingAuthenticationManagerResolverTests {
public void resolveWhenMatchesThenReturnsAuthenticationManager() {
RequestMatcherDelegatingAuthenticationManagerResolver resolver = RequestMatcherDelegatingAuthenticationManagerResolver
.builder()
.add(new AntPathRequestMatcher("/one/**"), this.one)
.add(new AntPathRequestMatcher("/two/**"), this.two)
.add(pathPattern("/one/**"), this.one)
.add(pathPattern("/two/**"), this.two)
.build();
MockHttpServletRequest request = get("/one/location").build();
@ -57,8 +57,8 @@ public class RequestMatcherDelegatingAuthenticationManagerResolverTests {
public void resolveWhenDoesNotMatchThenReturnsDefaultAuthenticationManager() {
RequestMatcherDelegatingAuthenticationManagerResolver resolver = RequestMatcherDelegatingAuthenticationManagerResolver
.builder()
.add(new AntPathRequestMatcher("/one/**"), this.one)
.add(new AntPathRequestMatcher("/two/**"), this.two)
.add(pathPattern("/one/**"), this.one)
.add(pathPattern("/two/**"), this.two)
.build();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/wrong/location");

View File

@ -41,7 +41,6 @@ import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.ForwardAuthenticationFailureHandler;
import org.springframework.security.web.authentication.ForwardAuthenticationSuccessHandler;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@ -52,6 +51,7 @@ import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
/**
* @author Rob Winch
@ -367,7 +367,7 @@ public class AbstractPreAuthenticatedProcessingFilterTests {
MockHttpServletResponse response = new MockHttpServletResponse();
MockFilterChain chain = new MockFilterChain();
ConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();
filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/no-matching"));
filter.setRequiresAuthenticationRequestMatcher(pathPattern("/no-matching"));
AuthenticationManager am = mock(AuthenticationManager.class);
filter.setAuthenticationManager(am);
filter.afterPropertiesSet();
@ -381,7 +381,7 @@ public class AbstractPreAuthenticatedProcessingFilterTests {
MockHttpServletResponse response = new MockHttpServletResponse();
MockFilterChain chain = new MockFilterChain();
ConcretePreAuthenticatedProcessingFilter filter = new ConcretePreAuthenticatedProcessingFilter();
filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/**"));
filter.setRequiresAuthenticationRequestMatcher(pathPattern("/**"));
AuthenticationManager am = mock(AuthenticationManager.class);
filter.setAuthenticationManager(am);
filter.afterPropertiesSet();

View File

@ -46,7 +46,6 @@ import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.util.WebUtils;
@ -62,6 +61,7 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
/**
* Tests {@link BasicAuthenticationFilter}.
@ -513,7 +513,7 @@ public class BasicAuthenticationFilterTests {
static class TestAuthenticationConverter implements AuthenticationConverter {
private final RequestMatcher matcher = AntPathRequestMatcher.antMatcher("/ignored");
private final RequestMatcher matcher = pathPattern("/ignored");
private final BasicAuthenticationConverter delegate = new BasicAuthenticationConverter();

View File

@ -29,6 +29,7 @@ import org.springframework.web.util.ServletRequestPathUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
/**
@ -38,49 +39,49 @@ public class PathPatternRequestMatcherTests {
@Test
void matcherWhenPatternMatchesRequestThenMatchResult() {
RequestMatcher matcher = PathPatternRequestMatcher.withDefaults().matcher("/uri");
RequestMatcher matcher = pathPattern("/uri");
assertThat(matcher.matches(request("/uri"))).isTrue();
}
@Test
void matcherWhenPatternContainsPlaceholdersThenMatchResult() {
RequestMatcher matcher = PathPatternRequestMatcher.withDefaults().matcher("/uri/{username}");
RequestMatcher matcher = pathPattern("/uri/{username}");
assertThat(matcher.matcher(request("/uri/bob")).getVariables()).containsEntry("username", "bob");
}
@Test
void matcherWhenOnlyPathInfoMatchesThenNoMatch() {
RequestMatcher matcher = PathPatternRequestMatcher.withDefaults().matcher("/uri");
RequestMatcher matcher = pathPattern("/uri");
assertThat(matcher.matches(request("GET", "/mvc/uri", "/mvc"))).isFalse();
}
@Test
void matcherWhenUriContainsServletPathThenMatch() {
RequestMatcher matcher = PathPatternRequestMatcher.withDefaults().matcher("/mvc/uri");
RequestMatcher matcher = pathPattern("/mvc/uri");
assertThat(matcher.matches(request("GET", "/mvc/uri", "/mvc"))).isTrue();
}
@Test
void matcherWhenSameMethodThenMatchResult() {
RequestMatcher matcher = PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.GET, "/uri");
RequestMatcher matcher = pathPattern(HttpMethod.GET, "/uri");
assertThat(matcher.matches(request("/uri"))).isTrue();
}
@Test
void matcherWhenDifferentPathThenNoMatch() {
RequestMatcher matcher = PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.GET, "/uri");
RequestMatcher matcher = pathPattern(HttpMethod.GET, "/uri");
assertThat(matcher.matches(request("GET", "/urj", ""))).isFalse();
}
@Test
void matcherWhenDifferentMethodThenNoMatch() {
RequestMatcher matcher = PathPatternRequestMatcher.withDefaults().matcher(HttpMethod.GET, "/uri");
RequestMatcher matcher = pathPattern(HttpMethod.GET, "/uri");
assertThat(matcher.matches(request("POST", "/mvc/uri", "/mvc"))).isFalse();
}
@Test
void matcherWhenNoMethodThenMatches() {
RequestMatcher matcher = PathPatternRequestMatcher.withDefaults().matcher("/uri");
RequestMatcher matcher = pathPattern("/uri");
assertThat(matcher.matches(request("POST", "/uri", ""))).isTrue();
assertThat(matcher.matches(request("GET", "/uri", ""))).isTrue();
}

View File

@ -33,6 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
import static org.mockito.BDDMockito.given;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
/**
* @author Rob Winch
@ -59,7 +60,7 @@ public class AndRequestMatcherTests {
@Test
public void constructorListOfDoesNotThrowNullPointer() {
new AndRequestMatcher(List.of(new AntPathRequestMatcher("/test")));
new AndRequestMatcher(List.of(pathPattern("/test")));
}
@Test

View File

@ -34,6 +34,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
import static org.assertj.core.api.Assertions.assertThatNullPointerException;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
/**
* @author Rob Winch
@ -60,7 +61,7 @@ public class OrRequestMatcherTests {
@Test
public void constructorListOfDoesNotThrowNullPointer() {
new OrRequestMatcher(List.of(new AntPathRequestMatcher("/test")));
new OrRequestMatcher(List.of(pathPattern("/test")));
}
@Test