From 693bfb66b2afbff33a072dbffa0f53fbbb0a6a0a Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Fri, 28 Oct 2022 10:51:49 -0300 Subject: [PATCH] Document how to use the new requestMatchers and securityMatchers Closes gh-12100 --- docs/modules/ROOT/pages/migration.adoc | 417 +++++++++++++++++++++++++ 1 file changed, 417 insertions(+) diff --git a/docs/modules/ROOT/pages/migration.adoc b/docs/modules/ROOT/pages/migration.adoc index 91896cc496..865f58f1a3 100644 --- a/docs/modules/ROOT/pages/migration.adoc +++ b/docs/modules/ROOT/pages/migration.adoc @@ -2188,6 +2188,423 @@ versionFour.setAssertionValidator(validator) ---- ==== +[[use-new-requestmatchers]] +=== Use the new `requestMatchers` methods + +In Spring Security 5.8, the {security-api-url}org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.html#antMatchers(java.lang.String...)[`antMatchers`], {security-api-url}org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.html#mvcMatchers(java.lang.String...)`mvcMatchers`, and {security-api-url}org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.html#regexMatchers(java.lang.String...)[`regexMatchers`] methods were deprecated in favor of new xref::servlet/authorization/authorize-http-requests.adoc#_request_matchers[`requestMatchers` methods]. + +The new `requestMatchers` methods were added xref::servlet/authorization/authorize-http-requests.adoc[to `authorizeHttpRequests`], `authorizeRequests`, CSRF configuration, `WebSecurityCustomizer` and any other places that had the specialized `RequestMatcher` methods. +The deprecated methods are removed in Spring Security 6. + +These new methods have more secure defaults since they choose the most appropriate `RequestMatcher` implementation for your application. +In summary, the new methods choose the `MvcRequestMatcher` implementation if your application has Spring MVC in the classpath, falling back to the `AntPathRequestMatcher` implementation if Spring MVC is not present (aligning the behavior with the Kotlin equivalent methods). + +To start using the new methods, you can replace the deprecated methods with the new ones. For example, the following application configuration: + +==== +.Java +[source,java,role="primary"] +---- +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .authorizeHttpRequests((authz) -> authz + .antMatchers("/api/admin/**").hasRole("ADMIN") + .antMatchers("/api/user/**").hasRole("USER") + .anyRequest().authenticated() + ); + return http.build(); + } + +} +---- +==== + +can be changed to: + +==== +.Java +[source,java,role="primary"] +---- +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .authorizeHttpRequests((authz) -> authz + .requestMatchers("/api/admin/**").hasRole("ADMIN") + .requestMatchers("/api/user/**").hasRole("USER") + .anyRequest().authenticated() + ); + return http.build(); + } + +} +---- +==== + +If you have Spring MVC in the classpath and are using the `mvcMatchers` methods, you can replace it with the new methods and Spring Security will choose the `MvcRequestMatcher` implementation for you. +The following configuration: + +==== +.Java +[source,java,role="primary"] +---- +@Configuration +@EnableWebSecurity +@EnableWebMvc +public class SecurityConfig { + + @Bean + SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .authorizeHttpRequests((authz) -> authz + .mvcMatchers("/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + ); + return http.build(); + } + +} +---- +==== + +is equivalent to: + +==== +.Java +[source,java,role="primary"] +---- +@Configuration +@EnableWebSecurity +@EnableWebMvc +public class SecurityConfig { + + @Bean + SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .authorizeHttpRequests((authz) -> authz + .requestMatchers("/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + ); + return http.build(); + } + +} +---- +==== + +If you are customizing the `servletPath` property of the `MvcRequestMatcher`, you can now use the `MvcRequestMatcher.Builder` to create `MvcRequestMatcher` instances that share the same servlet path: + +==== +.Java +[source,java,role="primary"] +---- +@Configuration +@EnableWebSecurity +@EnableWebMvc +public class SecurityConfig { + + @Bean + SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .authorizeHttpRequests((authz) -> authz + .mvcMatchers("/admin").servletPath("/path").hasRole("ADMIN") + .mvcMatchers("/user").servletPath("/path").hasRole("USER") + .anyRequest().authenticated() + ); + return http.build(); + } + +} +---- +==== + +The code above can be rewritten using the `MvcRequestMatcher.Builder` and the `requestMatchers` method: + +==== +.Java +[source,java,role="primary"] +---- +@Configuration +@EnableWebSecurity +@EnableWebMvc +public class SecurityConfig { + + @Bean + SecurityFilterChain securityFilterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception { + MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector).servletPath("/path"); + http + .authorizeHttpRequests((authz) -> authz + .requestMatchers(mvcMatcherBuilder.pattern("/admin")).hasRole("ADMIN") + .requestMatchers(mvcMatcherBuilder.pattern("/user")).hasRole("USER") + .anyRequest().authenticated() + ); + return http.build(); + } + +} +---- +==== + +If you are having problem with the new `requestMatchers` methods, you can always switch back to the `RequestMatcher` implementation that you were using. +For example, if you still want to use `AntPathRequestMatcher` and `RegexRequestMatcher` implementations, you can use the `requestMatchers` method that accepts a `RequestMatcher` instance: + +==== +.Java +[source,java,role="primary"] +---- +import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher; +import static org.springframework.security.web.util.matcher.RegexRequestMatcher.regexMatcher; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + + @Bean + SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .authorizeHttpRequests((authz) -> authz + .requestMatchers(antMatcher("/user/**")).hasRole("USER") + .requestMatchers(antMatcher(HttpMethod.POST, "/user/**")).hasRole("ADMIN") + .requestMatchers(regexMatcher(".*\\?x=y")).hasRole("SPECIAL") // matches /any/path?x=y + .anyRequest().authenticated() + ); + return http.build(); + } + +} +---- +==== + +Note that the above sample uses static factory methods from {security-api-url}org/springframework/security/web/util/matcher/AntPathRequestMatcher.html[`AntPathRequestMatcher`] and {security-api-url}org/springframework/security/web/util/matcher/RegexRequestMatcher.html[`RegexRequestMatcher`] to improve readability. + +If you are using the `WebSecurityCustomizer` interface, you can replace the deprecated `antMatchers` methods: + +==== +.Java +[source,java,role="primary"] +---- +@Bean +public WebSecurityCustomizer webSecurityCustomizer() { + return (web) -> web.ignoring().antMatchers("/ignore1", "/ignore2"); +} +---- +==== + +with their `requestMatchers` counterparts: + +==== +.Java +[source,java,role="primary"] +---- +@Bean +public WebSecurityCustomizer webSecurityCustomizer() { + return (web) -> web.ignoring().requestMatchers("/ignore1", "/ignore2"); +} +---- +==== + +The same way, if you are customizing the CSRF configuration to ignore some paths, you can replace the deprecated methods with the `requestMatchers` methods: + +==== +.Java +[source,java,role="primary"] +---- +@Bean +public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .csrf((csrf) -> csrf + .ignoringAntMatchers("/no-csrf") + ); + return http.build(); +} +---- +==== + +can be changed to: + +==== +.Java +[source,java,role="primary"] +---- +@Bean +public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .csrf((csrf) -> csrf + .ignoringRequestMatchers("/no-csrf") + ); + return http.build(); +} +---- +==== + +[[use-new-security-matchers]] +=== Use the new `securityMatchers` methods + +In Spring Security 5.8, the `antMatchers`, `mvcMatchers` and `requestMatchers` methods from `HttpSecurity` were deprecated in favor of new `securityMatchers` methods. + +Note that these methods are not the same from `authorizeHttpRequests` methods <> in favor of the `requestMatchers` methods. +However, the `securityMatchers` methods are similar to the `requestMatchers` methods in the sense that they will choose the most appropriate `RequestMatcher` implementation for your application. +In summary, the new methods choose the `MvcRequestMatcher` implementation if your application has Spring MVC in the classpath, falling back to the `AntPathRequestMatcher` implementation if Spring MVC is not present (aligning the behavior with the Kotlin equivalent methods). +Another reason for adding the `securityMatchers` methods is to avoid confusion with the `requestMatchers` methods from `authorizeHttpRequests`. + +The following configuration: + +==== +.Java +[source,java,role="primary"] +---- +@Bean +public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .antMatcher("/api/**", "/app/**") + .authorizeHttpRequests((authz) -> authz + .requestMatchers("/api/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + ); + return http.build(); +} +---- +==== + +can be rewritten using the `securityMatchers` methods: + +==== +.Java +[source,java,role="primary"] +---- +@Bean +public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .securityMatcher("/api/**", "/app/**") + .authorizeHttpRequests((authz) -> authz + .requestMatchers("/api/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + ); + return http.build(); +} +---- +==== + +If you are using a custom `RequestMatcher` in your `HttpSecurity` configuration: + +==== +.Java +[source,java,role="primary"] +---- +@Bean +public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .requestMatcher(new MyCustomRequestMatcher()) + .authorizeHttpRequests((authz) -> authz + .requestMatchers("/api/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + ); + return http.build(); +} + +public class MyCustomRequestMatcher implements RequestMatcher { + // ... +} +---- +==== + +you can do the same using `securityMatcher`: + +==== +.Java +[source,java,role="primary"] +---- +@Bean +public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .securityMatcher(new MyCustomRequestMatcher()) + .authorizeHttpRequests((authz) -> authz + .requestMatchers("/api/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + ); + return http.build(); +} + +public class MyCustomRequestMatcher implements RequestMatcher { + // ... +} +---- +==== + +If you are combining multiple `RequestMatcher` implementations in your `HttpSecurity` configuration: + +==== +.Java +[source,java,role="primary"] +---- +@Bean +public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .requestMatchers((matchers) -> matchers + .antMatchers("/api/**", "/app/**") + .mvcMatchers("/admin/**") + .requestMatchers(new MyCustomRequestMatcher()) + ) + .authorizeHttpRequests((authz) -> authz + .requestMatchers("/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + ); + return http.build(); +} +---- +==== + +you can change it by using `securityMatchers`: + +==== +.Java +[source,java,role="primary"] +---- +@Bean +public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .securityMatchers((matchers) -> matchers + .requestMatchers("/api/**", "/app/**", "/admin/**") + .requestMatchers(new MyCustomRequestMatcher()) + ) + .authorizeHttpRequests((authz) -> authz + .requestMatchers("/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + ); + return http.build(); +} +---- +==== + +If you are having problems with the `securityMatchers` methods choosing the `RequestMatcher` implementation for you, you can always choose the `RequestMatcher` implementation yourself: + +==== +.Java +[source,java,role="primary"] +---- +import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher; + +@Bean +public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .securityMatcher(antMatcher("/api/**"), antMatcher("/app/**")) + .authorizeHttpRequests((authz) -> authz + .requestMatchers(antMatcher("/api/admin/**")).hasRole("ADMIN") + .anyRequest().authenticated() + ); + return http.build(); +} +---- +==== + == Reactive === Use `AuthorizationManager` for Method Security