Skip to content

Commit 05fdcd6

Browse files
committed
Deprecate MvcRequestMatcher
Closes gh-16631
1 parent 6927566 commit 05fdcd6

File tree

3 files changed

+117
-196
lines changed

3 files changed

+117
-196
lines changed

docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ Generally speaking, you can use `requestMatchers(String)` as demonstrated above.
583583

584584
However, if you have authorization rules from multiple servlets, you need to specify those:
585585

586-
.Match by MvcRequestMatcher
586+
.Match by PathPatternRequestMatcher
587587
[tabs]
588588
======
589589
Java::

docs/modules/ROOT/pages/servlet/integrations/mvc.adoc

Lines changed: 114 additions & 195 deletions
Original file line numberDiff line numberDiff line change
@@ -22,231 +22,58 @@ This means that, if you use more advanced options, such as integrating with `Web
2222
====
2323

2424
[[mvc-requestmatcher]]
25-
== MvcRequestMatcher
25+
== PathPatternRequestMatcher
2626

27-
Spring Security provides deep integration with how Spring MVC matches on URLs with `MvcRequestMatcher`.
27+
Spring Security provides deep integration with how Spring MVC matches on URLs with `PathPatternRequestMatcher`.
2828
This is helpful to ensure that your Security rules match the logic used to handle your requests.
2929

30-
To use `MvcRequestMatcher`, you must place the Spring Security Configuration in the same `ApplicationContext` as your `DispatcherServlet`.
31-
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.
32-
33-
For a `web.xml` file, this means that you should place your configuration in the `DispatcherServlet.xml`:
34-
35-
[source,xml]
36-
----
37-
<listener>
38-
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
39-
</listener>
40-
41-
<!-- All Spring Configuration (both MVC and Security) are in /WEB-INF/spring/ -->
42-
<context-param>
43-
<param-name>contextConfigLocation</param-name>
44-
<param-value>/WEB-INF/spring/*.xml</param-value>
45-
</context-param>
46-
47-
<servlet>
48-
<servlet-name>spring</servlet-name>
49-
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
50-
<!-- Load from the ContextLoaderListener -->
51-
<init-param>
52-
<param-name>contextConfigLocation</param-name>
53-
<param-value></param-value>
54-
</init-param>
55-
</servlet>
56-
57-
<servlet-mapping>
58-
<servlet-name>spring</servlet-name>
59-
<url-pattern>/</url-pattern>
60-
</servlet-mapping>
61-
----
62-
63-
The following `WebSecurityConfiguration` in placed in the `ApplicationContext` of the `DispatcherServlet`.
30+
`PathPatternRequestMatcher` must use the same `PathPatternParser` as Spring MVC.
31+
If you are not customizing the `PathPatternParser`, then you can do:
6432

6533
[tabs]
6634
======
6735
Java::
6836
+
6937
[source,java,role="primary"]
7038
----
71-
public class SecurityInitializer extends
72-
AbstractAnnotationConfigDispatcherServletInitializer {
73-
74-
@Override
75-
protected Class<?>[] getRootConfigClasses() {
76-
return null;
77-
}
78-
79-
@Override
80-
protected Class<?>[] getServletConfigClasses() {
81-
return new Class[] { RootConfiguration.class,
82-
WebMvcConfiguration.class };
83-
}
84-
85-
@Override
86-
protected String[] getServletMappings() {
87-
return new String[] { "/" };
88-
}
89-
}
90-
----
91-
92-
Kotlin::
93-
+
94-
[source,kotlin,role="secondary"]
95-
----
96-
class SecurityInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
97-
override fun getRootConfigClasses(): Array<Class<*>>? {
98-
return null
99-
}
100-
101-
override fun getServletConfigClasses(): Array<Class<*>> {
102-
return arrayOf(
103-
RootConfiguration::class.java,
104-
WebMvcConfiguration::class.java
105-
)
106-
}
107-
108-
override fun getServletMappings(): Array<String> {
109-
return arrayOf("/")
110-
}
111-
}
112-
----
113-
======
114-
115-
[NOTE]
116-
====
117-
We always recommend that you provide authorization rules by matching on the `HttpServletRequest` and method security.
118-
119-
Providing authorization rules by matching on `HttpServletRequest` is good, because it happens very early in the code path and helps reduce the https://en.wikipedia.org/wiki/Attack_surface[attack surface].
120-
Method security ensures that, if someone has bypassed the web authorization rules, your application is still secured.
121-
This is known as https://en.wikipedia.org/wiki/Defense_in_depth_(computing)[Defense in Depth]
122-
====
123-
124-
Consider a controller that is mapped as follows:
125-
126-
[tabs]
127-
======
128-
Java::
129-
+
130-
[source,java,role="primary"]
131-
----
132-
@RequestMapping("/admin")
133-
public String admin() {
134-
// ...
39+
@Bean
40+
PathPatternRequestMatcherBuilderFactoryBean usePathPattern() {
41+
return new PathPatternRequestMatcherBuilderFactoryBean();
13542
}
13643
----
13744
13845
Kotlin::
13946
+
14047
[source,kotlin,role="secondary"]
14148
----
142-
@RequestMapping("/admin")
143-
fun admin(): String {
144-
// ...
145-
}
146-
----
147-
======
148-
149-
To restrict access to this controller method to admin users, you can provide authorization rules by matching on the `HttpServletRequest` with the following:
150-
151-
[tabs]
152-
======
153-
Java::
154-
+
155-
[source,java,role="primary"]
156-
----
15749
@Bean
158-
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
159-
http
160-
.authorizeHttpRequests((authorize) -> authorize
161-
.requestMatchers("/admin").hasRole("ADMIN")
162-
);
163-
return http.build();
50+
fun usePathPattern(): PathPatternRequestMatcherBuilderFactoryBean {
51+
return PathPatternRequestMatcherBuilderFactoryBean()
16452
}
16553
----
16654
167-
Kotlin::
55+
Xml::
16856
+
169-
[source,kotlin,role="secondary"]
57+
[source,xml,role="secondary"]
17058
----
171-
@Bean
172-
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
173-
http {
174-
authorizeHttpRequests {
175-
authorize("/admin", hasRole("ADMIN"))
176-
}
177-
}
178-
return http.build()
179-
}
59+
<b:bean class="org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean"/>
18060
----
18161
======
18262

183-
The following listing does the same thing in XML:
184-
185-
[source,xml]
186-
----
187-
<http>
188-
<intercept-url pattern="/admin" access="hasRole('ADMIN')"/>
189-
</http>
190-
----
191-
192-
With either configuration, the `/admin` URL requires the authenticated user to be an admin user.
193-
However, depending on our Spring MVC configuration, the `/admin.html` URL also maps to our `admin()` method.
194-
Additionally, depending on our Spring MVC configuration, the `/admin` URL also maps to our `admin()` method.
63+
and Spring Security will find the appropriate Spring MVC configuration for you.
19564

196-
The problem is that our security rule protects only `/admin`.
197-
We could add additional rules for all the permutations of Spring MVC, but this would be quite verbose and tedious.
65+
If you *are* customizing Spring MVC's `PathPatternParser` instance, you will need to <<security-mvc-same-application-context, configure Spring Security and Spring MVC in the same `ApplicationContext`>>.
19866

199-
Fortunately, when using the `requestMatchers` DSL method, Spring Security automatically creates a `MvcRequestMatcher` if it detects that Spring MVC is available in the classpath.
200-
Therefore, it will protect the same URLs that Spring MVC will match on by using Spring MVC to match on the URL.
201-
202-
One common requirement when using Spring MVC is to specify the servlet path property.
203-
204-
For Java-based Configuration, you can use the `MvcRequestMatcher.Builder` to create multiple `MvcRequestMatcher` instances that share the same servlet path:
205-
206-
[source,java,role="primary"]
207-
----
208-
@Bean
209-
public SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {
210-
MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector).servletPath("/path");
211-
http
212-
.authorizeHttpRequests((authorize) -> authorize
213-
.requestMatchers(mvcMatcherBuilder.pattern("/admin")).hasRole("ADMIN")
214-
.requestMatchers(mvcMatcherBuilder.pattern("/user")).hasRole("USER")
215-
);
216-
return http.build();
217-
}
218-
----
67+
[NOTE]
68+
====
69+
We always recommend that you provide authorization rules by matching on the `HttpServletRequest` and method security.
21970
220-
For Kotlin and XML, this happens when you specify the servlet path for each path like so:
71+
Providing authorization rules by matching on `HttpServletRequest` is good, because it happens very early in the code path and helps reduce the https://en.wikipedia.org/wiki/Attack_surface[attack surface].
72+
Method security ensures that, if someone has bypassed the web authorization rules, your application is still secured.
73+
This is known as https://en.wikipedia.org/wiki/Defense_in_depth_(computing)[Defense in Depth]
74+
====
22175

222-
[tabs]
223-
======
224-
Kotlin::
225-
+
226-
[source,kotlin,role="secondary"]
227-
----
228-
@Bean
229-
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
230-
http {
231-
authorizeHttpRequests {
232-
authorize("/admin/**", "/mvc", hasRole("ADMIN"))
233-
authorize("/user/**", "/mvc", hasRole("USER"))
234-
}
235-
}
236-
return http.build()
237-
}
238-
----
239-
240-
Xml::
241-
+
242-
[source,xml, role="secondary"]
243-
----
244-
<http request-matcher="mvc">
245-
<intercept-url pattern="/admin/**" servlet-path="/mvc" access="hasRole('ADMIN')"/>
246-
<intercept-url pattern="/user/**" servlet-path="/mvc" access="hasRole('USER')"/>
247-
</http>
248-
----
249-
======
76+
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`.
25077

