From ff52e05b248339b5486c35b418d22dfa9454d90f Mon Sep 17 00:00:00 2001 From: Josh Cummings <3627351+jzheaux@users.noreply.github.com> Date: Wed, 26 Mar 2025 12:41:14 -0600 Subject: [PATCH] Favor PathPatternRequestMatcher in XML Configuration Update BeanDefinitionParsers to use PathPatternRequestMatcher conditionally on the presence of a PathPatternRequestMatcher.Builder bean Closes gh-16828 --- .../http/FormLoginBeanDefinitionParser.java | 4 +- .../config/http/HttpConfigurationBuilder.java | 3 +- .../http/LogoutBeanDefinitionParser.java | 4 +- .../http/RequestMatcherFactoryBean.java | 64 +++++++++++++++++++ .../http/Saml2LoginBeanDefinitionParser.java | 7 +- .../http/Saml2LogoutBeanDefinitionParser.java | 10 +-- ...ownChangePasswordBeanDefinitionParser.java | 8 ++- docs/modules/ROOT/pages/migration/web.adoc | 7 ++ 8 files changed, 89 insertions(+), 18 deletions(-) create mode 100644 config/src/main/java/org/springframework/security/config/http/RequestMatcherFactoryBean.java diff --git a/config/src/main/java/org/springframework/security/config/http/FormLoginBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/http/FormLoginBeanDefinitionParser.java index e2cb9f988e..217ef92359 100644 --- a/config/src/main/java/org/springframework/security/config/http/FormLoginBeanDefinitionParser.java +++ b/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"); diff --git a/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java b/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java index d15c8ba65a..fd18eb3362 100644 --- a/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java +++ b/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()); diff --git a/config/src/main/java/org/springframework/security/config/http/LogoutBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/http/LogoutBeanDefinitionParser.java index ed662fd526..72a479ef07 100644 --- a/config/src/main/java/org/springframework/security/config/http/LogoutBeanDefinitionParser.java +++ b/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"); diff --git a/config/src/main/java/org/springframework/security/config/http/RequestMatcherFactoryBean.java b/config/src/main/java/org/springframework/security/config/http/RequestMatcherFactoryBean.java new file mode 100644 index 0000000000..f08a21727a --- /dev/null +++ b/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, 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(); + } + +} diff --git a/config/src/main/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParser.java index 9f0560078e..0fff3c215b 100644 --- a/config/src/main/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParser.java +++ b/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); diff --git a/config/src/main/java/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParser.java index 24566458e1..bef7a69d19 100644 --- a/config/src/main/java/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParser.java +++ b/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 diff --git a/config/src/main/java/org/springframework/security/config/http/WellKnownChangePasswordBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/http/WellKnownChangePasswordBeanDefinitionParser.java index f911c674e8..8cd34c045b 100644 --- a/config/src/main/java/org/springframework/security/config/http/WellKnownChangePasswordBeanDefinitionParser.java +++ b/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); diff --git a/docs/modules/ROOT/pages/migration/web.adoc b/docs/modules/ROOT/pages/migration/web.adoc index 90675d17fc..8b92d2166b 100644 --- a/docs/modules/ROOT/pages/migration/web.adoc +++ b/docs/modules/ROOT/pages/migration/web.adoc @@ -29,6 +29,13 @@ fun requestMatcherBuilder(): PathPatternRequestMatcherBuilderFactoryBean { return PathPatternRequestMatcherBuilderFactoryBean() } ---- + +Xml:: ++ +[source,xml,role="secondary"] +---- + +---- ====== This will tell the Spring Security DSL to use `PathPatternRequestMatcher` for all request matchers that it constructs.