From 05fdcd6a089409e8ba6e92f4e64fa38785c2256f Mon Sep 17 00:00:00 2001
From: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
Date: Mon, 24 Mar 2025 22:03:22 -0600
Subject: [PATCH] Deprecate MvcRequestMatcher
Closes gh-16631
---
.../authorize-http-requests.adoc | 2 +-
.../ROOT/pages/servlet/integrations/mvc.adoc | 309 +++++++-----------
.../util/matcher/MvcRequestMatcher.java | 2 +
3 files changed, 117 insertions(+), 196 deletions(-)
diff --git a/docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc b/docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc
index 2761d71c5f..584c85da47 100644
--- a/docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc
+++ b/docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc
@@ -583,7 +583,7 @@ Generally speaking, you can use `requestMatchers(String)` as demonstrated above.
However, if you have authorization rules from multiple servlets, you need to specify those:
-.Match by MvcRequestMatcher
+.Match by PathPatternRequestMatcher
[tabs]
======
Java::
diff --git a/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc b/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc
index 49314e8ba8..89b2c45aca 100644
--- a/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc
+++ b/docs/modules/ROOT/pages/servlet/integrations/mvc.adoc
@@ -22,45 +22,13 @@ This means that, if you use more advanced options, such as integrating with `Web
====
[[mvc-requestmatcher]]
-== MvcRequestMatcher
+== PathPatternRequestMatcher
-Spring Security provides deep integration with how Spring MVC matches on URLs with `MvcRequestMatcher`.
+Spring Security provides deep integration with how Spring MVC matches on URLs with `PathPatternRequestMatcher`.
This is helpful to ensure that your Security rules match the logic used to handle your requests.
-To use `MvcRequestMatcher`, you must place the Spring Security Configuration in the same `ApplicationContext` as your `DispatcherServlet`.
-This is necessary because Spring Security's `MvcRequestMatcher` expects a `HandlerMappingIntrospector` bean with the name of `mvcHandlerMappingIntrospector` to be registered by your Spring MVC configuration that is used to perform the matching.
-
-For a `web.xml` file, this means that you should place your configuration in the `DispatcherServlet.xml`:
-
-[source,xml]
-----
-
- org.springframework.web.context.ContextLoaderListener
-
-
-
-
- contextConfigLocation
- /WEB-INF/spring/*.xml
-
-
-
- spring
- org.springframework.web.servlet.DispatcherServlet
-
-
- contextConfigLocation
-
-
-
-
-
- spring
- /
-
-----
-
-The following `WebSecurityConfiguration` in placed in the `ApplicationContext` of the `DispatcherServlet`.
+`PathPatternRequestMatcher` must use the same `PathPatternParser` as Spring MVC.
+If you are not customizing the `PathPatternParser`, then you can do:
[tabs]
======
@@ -68,24 +36,9 @@ Java::
+
[source,java,role="primary"]
----
-public class SecurityInitializer extends
- AbstractAnnotationConfigDispatcherServletInitializer {
-
- @Override
- protected Class>[] getRootConfigClasses() {
- return null;
- }
-
- @Override
- protected Class>[] getServletConfigClasses() {
- return new Class[] { RootConfiguration.class,
- WebMvcConfiguration.class };
- }
-
- @Override
- protected String[] getServletMappings() {
- return new String[] { "/" };
- }
+@Bean
+PathPatternRequestMatcherBuilderFactoryBean usePathPattern() {
+ return new PathPatternRequestMatcherBuilderFactoryBean();
}
----
@@ -93,25 +46,24 @@ Kotlin::
+
[source,kotlin,role="secondary"]
----
-class SecurityInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
- override fun getRootConfigClasses(): Array>? {
- return null
- }
-
- override fun getServletConfigClasses(): Array> {
- return arrayOf(
- RootConfiguration::class.java,
- WebMvcConfiguration::class.java
- )
- }
-
- override fun getServletMappings(): Array {
- return arrayOf("/")
- }
+@Bean
+fun usePathPattern(): PathPatternRequestMatcherBuilderFactoryBean {
+ return PathPatternRequestMatcherBuilderFactoryBean()
}
----
+
+Xml::
++
+[source,xml,role="secondary"]
+----
+
+----
======
+and Spring Security will find the appropriate Spring MVC configuration for you.
+
+If you *are* customizing Spring MVC's `PathPatternParser` instance, you will need to <>.
+
[NOTE]
====
We always recommend that you provide authorization rules by matching on the `HttpServletRequest` and method security.
@@ -121,132 +73,7 @@ Method security ensures that, if someone has bypassed the web authorization rule
This is known as https://en.wikipedia.org/wiki/Defense_in_depth_(computing)[Defense in Depth]
====
-Consider a controller that is mapped as follows:
-
-[tabs]
-======
-Java::
-+
-[source,java,role="primary"]
-----
-@RequestMapping("/admin")
-public String admin() {
- // ...
-}
-----
-
-Kotlin::
-+
-[source,kotlin,role="secondary"]
-----
-@RequestMapping("/admin")
-fun admin(): String {
- // ...
-}
-----
-======
-
-To restrict access to this controller method to admin users, you can provide authorization rules by matching on the `HttpServletRequest` with the following:
-
-[tabs]
-======
-Java::
-+
-[source,java,role="primary"]
-----
-@Bean
-public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
- http
- .authorizeHttpRequests((authorize) -> authorize
- .requestMatchers("/admin").hasRole("ADMIN")
- );
- return http.build();
-}
-----
-
-Kotlin::
-+
-[source,kotlin,role="secondary"]
-----
-@Bean
-open fun filterChain(http: HttpSecurity): SecurityFilterChain {
- http {
- authorizeHttpRequests {
- authorize("/admin", hasRole("ADMIN"))
- }
- }
- return http.build()
-}
-----
-======
-
-The following listing does the same thing in XML:
-
-[source,xml]
-----
-
-
-
-----
-
-With either configuration, the `/admin` URL requires the authenticated user to be an admin user.
-However, depending on our Spring MVC configuration, the `/admin.html` URL also maps to our `admin()` method.
-Additionally, depending on our Spring MVC configuration, the `/admin` URL also maps to our `admin()` method.
-
-The problem is that our security rule protects only `/admin`.
-We could add additional rules for all the permutations of Spring MVC, but this would be quite verbose and tedious.
-
-Fortunately, when using the `requestMatchers` DSL method, Spring Security automatically creates a `MvcRequestMatcher` if it detects that Spring MVC is available in the classpath.
-Therefore, it will protect the same URLs that Spring MVC will match on by using Spring MVC to match on the URL.
-
-One common requirement when using Spring MVC is to specify the servlet path property.
-
-For Java-based Configuration, you can use the `MvcRequestMatcher.Builder` to create multiple `MvcRequestMatcher` instances that share the same servlet path:
-
-[source,java,role="primary"]
-----
-@Bean
-public SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {
- MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector).servletPath("/path");
- http
- .authorizeHttpRequests((authorize) -> authorize
- .requestMatchers(mvcMatcherBuilder.pattern("/admin")).hasRole("ADMIN")
- .requestMatchers(mvcMatcherBuilder.pattern("/user")).hasRole("USER")
- );
- return http.build();
-}
-----
-
-For Kotlin and XML, this happens when you specify the servlet path for each path like so:
-
-[tabs]
-======
-Kotlin::
-+
-[source,kotlin,role="secondary"]
-----
-@Bean
-open fun filterChain(http: HttpSecurity): SecurityFilterChain {
- http {
- authorizeHttpRequests {
- authorize("/admin/**", "/mvc", hasRole("ADMIN"))
- authorize("/user/**", "/mvc", hasRole("USER"))
- }
- }
- return http.build()
-}
-----
-
-Xml::
-+
-[source,xml, role="secondary"]
-----
-
-
-
-
-----
-======
+Now that Spring MVC is integrated with Spring Security, you are ready to write some xref:servlet/authorization/authorize-http-requests.adoc[authorization rules] that will use `PathPatternRequestMatcher`.
[[mvc-authentication-principal]]
== @AuthenticationPrincipal
@@ -766,3 +593,95 @@ class CsrfController {
It is important to keep the `CsrfToken` a secret from other domains.
This means that, if you use https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS[Cross Origin Sharing (CORS)], you should *NOT* expose the `CsrfToken` to any external domains.
+
+[[security-mvc-same-application-context]]
+== Configuring Spring MVC and Spring Security in the Same Application Context
+
+If you are using Boot, Spring MVC and Spring Security are in the same application context by default.
+
+Otherwise, for Java Config, including both `@EnableWebMvc` and `@EnableWebSecurity` will construct Spring Security and Spring MVC components in the same context.
+
+Of, if you are using ``ServletListener``s you can do:
+
+[tabs]
+======
+Java::
++
+[source,java,role="primary"]
+----
+public class SecurityInitializer extends
+ AbstractAnnotationConfigDispatcherServletInitializer {
+
+ @Override
+ protected Class>[] getRootConfigClasses() {
+ return null;
+ }
+
+ @Override
+ protected Class>[] getServletConfigClasses() {
+ return new Class[] { RootConfiguration.class,
+ WebMvcConfiguration.class };
+ }
+
+ @Override
+ protected String[] getServletMappings() {
+ return new String[] { "/" };
+ }
+}
+----
+
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+class SecurityInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
+ override fun getRootConfigClasses(): Array>? {
+ return null
+ }
+
+ override fun getServletConfigClasses(): Array> {
+ return arrayOf(
+ RootConfiguration::class.java,
+ WebMvcConfiguration::class.java
+ )
+ }
+
+ override fun getServletMappings(): Array {
+ return arrayOf("/")
+ }
+}
+----
+======
+
+And finally for a `web.xml` file, you configure the `DispatcherServlet` like so:
+
+[source,xml]
+----
+
+ org.springframework.web.context.ContextLoaderListener
+
+
+
+
+ contextConfigLocation
+ /WEB-INF/spring/*.xml
+
+
+
+ spring
+ org.springframework.web.servlet.DispatcherServlet
+
+
+ contextConfigLocation
+
+
+
+
+
+ spring
+ /
+
+----
+
+The following `WebSecurityConfiguration` in placed in the `ApplicationContext` of the `DispatcherServlet`.
+
diff --git a/web/src/main/java/org/springframework/security/web/servlet/util/matcher/MvcRequestMatcher.java b/web/src/main/java/org/springframework/security/web/servlet/util/matcher/MvcRequestMatcher.java
index 9576f0533e..51e36d28a8 100644
--- a/web/src/main/java/org/springframework/security/web/servlet/util/matcher/MvcRequestMatcher.java
+++ b/web/src/main/java/org/springframework/security/web/servlet/util/matcher/MvcRequestMatcher.java
@@ -46,7 +46,9 @@ import org.springframework.web.util.UrlPathHelper;
* @author EddĂș MelĂ©ndez
* @author Evgeniy Cheban
* @since 4.1.1
+ * @deprecated Please use {@link PathPatternRequestMatcher} instead
*/
+@Deprecated(forRemoval = true)
public class MvcRequestMatcher implements RequestMatcher, RequestVariablesExtractor {
private final DefaultMatcher defaultMatcher = new DefaultMatcher();