25178
[[mvc-authentication-principal]]
25279
== @AuthenticationPrincipal
@@ -766,3 +593,95 @@ class CsrfController {
766593

767594
It is important to keep the `CsrfToken` a secret from other domains.
768595
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.
596+
597+
[[security-mvc-same-application-context]]
598+
== Configuring Spring MVC and Spring Security in the Same Application Context
599+
600+
If you are using Boot, Spring MVC and Spring Security are in the same application context by default.
601+
602+
Otherwise, for Java Config, including both `@EnableWebMvc` and `@EnableWebSecurity` will construct Spring Security and Spring MVC components in the same context.
603+
604+
Of, if you are using ``ServletListener``s you can do:
605+
606+
[tabs]
607+
======
608+
Java::
609+
+
610+
[source,java,role="primary"]
611+
----
612+
public class SecurityInitializer extends
613+
AbstractAnnotationConfigDispatcherServletInitializer {
614+
615+
@Override
616+
protected Class<?>[] getRootConfigClasses() {
617+
return null;
618+
}
619+
620+
@Override
621+
protected Class<?>[] getServletConfigClasses() {
622+
return new Class[] { RootConfiguration.class,
623+
WebMvcConfiguration.class };
624+
}
625+
626+
@Override
627+
protected String[] getServletMappings() {
628+
return new String[] { "/" };
629+
}
630+
}
631+
----
632+
633+
Kotlin::
634+
+
635+
[source,kotlin,role="secondary"]
636+
----
637+
class SecurityInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
638+
override fun getRootConfigClasses(): Array<Class<*>>? {
639+
return null
640+
}
641+
642+
override fun getServletConfigClasses(): Array<Class<*>> {
643+
return arrayOf(
644+
RootConfiguration::class.java,
645+
WebMvcConfiguration::class.java
646+
)
647+
}
648+
649+
override fun getServletMappings(): Array<String> {
650+
return arrayOf("/")
651+
}
652+
}
653+
----
654+
======
655+
656+
And finally for a `web.xml` file, you configure the `DispatcherServlet` like so:
657+
658+
[source,xml]
659+
----
660+
<listener>
661+
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
662+
</listener>
663+
664+
<!-- All Spring Configuration (both MVC and Security) are in /WEB-INF/spring/ -->
665+
<context-param>
666+
<param-name>contextConfigLocation</param-name>
667+
<param-value>/WEB-INF/spring/*.xml</param-value>
668+
</context-param>
669+
670+
<servlet>
671+
<servlet-name>spring</servlet-name>
672+
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
673+
<!-- Load from the ContextLoaderListener -->
674+
<init-param>
675+
<param-name>contextConfigLocation</param-name>
676+
<param-value></param-value>
677+
</init-param>
678+
</servlet>
679+
680+
<servlet-mapping>
681+
<servlet-name>spring</servlet-name>
682+
<url-pattern>/</url-pattern>
683+
</servlet-mapping>
684+
----
685+
686+
The following `WebSecurityConfiguration` in placed in the `ApplicationContext` of the `DispatcherServlet`.
687+

web/src/main/java/org/springframework/security/web/servlet/util/matcher/MvcRequestMatcher.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@
4646
* @author Eddú Meléndez
4747
* @author Evgeniy Cheban
4848
* @since 4.1.1
49+
* @deprecated Please use {@link PathPatternRequestMatcher} instead
4950
*/
51+
@Deprecated(forRemoval = true)
5052
public class MvcRequestMatcher implements RequestMatcher, RequestVariablesExtractor {
5153

5254
private final DefaultMatcher defaultMatcher = new DefaultMatcher();

0 commit comments

Comments
 (0)