Browse Source

Favor PathPatternRequestMatcher in XML Configuration

Update BeanDefinitionParsers to use PathPatternRequestMatcher conditionally
on the presence of a PathPatternRequestMatcher.Builder bean

Closes gh-16828
Josh Cummings 5 months ago
parent
commit
ff52e05b24

+ 2 - 2
config/src/main/java/org/springframework/security/config/http/FormLoginBeanDefinitionParser.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2018 the original author or authors.
+ * Copyright 2002-2025 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.
@@ -178,7 +178,7 @@ public class FormLoginBeanDefinitionParser {
 		}
 		this.loginProcessingUrl = loginUrl;
 		BeanDefinitionBuilder matcherBuilder = BeanDefinitionBuilder
-			.rootBeanDefinition("org.springframework.security.web.util.matcher.AntPathRequestMatcher");
+			.rootBeanDefinition(RequestMatcherFactoryBean.class);
 		matcherBuilder.addConstructorArgValue(loginUrl);
 		if (this.loginMethod != null) {
 			matcherBuilder.addConstructorArgValue("POST");

+ 1 - 2
config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java

@@ -83,7 +83,6 @@ import org.springframework.security.web.session.SessionManagementFilter;
 import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;
 import org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;
 import org.springframework.security.web.transport.HttpsRedirectFilter;
-import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
 import org.springframework.util.Assert;
 import org.springframework.util.ClassUtils;
 import org.springframework.util.StringUtils;
@@ -751,7 +750,7 @@ class HttpConfigurationBuilder {
 				requestCacheBldr.addPropertyValue("portResolver", this.portResolver);
 				if (this.csrfFilter != null) {
 					BeanDefinitionBuilder requestCacheMatcherBldr = BeanDefinitionBuilder
-						.rootBeanDefinition(AntPathRequestMatcher.class);
+						.rootBeanDefinition(RequestMatcherFactoryBean.class);
 					requestCacheMatcherBldr.addConstructorArgValue("/**");
 					requestCacheMatcherBldr.addConstructorArgValue("GET");
 					requestCacheBldr.addPropertyValue("requestMatcher", requestCacheMatcherBldr.getBeanDefinition());

+ 2 - 2
config/src/main/java/org/springframework/security/config/http/LogoutBeanDefinitionParser.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2025 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.
@@ -134,7 +134,7 @@ class LogoutBeanDefinitionParser implements BeanDefinitionParser {
 
 	private BeanDefinition getLogoutRequestMatcher(String logoutUrl) {
 		BeanDefinitionBuilder matcherBuilder = BeanDefinitionBuilder
-			.rootBeanDefinition("org.springframework.security.web.util.matcher.AntPathRequestMatcher");
+			.rootBeanDefinition(RequestMatcherFactoryBean.class);
 		matcherBuilder.addConstructorArgValue(logoutUrl);
 		if (this.csrfEnabled) {
 			matcherBuilder.addConstructorArgValue("POST");

+ 64 - 0
config/src/main/java/org/springframework/security/config/http/RequestMatcherFactoryBean.java

@@ -0,0 +1,64 @@
+/*
+ * Copyright 2002-2025 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.config.http;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+
+@Deprecated
+public final class RequestMatcherFactoryBean implements FactoryBean<RequestMatcher>, ApplicationContextAware {
+
+	private PathPatternRequestMatcher.Builder builder;
+
+	private final HttpMethod method;
+
+	private final String path;
+
+	public RequestMatcherFactoryBean(String path) {
+		this(path, null);
+	}
+
+	public RequestMatcherFactoryBean(String path, HttpMethod method) {
+		this.method = method;
+		this.path = path;
+	}
+
+	@Override
+	public RequestMatcher getObject() throws Exception {
+		if (this.builder != null) {
+			return this.builder.matcher(this.method, this.path);
+		}
+		return new AntPathRequestMatcher(this.path, (this.method != null) ? this.method.name() : null);
+	}
+
+	@Override
+	public Class<?> getObjectType() {
+		return null;
+	}
+
+	@Override
+	public void setApplicationContext(ApplicationContext context) throws BeansException {
+		this.builder = context.getBeanProvider(PathPatternRequestMatcher.Builder.class).getIfUnique();
+	}
+
+}

+ 3 - 4
config/src/main/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParser.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2025 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.
@@ -43,7 +43,6 @@ import org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthen
 import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;
 import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
 import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
-import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
 import org.springframework.util.StringUtils;
 import org.springframework.util.xml.DomUtils;
 
@@ -216,7 +215,7 @@ final class Saml2LoginBeanDefinitionParser implements BeanDefinitionParser {
 		}
 		if (saml2LoginAuthenticationEntryPoint != null) {
 			BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
-				.rootBeanDefinition(AntPathRequestMatcher.class);
+				.rootBeanDefinition(RequestMatcherFactoryBean.class);
 			requestMatcherBuilder.addConstructorArgValue(this.loginProcessingUrl);
 			BeanDefinition requestMatcher = requestMatcherBuilder.getBeanDefinition();
 			this.entryPoints.put(requestMatcher, saml2LoginAuthenticationEntryPoint);
@@ -260,7 +259,7 @@ final class Saml2LoginBeanDefinitionParser implements BeanDefinitionParser {
 
 	private void registerDefaultCsrfOverride() {
 		BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
-			.rootBeanDefinition(AntPathRequestMatcher.class);
+			.rootBeanDefinition(RequestMatcherFactoryBean.class);
 		requestMatcherBuilder.addConstructorArgValue(this.loginProcessingUrl);
 		BeanDefinition requestMatcher = requestMatcherBuilder.getBeanDefinition();
 		this.csrfIgnoreRequestMatchers.add(requestMatcher);

+ 5 - 5
config/src/main/java/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParser.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2024 the original author or authors.
+ * Copyright 2002-2025 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.
@@ -41,7 +41,6 @@ import org.springframework.security.web.authentication.logout.LogoutSuccessEvent
 import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
 import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
 import org.springframework.security.web.util.matcher.AndRequestMatcher;
-import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
 import org.springframework.security.web.util.matcher.ParameterRequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcher;
 import org.springframework.util.CollectionUtils;
@@ -171,7 +170,7 @@ final class Saml2LogoutBeanDefinitionParser implements BeanDefinitionParser {
 	}
 
 	private BeanMetadataElement createLogoutRequestMatcher() {
-		BeanMetadataElement logoutMatcher = BeanDefinitionBuilder.rootBeanDefinition(AntPathRequestMatcher.class)
+		BeanMetadataElement logoutMatcher = BeanDefinitionBuilder.rootBeanDefinition(RequestMatcherFactoryBean.class)
 			.addConstructorArgValue(this.logoutUrl)
 			.addConstructorArgValue("POST")
 			.getBeanDefinition();
@@ -184,7 +183,8 @@ final class Saml2LogoutBeanDefinitionParser implements BeanDefinitionParser {
 	}
 
 	private BeanMetadataElement createSaml2LogoutRequestMatcher() {
-		BeanMetadataElement logoutRequestMatcher = BeanDefinitionBuilder.rootBeanDefinition(AntPathRequestMatcher.class)
+		BeanMetadataElement logoutRequestMatcher = BeanDefinitionBuilder
+			.rootBeanDefinition(RequestMatcherFactoryBean.class)
 			.addConstructorArgValue(this.logoutRequestUrl)
 			.getBeanDefinition();
 		BeanMetadataElement saml2RequestMatcher = BeanDefinitionBuilder
@@ -198,7 +198,7 @@ final class Saml2LogoutBeanDefinitionParser implements BeanDefinitionParser {
 
 	private BeanMetadataElement createSaml2LogoutResponseMatcher() {
 		BeanMetadataElement logoutResponseMatcher = BeanDefinitionBuilder
-			.rootBeanDefinition(AntPathRequestMatcher.class)
+			.rootBeanDefinition(RequestMatcherFactoryBean.class)
 			.addConstructorArgValue(this.logoutResponseUrl)
 			.getBeanDefinition();
 		BeanMetadataElement saml2ResponseMatcher = BeanDefinitionBuilder

+ 5 - 3
config/src/main/java/org/springframework/security/config/http/WellKnownChangePasswordBeanDefinitionParser.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2025 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.
@@ -23,7 +23,6 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
 import org.springframework.beans.factory.xml.BeanDefinitionParser;
 import org.springframework.beans.factory.xml.ParserContext;
 import org.springframework.security.web.RequestMatcherRedirectFilter;
-import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
 import org.springframework.util.StringUtils;
 
 /**
@@ -45,9 +44,12 @@ public final class WellKnownChangePasswordBeanDefinitionParser implements BeanDe
 	 */
 	@Override
 	public BeanDefinition parse(Element element, ParserContext parserContext) {
+		BeanDefinition requestMatcher = BeanDefinitionBuilder.rootBeanDefinition(RequestMatcherFactoryBean.class)
+			.addConstructorArgValue(WELL_KNOWN_CHANGE_PASSWORD_PATTERN)
+			.getBeanDefinition();
 		BeanDefinition changePasswordFilter = BeanDefinitionBuilder
 			.rootBeanDefinition(RequestMatcherRedirectFilter.class)
-			.addConstructorArgValue(new AntPathRequestMatcher(WELL_KNOWN_CHANGE_PASSWORD_PATTERN))
+			.addConstructorArgValue(requestMatcher)
 			.addConstructorArgValue(getChangePasswordPage(element))
 			.getBeanDefinition();
 		parserContext.getReaderContext().registerWithGeneratedName(changePasswordFilter);

+ 7 - 0
docs/modules/ROOT/pages/migration/web.adoc

@@ -29,6 +29,13 @@ fun requestMatcherBuilder(): PathPatternRequestMatcherBuilderFactoryBean {
     return PathPatternRequestMatcherBuilderFactoryBean()
 }
 ----
+
+Xml::
++
+[source,xml,role="secondary"]
+----
+<b:bean class="org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean"/>
+----
 ======
 
 This will tell the Spring Security DSL to use `PathPatternRequestMatcher` for all request matchers that it constructs.