浏览代码

Merge branch '5.8.x'

Closes gh-11347 in 6.0.x
Closes gh-11945
Marcus Da Coregio 2 年之前
父节点
当前提交
ad2abd39dc
共有 18 个文件被更改,包括 1391 次插入38 次删除
  1. 102 1
      config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java
  2. 336 3
      config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java
  3. 14 4
      config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurity.java
  4. 2 2
      config/src/main/java/org/springframework/security/config/annotation/web/configuration/EnableWebSecurity.java
  5. 10 0
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java
  6. 40 0
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurer.java
  7. 13 1
      config/src/main/kotlin/org/springframework/security/config/annotation/web/CsrfDsl.kt
  8. 105 1
      config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryTests.java
  9. 54 0
      config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerIgnoringRequestMatchersTests.java
  10. 567 0
      config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpSecuritySecurityMatchersTests.java
  11. 33 0
      config/src/test/kotlin/org/springframework/security/config/annotation/web/CsrfDslTests.kt
  12. 6 5
      docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc
  13. 2 2
      docs/modules/ROOT/pages/servlet/authorization/expression-based.adoc
  14. 20 14
      docs/modules/ROOT/pages/servlet/integrations/mvc.adoc
  15. 2 2
      docs/modules/ROOT/pages/servlet/integrations/websocket.adoc
  16. 1 1
      docs/modules/ROOT/pages/whats-new.adoc
  17. 56 1
      web/src/main/java/org/springframework/security/web/servlet/util/matcher/MvcRequestMatcher.java
  18. 28 1
      web/src/test/java/org/springframework/security/web/servlet/util/matcher/MvcRequestMatcherTests.java

+ 102 - 1
config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -35,6 +35,7 @@ import org.springframework.security.web.util.matcher.DispatcherTypeRequestMatche
 import org.springframework.security.web.util.matcher.RegexRequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcher;
 import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
 import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
 
 /**
@@ -50,12 +51,21 @@ public abstract class AbstractRequestMatcherRegistry<C> {
 
 	private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
 
+	private static final String HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector";
+
+	private static final boolean mvcPresent;
+
 	private static final RequestMatcher ANY_REQUEST = AnyRequestMatcher.INSTANCE;
 
 	private ApplicationContext context;
 
 	private boolean anyRequestConfigured = false;
 
+	static {
+		mvcPresent = ClassUtils.isPresent(HANDLER_MAPPING_INTROSPECTOR,
+				AbstractRequestMatcherRegistry.class.getClassLoader());
+	}
+
 	protected final void setApplicationContext(ApplicationContext context) {
 		this.context = context;
 	}
@@ -85,7 +95,9 @@ public abstract class AbstractRequestMatcherRegistry<C> {
 	 * instances.
 	 * @param method the {@link HttpMethod} to use for any {@link HttpMethod}.
 	 * @return the object that is chained after creating the {@link RequestMatcher}
+	 * @deprecated use {@link #requestMatchers(HttpMethod)} instead
 	 */
+	@Deprecated
 	public C antMatchers(HttpMethod method) {
 		return antMatchers(method, "/**");
 	}
@@ -99,7 +111,9 @@ public abstract class AbstractRequestMatcherRegistry<C> {
 	 * @param antPatterns the ant patterns to create. If {@code null} or empty, then
 	 * matches on nothing.
 	 * @return the object that is chained after creating the {@link RequestMatcher}
+	 * @deprecated use {@link #requestMatchers(HttpMethod, String...)} instead
 	 */
+	@Deprecated
 	public C antMatchers(HttpMethod method, String... antPatterns) {
 		Assert.state(!this.anyRequestConfigured, "Can't configure antMatchers after anyRequest");
 		return chainRequestMatchers(RequestMatchers.antMatchers(method, antPatterns));
@@ -112,7 +126,9 @@ public abstract class AbstractRequestMatcherRegistry<C> {
 	 * @param antPatterns the ant patterns to create
 	 * {@link org.springframework.security.web.util.matcher.AntPathRequestMatcher} from
 	 * @return the object that is chained after creating the {@link RequestMatcher}
+	 * @deprecated use {@link #requestMatchers(String...)} instead
 	 */
+	@Deprecated
 	public C antMatchers(String... antPatterns) {
 		Assert.state(!this.anyRequestConfigured, "Can't configure antMatchers after anyRequest");
 		return chainRequestMatchers(RequestMatchers.antMatchers(antPatterns));
@@ -132,7 +148,9 @@ public abstract class AbstractRequestMatcherRegistry<C> {
 	 * @param mvcPatterns the patterns to match on. The rules for matching are defined by
 	 * Spring MVC
 	 * @return the object that is chained after creating the {@link RequestMatcher}.
+	 * @deprecated use {@link #requestMatchers(String...)} instead
 	 */
+	@Deprecated
 	public abstract C mvcMatchers(String... mvcPatterns);
 
 	/**
@@ -150,7 +168,9 @@ public abstract class AbstractRequestMatcherRegistry<C> {
 	 * @param mvcPatterns the patterns to match on. The rules for matching are defined by
 	 * Spring MVC
 	 * @return the object that is chained after creating the {@link RequestMatcher}.
+	 * @deprecated use {@link #requestMatchers(HttpMethod, String...)} instead
 	 */
+	@Deprecated
 	public abstract C mvcMatchers(HttpMethod method, String... mvcPatterns);
 
 	/**
@@ -190,7 +210,10 @@ public abstract class AbstractRequestMatcherRegistry<C> {
 	 * @param regexPatterns the regular expressions to create
 	 * {@link org.springframework.security.web.util.matcher.RegexRequestMatcher} from
 	 * @return the object that is chained after creating the {@link RequestMatcher}
+	 * @deprecated use {@link #requestMatchers(RequestMatcher...)} with a
+	 * {@link RegexRequestMatcher} instead
 	 */
+	@Deprecated
 	public C regexMatchers(HttpMethod method, String... regexPatterns) {
 		Assert.state(!this.anyRequestConfigured, "Can't configure regexMatchers after anyRequest");
 		return chainRequestMatchers(RequestMatchers.regexMatchers(method, regexPatterns));
@@ -203,7 +226,10 @@ public abstract class AbstractRequestMatcherRegistry<C> {
 	 * @param regexPatterns the regular expressions to create
 	 * {@link org.springframework.security.web.util.matcher.RegexRequestMatcher} from
 	 * @return the object that is chained after creating the {@link RequestMatcher}
+	 * @deprecated use {@link #requestMatchers(RequestMatcher...)} with a
+	 * {@link RegexRequestMatcher} instead
 	 */
+	@Deprecated
 	public C regexMatchers(String... regexPatterns) {
 		Assert.state(!this.anyRequestConfigured, "Can't configure regexMatchers after anyRequest");
 		return chainRequestMatchers(RequestMatchers.regexMatchers(regexPatterns));
@@ -250,6 +276,81 @@ public abstract class AbstractRequestMatcherRegistry<C> {
 		return chainRequestMatchers(Arrays.asList(requestMatchers));
 	}
 
+	/**
+	 * <p>
+	 * If the {@link HandlerMappingIntrospector} is available in the classpath, maps to an
+	 * {@link MvcRequestMatcher} that also specifies a specific {@link HttpMethod} to
+	 * match on. This matcher will use the same rules that Spring MVC uses for matching.
+	 * For example, often times a mapping of the path "/path" will match on "/path",
+	 * "/path/", "/path.html", etc. If the {@link HandlerMappingIntrospector} is not
+	 * available, maps to an {@link AntPathRequestMatcher}.
+	 * </p>
+	 * <p>
+	 * If a specific {@link RequestMatcher} must be specified, use
+	 * {@link #requestMatchers(RequestMatcher...)} instead
+	 * </p>
+	 * @param method the {@link HttpMethod} to use or {@code null} for any
+	 * {@link HttpMethod}.
+	 * @param patterns the patterns to match on. The rules for matching are defined by
+	 * Spring MVC if {@link MvcRequestMatcher} is used
+	 * @return the object that is chained after creating the {@link RequestMatcher}.
+	 * @since 5.8
+	 */
+	public C requestMatchers(HttpMethod method, String... patterns) {
+		List<RequestMatcher> matchers = new ArrayList<>();
+		if (mvcPresent) {
+			matchers.addAll(createMvcMatchers(method, patterns));
+		}
+		else {
+			matchers.addAll(RequestMatchers.antMatchers(method, patterns));
+		}
+		return requestMatchers(matchers.toArray(new RequestMatcher[0]));
+	}
+
+	/**
+	 * <p>
+	 * If the {@link HandlerMappingIntrospector} is available in the classpath, maps to an
+	 * {@link MvcRequestMatcher} that does not care which {@link HttpMethod} is used. This
+	 * matcher will use the same rules that Spring MVC uses for matching. For example,
+	 * often times a mapping of the path "/path" will match on "/path", "/path/",
+	 * "/path.html", etc. If the {@link HandlerMappingIntrospector} is not available, maps
+	 * to an {@link AntPathRequestMatcher}.
+	 * </p>
+	 * <p>
+	 * If a specific {@link RequestMatcher} must be specified, use
+	 * {@link #requestMatchers(RequestMatcher...)} instead
+	 * </p>
+	 * @param patterns the patterns to match on. The rules for matching are defined by
+	 * Spring MVC if {@link MvcRequestMatcher} is used
+	 * @return the object that is chained after creating the {@link RequestMatcher}.
+	 * @since 5.8
+	 */
+	public C requestMatchers(String... patterns) {
+		return requestMatchers(null, patterns);
+	}
+
+	/**
+	 * <p>
+	 * If the {@link HandlerMappingIntrospector} is available in the classpath, maps to an
+	 * {@link MvcRequestMatcher} that matches on a specific {@link HttpMethod}. This
+	 * matcher will use the same rules that Spring MVC uses for matching. For example,
+	 * often times a mapping of the path "/path" will match on "/path", "/path/",
+	 * "/path.html", etc. If the {@link HandlerMappingIntrospector} is not available, maps
+	 * to an {@link AntPathRequestMatcher}.
+	 * </p>
+	 * <p>
+	 * If a specific {@link RequestMatcher} must be specified, use
+	 * {@link #requestMatchers(RequestMatcher...)} instead
+	 * </p>
+	 * @param method the {@link HttpMethod} to use or {@code null} for any
+	 * {@link HttpMethod}.
+	 * @return the object that is chained after creating the {@link RequestMatcher}.
+	 * @since 5.8
+	 */
+	public C requestMatchers(HttpMethod method) {
+		return requestMatchers(method, "/**");
+	}
+
 	/**
 	 * Subclasses should implement this method for returning the object that is chained to
 	 * the creation of the {@link RequestMatcher} instances.

+ 336 - 3
config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java

@@ -28,6 +28,7 @@ import jakarta.servlet.ServletRequest;
 import jakarta.servlet.ServletResponse;
 import jakarta.servlet.http.HttpServletRequest;
 
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
 import org.springframework.context.ApplicationContext;
 import org.springframework.core.OrderComparator;
 import org.springframework.core.Ordered;
@@ -91,6 +92,7 @@ import org.springframework.security.web.util.matcher.OrRequestMatcher;
 import org.springframework.security.web.util.matcher.RegexRequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcher;
 import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
 import org.springframework.web.cors.CorsConfiguration;
 import org.springframework.web.filter.CorsFilter;
 import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
@@ -116,7 +118,7 @@ import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
  *
  * 	&#064;Bean
  * 	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- * 		http.authorizeRequests().antMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;).and().formLogin();
+ * 		http.authorizeHttpRequests().requestMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;).and().formLogin();
  * 		return http.build();
  * 	}
  *
@@ -140,6 +142,12 @@ import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
 public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
 		implements SecurityBuilder<DefaultSecurityFilterChain>, HttpSecurityBuilder<HttpSecurity> {
 
+	private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
+
+	private static final String HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector";
+
+	private static final boolean mvcPresent;
+
 	private final RequestMatcherConfigurer requestMatcherConfigurer;
 
 	private List<OrderedFilter> filters = new ArrayList<>();
@@ -150,6 +158,10 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
 
 	private AuthenticationManager authenticationManager;
 
+	static {
+		mvcPresent = ClassUtils.isPresent(HANDLER_MAPPING_INTROSPECTOR, HttpSecurity.class.getClassLoader());
+	}
+
 	/**
 	 * Creates a new instance
 	 * @param objectPostProcessor the {@link ObjectPostProcessor} that should be used
@@ -3193,7 +3205,9 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
 	 * }
 	 * </pre>
 	 * @return the {@link RequestMatcherConfigurer} for further customizations
+	 * @deprecated use {@link #securityMatchers()} instead
 	 */
+	@Deprecated
 	public RequestMatcherConfigurer requestMatchers() {
 		return this.requestMatcherConfigurer;
 	}
@@ -3325,7 +3339,9 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
 	 * @param requestMatcherCustomizer the {@link Customizer} to provide more options for
 	 * the {@link RequestMatcherConfigurer}
 	 * @return the {@link HttpSecurity} for further customizations
+	 * @deprecated use {@link #securityMatchers(Customizer)} instead
 	 */
+	@Deprecated
 	public HttpSecurity requestMatchers(Customizer<RequestMatcherConfigurer> requestMatcherCustomizer) {
 		requestMatcherCustomizer.customize(this.requestMatcherConfigurer);
 		return HttpSecurity.this;
@@ -3345,15 +3361,318 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
 	 * @param requestMatcher the {@link RequestMatcher} to use (i.e. new
 	 * AntPathRequestMatcher("/admin/**","GET") )
 	 * @return the {@link HttpSecurity} for further customizations
+	 * @deprecated use {@link #securityMatcher(RequestMatcher)} instead
 	 * @see #requestMatchers()
 	 * @see #antMatcher(String)
 	 * @see #regexMatcher(String)
 	 */
+	@Deprecated
 	public HttpSecurity requestMatcher(RequestMatcher requestMatcher) {
 		this.requestMatcher = requestMatcher;
 		return this;
 	}
 
+	/**
+	 * Allows specifying which {@link HttpServletRequest} instances this
+	 * {@link HttpSecurity} will be invoked on. This method allows for easily invoking the
+	 * {@link HttpSecurity} for multiple different {@link RequestMatcher} instances. If
+	 * only a single {@link RequestMatcher} is necessary consider using
+	 * {@link #securityMatcher(String)}, or {@link #securityMatcher(RequestMatcher)}.
+	 *
+	 * <p>
+	 * Invoking {@link #securityMatchers()} will not override previous invocations of
+	 * {@link #securityMatchers()}}, {@link #securityMatchers(Customizer)}
+	 * {@link #securityMatcher(String)} and {@link #securityMatcher(RequestMatcher)}
+	 * </p>
+	 *
+	 * <h3>Example Configurations</h3>
+	 *
+	 * The following configuration enables the {@link HttpSecurity} for URLs that begin
+	 * with "/api/" or "/oauth/".
+	 *
+	 * <pre>
+	 * &#064;Configuration
+	 * &#064;EnableWebSecurity
+	 * public class RequestMatchersSecurityConfig {
+	 *
+	 * 	&#064;Bean
+	 * 	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+	 * 		http
+	 * 			.securityMatchers((matchers) -&gt; matchers
+	 * 				.requestMatchers(&quot;/api/**&quot;, &quot;/oauth/**&quot;)
+	 * 			)
+	 * 			.authorizeHttpRequests((authorize) -&gt; authorize
+	 * 				anyRequest().hasRole(&quot;USER&quot;)
+	 * 			)
+	 * 			.httpBasic(withDefaults());
+	 * 		return http.build();
+	 * 	}
+	 *
+	 * 	&#064;Bean
+	 * 	public UserDetailsService userDetailsService() {
+	 * 		UserDetails user = User.withDefaultPasswordEncoder()
+	 * 			.username(&quot;user&quot;)
+	 * 			.password(&quot;password&quot;)
+	 * 			.roles(&quot;USER&quot;)
+	 * 			.build();
+	 * 		return new InMemoryUserDetailsManager(user);
+	 * 	}
+	 * }
+	 * </pre>
+	 *
+	 * The configuration below is the same as the previous configuration.
+	 *
+	 * <pre>
+	 * &#064;Configuration
+	 * &#064;EnableWebSecurity
+	 * public class RequestMatchersSecurityConfig {
+	 *
+	 * 	&#064;Bean
+	 * 	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+	 * 		http
+	 * 			.securityMatchers((matchers) -&gt; matchers
+	 * 				.requestMatchers(&quot;/api/**&quot;)
+	 * 				.requestMatchers(&quot;/oauth/**&quot;)
+	 * 			)
+	 * 			.authorizeHttpRequests((authorize) -&gt; authorize
+	 * 				anyRequest().hasRole(&quot;USER&quot;)
+	 * 			)
+	 * 			.httpBasic(withDefaults());
+	 * 		return http.build();
+	 * 	}
+	 *
+	 * 	&#064;Bean
+	 * 	public UserDetailsService userDetailsService() {
+	 * 		UserDetails user = User.withDefaultPasswordEncoder()
+	 * 			.username(&quot;user&quot;)
+	 * 			.password(&quot;password&quot;)
+	 * 			.roles(&quot;USER&quot;)
+	 * 			.build();
+	 * 		return new InMemoryUserDetailsManager(user);
+	 * 	}
+	 * }
+	 * </pre>
+	 *
+	 * The configuration below is also the same as the above configuration.
+	 *
+	 * <pre>
+	 * &#064;Configuration
+	 * &#064;EnableWebSecurity
+	 * public class RequestMatchersSecurityConfig {
+	 *
+	 * 	&#064;Bean
+	 * 	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+	 * 		http
+	 * 			.securityMatchers((matchers) -&gt; matchers
+	 * 				.requestMatchers(&quot;/api/**&quot;)
+	 * 			)
+	 *			.securityMatchers((matchers) -&gt; matchers
+	 *				.requestMatchers(&quot;/oauth/**&quot;)
+	 * 			)
+	 * 			.authorizeHttpRequests((authorize) -&gt; authorize
+	 * 				anyRequest().hasRole(&quot;USER&quot;)
+	 * 			)
+	 * 			.httpBasic(withDefaults());
+	 * 		return http.build();
+	 * 	}
+	 *
+	 * 	&#064;Bean
+	 * 	public UserDetailsService userDetailsService() {
+	 * 		UserDetails user = User.withDefaultPasswordEncoder()
+	 * 			.username(&quot;user&quot;)
+	 * 			.password(&quot;password&quot;)
+	 * 			.roles(&quot;USER&quot;)
+	 * 			.build();
+	 * 		return new InMemoryUserDetailsManager(user);
+	 * 	}
+	 * }
+	 * </pre>
+	 * @return the {@link RequestMatcherConfigurer} for further customizations
+	 */
+	public RequestMatcherConfigurer securityMatchers() {
+		return this.requestMatcherConfigurer;
+	}
+
+	/**
+	 * Allows specifying which {@link HttpServletRequest} instances this
+	 * {@link HttpSecurity} will be invoked on. This method allows for easily invoking the
+	 * {@link HttpSecurity} for multiple different {@link RequestMatcher} instances. If
+	 * only a single {@link RequestMatcher} is necessary consider using
+	 * {@link #securityMatcher(String)}, or {@link #securityMatcher(RequestMatcher)}.
+	 *
+	 * <p>
+	 * Invoking {@link #securityMatchers(Customizer)} will not override previous
+	 * invocations of {@link #securityMatchers()}}, {@link #securityMatchers(Customizer)}
+	 * {@link #securityMatcher(String)} and {@link #securityMatcher(RequestMatcher)}
+	 * </p>
+	 *
+	 * <h3>Example Configurations</h3>
+	 *
+	 * The following configuration enables the {@link HttpSecurity} for URLs that begin
+	 * with "/api/" or "/oauth/".
+	 *
+	 * <pre>
+	 * &#064;Configuration
+	 * &#064;EnableWebSecurity
+	 * public class RequestMatchersSecurityConfig {
+	 *
+	 * 	&#064;Bean
+	 * 	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+	 * 		http
+	 * 			.securityMatchers((matchers) -&gt; matchers
+	 * 				.requestMatchers(&quot;/api/**&quot;, &quot;/oauth/**&quot;)
+	 * 			)
+	 * 			.authorizeHttpRequests((authorize) -&gt; authorize
+	 * 				.anyRequest().hasRole(&quot;USER&quot;)
+	 * 			)
+	 * 			.httpBasic(withDefaults());
+	 * 		return http.build();
+	 * 	}
+	 *
+	 * 	&#064;Bean
+	 * 	public UserDetailsService userDetailsService() {
+	 * 		UserDetails user = User.withDefaultPasswordEncoder()
+	 * 			.username(&quot;user&quot;)
+	 * 			.password(&quot;password&quot;)
+	 * 			.roles(&quot;USER&quot;)
+	 * 			.build();
+	 * 		return new InMemoryUserDetailsManager(user);
+	 * 	}
+	 * }
+	 * </pre>
+	 *
+	 * The configuration below is the same as the previous configuration.
+	 *
+	 * <pre>
+	 * &#064;Configuration
+	 * &#064;EnableWebSecurity
+	 * public class RequestMatchersSecurityConfig {
+	 *
+	 * 	&#064;Bean
+	 * 	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+	 * 		http
+	 * 			.securityMatchers((matchers) -&gt; matchers
+	 * 				.requestMatchers(&quot;/api/**&quot;)
+	 * 				.requestMatchers(&quot;/oauth/**&quot;)
+	 * 			)
+	 * 			.authorizeHttpRequests((authorize) -&gt; authorize
+	 * 				.anyRequest().hasRole(&quot;USER&quot;)
+	 * 			)
+	 * 			.httpBasic(withDefaults());
+	 * 		return http.build();
+	 * 	}
+	 *
+	 * 	&#064;Bean
+	 * 	public UserDetailsService userDetailsService() {
+	 * 		UserDetails user = User.withDefaultPasswordEncoder()
+	 * 			.username(&quot;user&quot;)
+	 * 			.password(&quot;password&quot;)
+	 * 			.roles(&quot;USER&quot;)
+	 * 			.build();
+	 * 		return new InMemoryUserDetailsManager(user);
+	 * 	}
+	 * }
+	 * </pre>
+	 *
+	 * The configuration below is also the same as the above configuration.
+	 *
+	 * <pre>
+	 * &#064;Configuration
+	 * &#064;EnableWebSecurity
+	 * public class RequestMatchersSecurityConfig {
+	 *
+	 * 	&#064;Bean
+	 * 	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+	 * 		http
+	 * 			.securityMatchers((matchers) -&gt; matchers
+	 * 				.requestMatchers(&quot;/api/**&quot;)
+	 * 			)
+	 *			.securityMatchers((matchers) -&gt; matchers
+	 *				.requestMatchers(&quot;/oauth/**&quot;)
+	 * 			)
+	 * 			.authorizeHttpRequests((authorize) -&gt; authorize
+	 * 				.anyRequest().hasRole(&quot;USER&quot;)
+	 * 			)
+	 * 			.httpBasic(withDefaults());
+	 * 		return http.build();
+	 * 	}
+	 *
+	 * 	&#064;Bean
+	 * 	public UserDetailsService userDetailsService() {
+	 * 		UserDetails user = User.withDefaultPasswordEncoder()
+	 * 			.username(&quot;user&quot;)
+	 * 			.password(&quot;password&quot;)
+	 * 			.roles(&quot;USER&quot;)
+	 * 			.build();
+	 * 		return new InMemoryUserDetailsManager(user);
+	 * 	}
+	 * }
+	 * </pre>
+	 * @param requestMatcherCustomizer the {@link Customizer} to provide more options for
+	 * the {@link RequestMatcherConfigurer}
+	 * @return the {@link HttpSecurity} for further customizations
+	 */
+	public HttpSecurity securityMatchers(Customizer<RequestMatcherConfigurer> requestMatcherCustomizer) {
+		requestMatcherCustomizer.customize(this.requestMatcherConfigurer);
+		return HttpSecurity.this;
+	}
+
+	/**
+	 * Allows configuring the {@link HttpSecurity} to only be invoked when matching the
+	 * provided {@link RequestMatcher}. If more advanced configuration is necessary,
+	 * consider using {@link #securityMatchers(Customizer)} ()}.
+	 *
+	 * <p>
+	 * Invoking {@link #securityMatcher(RequestMatcher)} will override previous
+	 * invocations of {@link #requestMatchers()}, {@link #mvcMatcher(String)},
+	 * {@link #antMatcher(String)}, {@link #regexMatcher(String)},
+	 * {@link #requestMatcher(RequestMatcher)}, {@link #securityMatchers(Customizer)},
+	 * {@link #securityMatchers()} and {@link #securityMatcher(String)}
+	 * </p>
+	 * @param requestMatcher the {@link RequestMatcher} to use (i.e. new
+	 * AntPathRequestMatcher("/admin/**","GET") )
+	 * @return the {@link HttpSecurity} for further customizations
+	 * @see #securityMatcher(String)
+	 */
+	public HttpSecurity securityMatcher(RequestMatcher requestMatcher) {
+		this.requestMatcher = requestMatcher;
+		return this;
+	}
+
+	/**
+	 * Allows configuring the {@link HttpSecurity} to only be invoked when matching the
+	 * provided pattern. This method creates a {@link MvcRequestMatcher} if Spring MVC is
+	 * in the classpath or creates an {@link AntPathRequestMatcher} if not. If more
+	 * advanced configuration is necessary, consider using
+	 * {@link #securityMatchers(Customizer)} or {@link #securityMatcher(RequestMatcher)}.
+	 *
+	 * <p>
+	 * Invoking {@link #securityMatcher(String)} will override previous invocations of
+	 * {@link #mvcMatcher(String)}}, {@link #requestMatchers()},
+	 * {@link #antMatcher(String)}, {@link #regexMatcher(String)}, and
+	 * {@link #requestMatcher(RequestMatcher)}.
+	 * </p>
+	 * @param pattern the pattern to match on (i.e. "/admin/**")
+	 * @return the {@link HttpSecurity} for further customizations
+	 * @see AntPathRequestMatcher
+	 * @see MvcRequestMatcher
+	 */
+	public HttpSecurity securityMatcher(String pattern) {
+		if (!mvcPresent) {
+			this.requestMatcher = new AntPathRequestMatcher(pattern);
+			return this;
+		}
+		if (!getContext().containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
+			throw new NoSuchBeanDefinitionException("A Bean named " + HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME
+					+ " of type " + HandlerMappingIntrospector.class.getName()
+					+ " is required to use MvcRequestMatcher. Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext.");
+		}
+		HandlerMappingIntrospector introspector = getContext().getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME,
+				HandlerMappingIntrospector.class);
+		this.requestMatcher = new MvcRequestMatcher(introspector, pattern);
+		return this;
+	}
+
 	/**
 	 * Allows configuring the {@link HttpSecurity} to only be invoked when matching the
 	 * provided ant pattern. If more advanced configuration is necessary, consider using
@@ -3367,8 +3686,10 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
 	 * </p>
 	 * @param antPattern the Ant Pattern to match on (i.e. "/admin/**")
 	 * @return the {@link HttpSecurity} for further customizations
+	 * @deprecated use {@link #securityMatcher(String)} instead
 	 * @see AntPathRequestMatcher
 	 */
+	@Deprecated
 	public HttpSecurity antMatcher(String antPattern) {
 		return requestMatcher(new AntPathRequestMatcher(antPattern));
 	}
@@ -3386,8 +3707,10 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
 	 * </p>
 	 * @param mvcPattern the Spring MVC Pattern to match on (i.e. "/admin/**")
 	 * @return the {@link HttpSecurity} for further customizations
+	 * @deprecated use {@link #securityMatcher(String)} instead
 	 * @see MvcRequestMatcher
 	 */
+	@Deprecated
 	public HttpSecurity mvcMatcher(String mvcPattern) {
 		HandlerMappingIntrospector introspector = new HandlerMappingIntrospector();
 		introspector.setApplicationContext(getContext());
@@ -3408,8 +3731,10 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
 	 * </p>
 	 * @param pattern the Regular Expression to match on (i.e. "/admin/.+")
 	 * @return the {@link HttpSecurity} for further customizations
-	 * @see RegexRequestMatcher
+	 * @deprecated use {@link #securityMatcher(RequestMatcher)} with a
+	 * {@link RegexRequestMatcher} instead
 	 */
+	@Deprecated
 	public HttpSecurity regexMatcher(String pattern) {
 		return requestMatcher(new RegexRequestMatcher(pattern, null));
 	}
@@ -3480,14 +3805,22 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
 			setApplicationContext(context);
 		}
 
+		/**
+		 * @deprecated use {@link #requestMatchers(HttpMethod, String...)} instead
+		 */
 		@Override
+		@Deprecated
 		public MvcMatchersRequestMatcherConfigurer mvcMatchers(HttpMethod method, String... mvcPatterns) {
 			List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns);
 			setMatchers(mvcMatchers);
 			return new MvcMatchersRequestMatcherConfigurer(getContext(), mvcMatchers, this.matchers);
 		}
 
+		/**
+		 * @deprecated use {@link #requestMatchers(String...)} instead
+		 */
 		@Override
+		@Deprecated
 		public MvcMatchersRequestMatcherConfigurer mvcMatchers(String... patterns) {
 			return mvcMatchers(null, patterns);
 		}
@@ -3500,7 +3833,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
 
 		private void setMatchers(List<? extends RequestMatcher> requestMatchers) {
 			this.matchers.addAll(requestMatchers);
-			requestMatcher(new OrRequestMatcher(this.matchers));
+			securityMatcher(new OrRequestMatcher(this.matchers));
 		}
 
 		/**

+ 14 - 4
config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurity.java

@@ -135,7 +135,7 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
 	 * <pre>
 	 * webSecurityBuilder.ignoring()
 	 * // ignore all URLs that start with /resources/ or /static/
-	 * 		.antMatchers(&quot;/resources/**&quot;, &quot;/static/**&quot;);
+	 * 		.requestMatchers(&quot;/resources/**&quot;, &quot;/static/**&quot;);
 	 * </pre>
 	 *
 	 * Alternatively this will accomplish the same result:
@@ -143,7 +143,7 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
 	 * <pre>
 	 * webSecurityBuilder.ignoring()
 	 * // ignore all URLs that start with /resources/ or /static/
-	 * 		.antMatchers(&quot;/resources/**&quot;).antMatchers(&quot;/static/**&quot;);
+	 * 		.requestMatchers(&quot;/resources/**&quot;).requestMatchers(&quot;/static/**&quot;);
 	 * </pre>
 	 *
 	 * Multiple invocations of ignoring() are also additive, so the following is also
@@ -152,10 +152,10 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
 	 * <pre>
 	 * webSecurityBuilder.ignoring()
 	 * // ignore all URLs that start with /resources/
-	 * 		.antMatchers(&quot;/resources/**&quot;);
+	 * 		.requestMatchers(&quot;/resources/**&quot;);
 	 * webSecurityBuilder.ignoring()
 	 * // ignore all URLs that start with /static/
-	 * 		.antMatchers(&quot;/static/**&quot;);
+	 * 		.requestMatchers(&quot;/static/**&quot;);
 	 * // now both URLs that start with /resources/ and /static/ will be ignored
 	 * </pre>
 	 * @return the {@link IgnoredRequestConfigurer} to use for registering request that
@@ -380,7 +380,9 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
 	 * {@link MvcRequestMatcher#setMethod(HttpMethod)}
 	 *
 	 * @author Rob Winch
+	 * @deprecated use {@link MvcRequestMatcher.Builder} instead
 	 */
+	@Deprecated
 	public final class MvcMatchersIgnoredRequestConfigurer extends IgnoredRequestConfigurer {
 
 		private final List<MvcRequestMatcher> mvcMatchers;
@@ -412,14 +414,22 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
 			setApplicationContext(context);
 		}
 
+		/**
+		 * @deprecated use {@link #requestMatchers(HttpMethod, String...)} instead
+		 */
 		@Override
+		@Deprecated
 		public MvcMatchersIgnoredRequestConfigurer mvcMatchers(HttpMethod method, String... mvcPatterns) {
 			List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns);
 			WebSecurity.this.ignoredRequests.addAll(mvcMatchers);
 			return new MvcMatchersIgnoredRequestConfigurer(getApplicationContext(), mvcMatchers);
 		}
 
+		/**
+		 * @deprecated use {@link #requestMatchers(String...)} instead
+		 */
 		@Override
+		@Deprecated
 		public MvcMatchersIgnoredRequestConfigurer mvcMatchers(String... mvcPatterns) {
 			return mvcMatchers(null, mvcPatterns);
 		}

+ 2 - 2
config/src/main/java/org/springframework/security/config/annotation/web/configuration/EnableWebSecurity.java

@@ -41,12 +41,12 @@ import org.springframework.security.web.SecurityFilterChain;
  * 	public WebSecurityCustomizer webSecurityCustomizer() {
  * 		return (web) -> web.ignoring()
  * 		// Spring Security should completely ignore URLs starting with /resources/
- * 				.antMatchers(&quot;/resources/**&quot;);
+ * 				.requestMatchers(&quot;/resources/**&quot;);
  * 	}
  *
  * 	&#064;Bean
  * 	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- * 		http.authorizeRequests().antMatchers(&quot;/public/**&quot;).permitAll().anyRequest()
+ * 		http.authorizeRequests().requestMatchers(&quot;/public/**&quot;).permitAll().anyRequest()
  * 				.hasRole(&quot;USER&quot;).and()
  * 				// Possibly more configuration ...
  * 				.formLogin() // enable form based log in

+ 10 - 0
config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java

@@ -146,12 +146,20 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
 			return postProcess(this.managerBuilder.build());
 		}
 
+		/**
+		 * @deprecated use {@link #requestMatchers(String...)} instead
+		 */
 		@Override
+		@Deprecated
 		public MvcMatchersAuthorizedUrl mvcMatchers(String... mvcPatterns) {
 			return mvcMatchers(null, mvcPatterns);
 		}
 
+		/**
+		 * @deprecated use {@link #requestMatchers(HttpMethod, String...)} instead
+		 */
 		@Override
+		@Deprecated
 		public MvcMatchersAuthorizedUrl mvcMatchers(HttpMethod method, String... mvcPatterns) {
 			return new MvcMatchersAuthorizedUrl(createMvcMatchers(method, mvcPatterns));
 		}
@@ -202,7 +210,9 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
 	 * configuring the {@link MvcRequestMatcher#setServletPath(String)}.
 	 *
 	 * @author Evgeniy Cheban
+	 * @deprecated use {@link MvcRequestMatcher.Builder} instead
 	 */
+	@Deprecated
 	public final class MvcMatchersAuthorizedUrl extends AuthorizedUrl {
 
 		private MvcMatchersAuthorizedUrl(List<MvcRequestMatcher> matchers) {

+ 40 - 0
config/src/main/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurer.java

@@ -163,7 +163,10 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
 	 * </pre>
 	 *
 	 * @since 4.0
+	 * @deprecated use {@link #ignoringRequestMatchers(RequestMatcher...)} with an
+	 * {@link org.springframework.security.web.util.matcher.AntPathRequestMatcher} instead
 	 */
+	@Deprecated
 	public CsrfConfigurer<H> ignoringAntMatchers(String... antPatterns) {
 		return new IgnoreCsrfProtectionRegistry(this.context).antMatchers(antPatterns).and();
 	}
@@ -197,6 +200,35 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
 		return new IgnoreCsrfProtectionRegistry(this.context).requestMatchers(requestMatchers).and();
 	}
 
+	/**
+	 * <p>
+	 * Allows specifying {@link HttpServletRequest} that should not use CSRF Protection
+	 * even if they match the {@link #requireCsrfProtectionMatcher(RequestMatcher)}.
+	 * </p>
+	 *
+	 * <p>
+	 * For example, the following configuration will ensure CSRF protection ignores:
+	 * </p>
+	 * <ul>
+	 * <li>Any GET, HEAD, TRACE, OPTIONS (this is the default)</li>
+	 * <li>We also explicitly state to ignore any request that starts with "/sockjs/"</li>
+	 * </ul>
+	 *
+	 * <pre>
+	 * http
+	 *     .csrf()
+	 *         .ignoringRequestMatchers("/sockjs/**")
+	 *         .and()
+	 *     ...
+	 * </pre>
+	 *
+	 * @since 5.8
+	 * @see AbstractRequestMatcherRegistry#requestMatchers(String...)
+	 */
+	public CsrfConfigurer<H> ignoringRequestMatchers(String... patterns) {
+		return new IgnoreCsrfProtectionRegistry(this.context).requestMatchers(patterns).and();
+	}
+
 	/**
 	 * <p>
 	 * Specify the {@link SessionAuthenticationStrategy} to use. The default is a
@@ -350,14 +382,22 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
 			setApplicationContext(context);
 		}
 
+		/**
+		 * @deprecated use {@link #requestMatchers(HttpMethod, String...)} instead
+		 */
 		@Override
+		@Deprecated
 		public MvcMatchersIgnoreCsrfProtectionRegistry mvcMatchers(HttpMethod method, String... mvcPatterns) {
 			List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns);
 			CsrfConfigurer.this.ignoredCsrfProtectionMatchers.addAll(mvcMatchers);
 			return new MvcMatchersIgnoreCsrfProtectionRegistry(getApplicationContext(), mvcMatchers);
 		}
 
+		/**
+		 * @deprecated use {@link #requestMatchers(String...)} instead
+		 */
 		@Override
+		@Deprecated
 		public MvcMatchersIgnoreCsrfProtectionRegistry mvcMatchers(String... mvcPatterns) {
 			return mvcMatchers(null, mvcPatterns);
 		}

+ 13 - 1
config/src/main/kotlin/org/springframework/security/config/annotation/web/CsrfDsl.kt

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2020 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -42,6 +42,7 @@ class CsrfDsl {
 
     private var ignoringAntMatchers: Array<out String>? = null
     private var ignoringRequestMatchers: Array<out RequestMatcher>? = null
+    private var ignoringRequestMatchersPatterns: Array<out String>? = null
     private var disabled = false
 
     /**
@@ -66,6 +67,16 @@ class CsrfDsl {
         ignoringRequestMatchers = requestMatchers
     }
 
+    /**
+     * Allows specifying [HttpServletRequest]s that should not use CSRF Protection
+     * even if they match the [requireCsrfProtectionMatcher].
+     *
+     * @param patterns the patterns that should not use CSRF protection
+     */
+    fun ignoringRequestMatchers(vararg patterns: String) {
+        ignoringRequestMatchersPatterns = patterns
+    }
+
     /**
      * Disable CSRF protection
      */
@@ -80,6 +91,7 @@ class CsrfDsl {
             sessionAuthenticationStrategy?.also { csrf.sessionAuthenticationStrategy(sessionAuthenticationStrategy) }
             ignoringAntMatchers?.also { csrf.ignoringAntMatchers(*ignoringAntMatchers!!) }
             ignoringRequestMatchers?.also { csrf.ignoringRequestMatchers(*ignoringRequestMatchers!!) }
+            ignoringRequestMatchersPatterns?.also { csrf.ignoringRequestMatchers(*ignoringRequestMatchersPatterns!!) }
             if (disabled) {
                 csrf.disable()
             }

+ 105 - 1
config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2018 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -16,19 +16,28 @@
 
 package org.springframework.security.config.annotation.web;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
 import java.util.List;
 
 import jakarta.servlet.DispatcherType;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.context.ApplicationContext;
 import org.springframework.http.HttpMethod;
+import org.springframework.security.config.annotation.ObjectPostProcessor;
+import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
 import org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher;
 import org.springframework.security.web.util.matcher.RegexRequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcher;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
 
 /**
  * Tests for {@link AbstractRequestMatcherRegistry}.
@@ -37,11 +46,21 @@ import static org.assertj.core.api.Assertions.assertThat;
  */
 public class AbstractRequestMatcherRegistryTests {
 
+	private static final ObjectPostProcessor<Object> NO_OP_OBJECT_POST_PROCESSOR = new ObjectPostProcessor<Object>() {
+		@Override
+		public <O> O postProcess(O object) {
+			return object;
+		}
+	};
+
 	private TestRequestMatcherRegistry matcherRegistry;
 
 	@BeforeEach
 	public void setUp() {
 		this.matcherRegistry = new TestRequestMatcherRegistry();
+		ApplicationContext context = mock(ApplicationContext.class);
+		given(context.getBean(ObjectPostProcessor.class)).willReturn(NO_OP_OBJECT_POST_PROCESSOR);
+		this.matcherRegistry.setApplicationContext(context);
 	}
 
 	@Test
@@ -93,6 +112,91 @@ public class AbstractRequestMatcherRegistryTests {
 		assertThat(requestMatchers.get(0)).isExactlyInstanceOf(DispatcherTypeRequestMatcher.class);
 	}
 
+	@Test
+	public void requestMatchersWhenPatternAndMvcPresentThenReturnMvcRequestMatcherType() throws Exception {
+		mockMvcPresentClasspath(true);
+		mockMvcIntrospector(true);
+		List<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers("/path");
+		assertThat(requestMatchers).isNotEmpty();
+		assertThat(requestMatchers.size()).isEqualTo(1);
+		assertThat(requestMatchers.get(0)).isExactlyInstanceOf(MvcRequestMatcher.class);
+	}
+
+	@Test
+	public void requestMatchersWhenHttpMethodAndPatternAndMvcPresentThenReturnMvcRequestMatcherType() throws Exception {
+		mockMvcPresentClasspath(true);
+		mockMvcIntrospector(true);
+		List<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers(HttpMethod.GET, "/path");
+		assertThat(requestMatchers).isNotEmpty();
+		assertThat(requestMatchers.size()).isEqualTo(1);
+		assertThat(requestMatchers.get(0)).isExactlyInstanceOf(MvcRequestMatcher.class);
+	}
+
+	@Test
+	public void requestMatchersWhenHttpMethodAndMvcPresentThenReturnMvcRequestMatcherType() throws Exception {
+		mockMvcPresentClasspath(true);
+		mockMvcIntrospector(true);
+		List<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers(HttpMethod.GET);
+		assertThat(requestMatchers).isNotEmpty();
+		assertThat(requestMatchers.size()).isEqualTo(1);
+		assertThat(requestMatchers.get(0)).isExactlyInstanceOf(MvcRequestMatcher.class);
+	}
+
+	@Test
+	public void requestMatchersWhenPatternAndMvcNotPresentThenReturnAntPathRequestMatcherType() throws Exception {
+		mockMvcPresentClasspath(false);
+		mockMvcIntrospector(false);
+		List<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers("/path");
+		assertThat(requestMatchers).isNotEmpty();
+		assertThat(requestMatchers.size()).isEqualTo(1);
+		assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class);
+	}
+
+	@Test
+	public void requestMatchersWhenHttpMethodAndPatternAndMvcNotPresentThenReturnAntPathRequestMatcherType()
+			throws Exception {
+		mockMvcPresentClasspath(false);
+		mockMvcIntrospector(false);
+		List<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers(HttpMethod.GET, "/path");
+		assertThat(requestMatchers).isNotEmpty();
+		assertThat(requestMatchers.size()).isEqualTo(1);
+		assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class);
+	}
+
+	@Test
+	public void requestMatchersWhenHttpMethodAndMvcNotPresentThenReturnAntPathMatcherType() throws Exception {
+		mockMvcPresentClasspath(false);
+		mockMvcIntrospector(false);
+		List<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers(HttpMethod.GET);
+		assertThat(requestMatchers).isNotEmpty();
+		assertThat(requestMatchers.size()).isEqualTo(1);
+		assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class);
+	}
+
+	@Test
+	public void requestMatchersWhenMvcPresentInClassPathAndMvcIntrospectorBeanNotAvailableThenException()
+			throws Exception {
+		mockMvcPresentClasspath(true);
+		mockMvcIntrospector(false);
+		assertThatExceptionOfType(NoSuchBeanDefinitionException.class)
+				.isThrownBy(() -> this.matcherRegistry.requestMatchers("/path")).withMessageContaining(
+						"Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext");
+	}
+
+	private void mockMvcIntrospector(boolean isPresent) {
+		ApplicationContext context = this.matcherRegistry.getApplicationContext();
+		given(context.containsBean("mvcHandlerMappingIntrospector")).willReturn(isPresent);
+	}
+
+	private void mockMvcPresentClasspath(Object newValue) throws Exception {
+		Field mvcPresentField = AbstractRequestMatcherRegistry.class.getDeclaredField("mvcPresent");
+		mvcPresentField.setAccessible(true);
+		Field modifiersField = Field.class.getDeclaredField("modifiers");
+		modifiersField.setAccessible(true);
+		modifiersField.setInt(mvcPresentField, mvcPresentField.getModifiers() & ~Modifier.FINAL);
+		mvcPresentField.set(null, newValue);
+	}
+
 	private static class TestRequestMatcherRegistry extends AbstractRequestMatcherRegistry<List<RequestMatcher>> {
 
 		@Override

+ 54 - 0
config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerIgnoringRequestMatchersTests.java

@@ -20,6 +20,7 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.http.HttpMethod;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -27,11 +28,13 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 import org.springframework.security.config.test.SpringTestContext;
 import org.springframework.security.config.test.SpringTestContextExtension;
+import org.springframework.security.web.SecurityFilterChain;
 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcher;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
@@ -80,6 +83,22 @@ public class CsrfConfigurerIgnoringRequestMatchersTests {
 		this.mvc.perform(put("/no-csrf")).andExpect(status().isOk());
 	}
 
+	@Test
+	public void requestWhenIgnoringRequestMatcherPatternThenIgnores() throws Exception {
+		this.spring.register(IgnoringPathsAndMatchersPatternConfig.class, BasicController.class).autowire();
+		this.mvc.perform(put("/csrf")).andExpect(status().isForbidden());
+		this.mvc.perform(post("/csrf")).andExpect(status().isForbidden());
+		this.mvc.perform(put("/no-csrf")).andExpect(status().isOk());
+	}
+
+	@Test
+	public void requestWhenIgnoringRequestMatcherPatternInLambdaThenIgnores() throws Exception {
+		this.spring.register(IgnoringPathsAndMatchersPatternInLambdaConfig.class, BasicController.class).autowire();
+		this.mvc.perform(put("/csrf")).andExpect(status().isForbidden());
+		this.mvc.perform(post("/csrf")).andExpect(status().isForbidden());
+		this.mvc.perform(put("/no-csrf")).andExpect(status().isOk());
+	}
+
 	@Configuration
 	@EnableWebSecurity
 	static class IgnoringRequestMatchers extends WebSecurityConfigurerAdapter {
@@ -156,6 +175,41 @@ public class CsrfConfigurerIgnoringRequestMatchersTests {
 
 	}
 
+	@Configuration
+	@EnableWebSecurity
+	@EnableWebMvc
+	static class IgnoringPathsAndMatchersPatternConfig {
+
+		@Bean
+		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.csrf()
+					.ignoringRequestMatchers("/no-csrf");
+			// @formatter:on
+			return http.build();
+		}
+
+	}
+
+	@Configuration
+	@EnableWebSecurity
+	@EnableWebMvc
+	static class IgnoringPathsAndMatchersPatternInLambdaConfig {
+
+		@Bean
+		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.csrf((csrf) -> csrf
+					.ignoringRequestMatchers("/no-csrf")
+				);
+			// @formatter:on
+			return http.build();
+		}
+
+	}
+
 	@RestController
 	public static class BasicController {
 

+ 567 - 0
config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpSecuritySecurityMatchersTests.java

@@ -0,0 +1,567 @@
+/*
+ * Copyright 2002-2022 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.annotation.web.configurers;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.mock.web.MockFilterChain;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.mock.web.MockServletContext;
+import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.provisioning.InMemoryUserDetailsManager;
+import org.springframework.security.web.FilterChainProxy;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.security.config.Customizer.withDefaults;
+
+/**
+ * @author Rob Winch
+ *
+ */
+public class HttpSecuritySecurityMatchersTests {
+
+	AnnotationConfigWebApplicationContext context;
+
+	MockHttpServletRequest request;
+
+	MockHttpServletResponse response;
+
+	MockFilterChain chain;
+
+	@Autowired
+	FilterChainProxy springSecurityFilterChain;
+
+	@BeforeEach
+	public void setup() throws Exception {
+		this.request = new MockHttpServletRequest("GET", "");
+		this.request.setMethod("GET");
+		this.response = new MockHttpServletResponse();
+		this.chain = new MockFilterChain();
+		mockMvcPresentClasspath(true);
+	}
+
+	@AfterEach
+	public void cleanup() {
+		if (this.context != null) {
+			this.context.close();
+		}
+	}
+
+	@Test
+	public void securityMatcherWhenMvcThenMvcMatcher() throws Exception {
+		loadConfig(SecurityMatcherMvcConfig.class, LegacyMvcMatchingConfig.class);
+		this.request.setServletPath("/path");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
+		setup();
+		this.request.setServletPath("/path.html");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
+		setup();
+		this.request.setServletPath("/path/");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
+	}
+
+	@Test
+	public void securityMatcherWhenNoMvcThenAntMatcher() throws Exception {
+		mockMvcPresentClasspath(false);
+		loadConfig(SecurityMatcherNoMvcConfig.class, LegacyMvcMatchingConfig.class);
+		this.request.setServletPath("/path");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
+		setup();
+		this.request.setServletPath("/path.html");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
+		setup();
+		this.request.setServletPath("/path/");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
+	}
+
+	@Test
+	public void securityMatcherWhenMvcMatcherAndGetFiltersNoUnsupportedMethodExceptionFromDummyRequest() {
+		loadConfig(SecurityMatcherMvcConfig.class);
+		assertThat(this.springSecurityFilterChain.getFilters("/path")).isNotEmpty();
+	}
+
+	@Test
+	public void securityMatchersWhenMvcThenMvcMatcher() throws Exception {
+		loadConfig(SecurityMatchersMvcMatcherConfig.class, LegacyMvcMatchingConfig.class);
+		this.request.setServletPath("/path");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
+		setup();
+		this.request.setServletPath("/path.html");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
+		setup();
+		this.request.setServletPath("/path/");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
+	}
+
+	@Test
+	public void securityMatchersWhenMvcMatcherInLambdaThenPathIsSecured() throws Exception {
+		loadConfig(SecurityMatchersMvcMatcherInLambdaConfig.class, LegacyMvcMatchingConfig.class);
+		this.request.setServletPath("/path");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
+		setup();
+		this.request.setServletPath("/path.html");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
+		setup();
+		this.request.setServletPath("/path/");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
+	}
+
+	@Test
+	public void securityMatchersMvcMatcherServletPath() throws Exception {
+		loadConfig(SecurityMatchersMvcMatcherServletPathConfig.class);
+		this.request.setServletPath("/spring");
+		this.request.setRequestURI("/spring/path");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
+		setup();
+		this.request.setServletPath("");
+		this.request.setRequestURI("/path");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
+		setup();
+		this.request.setServletPath("/other");
+		this.request.setRequestURI("/other/path");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
+	}
+
+	@Test
+	public void securityMatchersWhensMvcMatcherServletPathInLambdaThenPathIsSecured() throws Exception {
+		loadConfig(SecurityMatchersMvcMatcherServletPathInLambdaConfig.class);
+		this.request.setServletPath("/spring");
+		this.request.setRequestURI("/spring/path");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
+		setup();
+		this.request.setServletPath("");
+		this.request.setRequestURI("/path");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
+		setup();
+		this.request.setServletPath("/other");
+		this.request.setRequestURI("/other/path");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
+	}
+
+	@Test
+	public void securityMatchersWhenMultiMvcMatcherInLambdaThenAllPathsAreDenied() throws Exception {
+		loadConfig(MultiMvcMatcherInLambdaConfig.class);
+		this.request.setRequestURI("/test-1");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
+		setup();
+		this.request.setRequestURI("/test-2");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
+		setup();
+		this.request.setRequestURI("/test-3");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
+	}
+
+	@Test
+	public void securityMatchersWhenMultiMvcMatcherThenAllPathsAreDenied() throws Exception {
+		loadConfig(MultiMvcMatcherConfig.class);
+		this.request.setRequestURI("/test-1");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
+		setup();
+		this.request.setRequestURI("/test-2");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
+		setup();
+		this.request.setRequestURI("/test-3");
+		this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
+		assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
+	}
+
+	public void loadConfig(Class<?>... configs) {
+		this.context = new AnnotationConfigWebApplicationContext();
+		this.context.register(configs);
+		this.context.setServletContext(new MockServletContext());
+		this.context.refresh();
+		this.context.getAutowireCapableBeanFactory().autowireBean(this);
+	}
+
+	private void mockMvcPresentClasspath(Object newValue) throws Exception {
+		mockMvcPresentClasspath(HttpSecurity.class, newValue);
+		mockMvcPresentClasspath(AbstractRequestMatcherRegistry.class, newValue);
+	}
+
+	private void mockMvcPresentClasspath(Class<?> clazz, Object newValue) throws Exception {
+		Field mvcPresentField = clazz.getDeclaredField("mvcPresent");
+		mvcPresentField.setAccessible(true);
+		Field modifiersField = Field.class.getDeclaredField("modifiers");
+		modifiersField.setAccessible(true);
+		modifiersField.setInt(mvcPresentField, mvcPresentField.getModifiers() & ~Modifier.FINAL);
+		mvcPresentField.set(null, newValue);
+	}
+
+	@EnableWebSecurity
+	@Configuration
+	@EnableWebMvc
+	static class MultiMvcMatcherInLambdaConfig {
+
+		@Bean
+		@Order(Ordered.HIGHEST_PRECEDENCE)
+		SecurityFilterChain first(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.securityMatchers((requests) -> requests
+					.requestMatchers("/test-1")
+					.requestMatchers("/test-2")
+					.requestMatchers("/test-3")
+				)
+				.authorizeHttpRequests((authorize) -> authorize.anyRequest().denyAll())
+				.httpBasic(withDefaults());
+			// @formatter:on
+			return http.build();
+		}
+
+		@Bean
+		SecurityFilterChain second(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.securityMatchers((requests) -> requests
+					.requestMatchers("/test-1")
+				)
+				.authorizeHttpRequests((authorize) -> authorize
+					.anyRequest().permitAll()
+				);
+			// @formatter:on
+			return http.build();
+		}
+
+		@RestController
+		static class PathController {
+
+			@RequestMapping({ "/test-1", "/test-2", "/test-3" })
+			String path() {
+				return "path";
+			}
+
+		}
+
+	}
+
+	@EnableWebSecurity
+	@Configuration
+	@EnableWebMvc
+	static class MultiMvcMatcherConfig {
+
+		@Bean
+		@Order(Ordered.HIGHEST_PRECEDENCE)
+		SecurityFilterChain first(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.securityMatchers()
+					.requestMatchers("/test-1")
+					.requestMatchers("/test-2")
+					.requestMatchers("/test-3")
+					.and()
+				.authorizeHttpRequests()
+					.anyRequest().denyAll()
+					.and()
+				.httpBasic(withDefaults());
+			// @formatter:on
+			return http.build();
+		}
+
+		@Bean
+		SecurityFilterChain second(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.securityMatchers()
+					.requestMatchers("/test-1")
+					.and()
+				.authorizeHttpRequests()
+					.anyRequest().permitAll();
+			// @formatter:on
+			return http.build();
+		}
+
+		@RestController
+		static class PathController {
+
+			@RequestMapping({ "/test-1", "/test-2", "/test-3" })
+			String path() {
+				return "path";
+			}
+
+		}
+
+	}
+
+	@EnableWebSecurity
+	@EnableWebMvc
+	@Configuration
+	@Import(UsersConfig.class)
+	static class SecurityMatcherMvcConfig {
+
+		@Bean
+		SecurityFilterChain appSecurity(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.securityMatcher("/path")
+				.httpBasic().and()
+				.authorizeHttpRequests()
+					.anyRequest().denyAll();
+			// @formatter:on
+			return http.build();
+		}
+
+		@RestController
+		static class PathController {
+
+			@RequestMapping("/path")
+			String path() {
+				return "path";
+			}
+
+		}
+
+	}
+
+	@EnableWebSecurity
+	@Configuration
+	@Import(UsersConfig.class)
+	static class SecurityMatcherNoMvcConfig {
+
+		@Bean
+		SecurityFilterChain appSecurity(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+					.securityMatcher("/path")
+					.httpBasic().and()
+					.authorizeHttpRequests()
+					.anyRequest().denyAll();
+			// @formatter:on
+			return http.build();
+		}
+
+		@RestController
+		static class PathController {
+
+			@RequestMapping("/path")
+			String path() {
+				return "path";
+			}
+
+		}
+
+	}
+
+	@EnableWebSecurity
+	@Configuration
+	@EnableWebMvc
+	@Import(UsersConfig.class)
+	static class SecurityMatchersMvcMatcherConfig {
+
+		@Bean
+		SecurityFilterChain appSecurity(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.securityMatchers()
+					.requestMatchers("/path")
+					.and()
+				.httpBasic().and()
+				.authorizeHttpRequests()
+					.anyRequest().denyAll();
+			// @formatter:on
+			return http.build();
+		}
+
+		@RestController
+		static class PathController {
+
+			@RequestMapping("/path")
+			String path() {
+				return "path";
+			}
+
+		}
+
+	}
+
+	@EnableWebSecurity
+	@Configuration
+	@EnableWebMvc
+	static class SecurityMatchersMvcMatcherInLambdaConfig {
+
+		@Bean
+		SecurityFilterChain appSecurity(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.securityMatchers((matchers) -> matchers
+					.requestMatchers("/path")
+				)
+				.httpBasic(withDefaults())
+				.authorizeHttpRequests((authorize) -> authorize
+					.anyRequest().denyAll()
+				);
+			// @formatter:on
+			return http.build();
+		}
+
+		@RestController
+		static class PathController {
+
+			@RequestMapping("/path")
+			String path() {
+				return "path";
+			}
+
+		}
+
+	}
+
+	@EnableWebSecurity
+	@Configuration
+	@EnableWebMvc
+	@Import(UsersConfig.class)
+	static class SecurityMatchersMvcMatcherServletPathConfig {
+
+		@Bean
+		SecurityFilterChain appSecurity(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {
+			MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector)
+					.servletPath("/spring");
+			// @formatter:off
+			http
+				.securityMatchers()
+					.requestMatchers(mvcMatcherBuilder.pattern("/path"))
+					.requestMatchers(mvcMatcherBuilder.pattern("/never-match"))
+					.and()
+				.httpBasic().and()
+				.authorizeHttpRequests()
+					.anyRequest().denyAll();
+			// @formatter:on
+			return http.build();
+		}
+
+		@RestController
+		static class PathController {
+
+			@RequestMapping("/path")
+			String path() {
+				return "path";
+			}
+
+		}
+
+	}
+
+	@EnableWebSecurity
+	@Configuration
+	@EnableWebMvc
+	@Import(UsersConfig.class)
+	static class SecurityMatchersMvcMatcherServletPathInLambdaConfig {
+
+		@Bean
+		SecurityFilterChain appSecurity(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {
+			MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector)
+					.servletPath("/spring");
+			// @formatter:off
+			http
+				.securityMatchers((matchers) -> matchers
+					.requestMatchers(mvcMatcherBuilder.pattern("/path"))
+					.requestMatchers(mvcMatcherBuilder.pattern("/never-match"))
+				)
+				.httpBasic(withDefaults())
+				.authorizeHttpRequests((authorize) -> authorize
+					.anyRequest().denyAll()
+				);
+			// @formatter:on
+			return http.build();
+		}
+
+		@RestController
+		static class PathController {
+
+			@RequestMapping("/path")
+			String path() {
+				return "path";
+			}
+
+		}
+
+	}
+
+	@Configuration
+	static class UsersConfig {
+
+		@Bean
+		UserDetailsService userDetailsService() {
+			UserDetails user = User.withDefaultPasswordEncoder().username("user").password("password").roles("USER")
+					.build();
+			return new InMemoryUserDetailsManager(user);
+		}
+
+	}
+
+	@Configuration
+	static class LegacyMvcMatchingConfig implements WebMvcConfigurer {
+
+		@Override
+		public void configurePathMatch(PathMatchConfigurer configurer) {
+			configurer.setUseSuffixPatternMatch(true);
+			configurer.setUseTrailingSlashMatch(true);
+		}
+
+	}
+
+}

+ 33 - 0
config/src/test/kotlin/org/springframework/security/config/annotation/web/CsrfDslTests.kt

@@ -44,6 +44,7 @@ import org.springframework.test.web.servlet.MockMvc
 import org.springframework.test.web.servlet.post
 import org.springframework.web.bind.annotation.PostMapping
 import org.springframework.web.bind.annotation.RestController
+import org.springframework.web.servlet.config.annotation.EnableWebMvc
 
 /**
  * Tests for [CsrfDsl]
@@ -279,6 +280,38 @@ class CsrfDslTests {
         }
     }
 
+    @Test
+    fun `CSRF when ignoring request matchers pattern then CSRF disabled on matching requests`() {
+        this.spring.register(IgnoringRequestMatchersPatternConfig::class.java, BasicController::class.java).autowire()
+
+        this.mockMvc.post("/test1")
+            .andExpect {
+                status { isForbidden() }
+            }
+
+        this.mockMvc.post("/test2")
+            .andExpect {
+                status { isOk() }
+            }
+    }
+
+    @Configuration
+    @EnableWebSecurity
+    @EnableWebMvc
+    open class IgnoringRequestMatchersPatternConfig {
+
+        @Bean
+        open fun filterChain(http: HttpSecurity): SecurityFilterChain {
+            http {
+                csrf {
+                    requireCsrfProtectionMatcher = AntPathRequestMatcher("/**")
+                    ignoringRequestMatchers("/test2")
+                }
+            }
+            return http.build()
+        }
+    }
+
     @RestController
     internal class BasicController {
         @PostMapping("/test1")

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

@@ -109,13 +109,14 @@ SecurityFilterChain web(HttpSecurity http, AuthorizationManager<RequestAuthoriza
 
 @Bean
 AuthorizationManager<RequestAuthorizationContext> requestMatcherAuthorizationManager(HandlerMappingIntrospector introspector) {
+    MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector);
     RequestMatcher permitAll =
             new AndRequestMatcher(
-                    new MvcRequestMatcher(introspector, "/resources/**"),
-                    new MvcRequestMatcher(introspector, "/signup"),
-                    new MvcRequestMatcher(introspector, "/about"));
-    RequestMatcher admin = new MvcRequestMatcher(introspector, "/admin/**");
-    RequestMatcher db = new MvcRequestMatcher(introspector, "/db/**");
+                    mvcMatcherBuilder.pattern("/resources/**"),
+                    mvcMatcherBuilder.pattern("/signup"),
+                    mvcMatcherBuilder.pattern("/about"));
+    RequestMatcher admin = mvcMatcherBuilder.pattern("/admin/**");
+    RequestMatcher db = mvcMatcherBuilder.pattern("/db/**");
     RequestMatcher any = AnyRequestMatcher.INSTANCE;
     AuthorizationManager<HttpRequestServlet> manager = RequestMatcherDelegatingAuthorizationManager.builder()
             .add(permitAll, (context) -> new AuthorizationDecision(true))

+ 2 - 2
docs/modules/ROOT/pages/servlet/authorization/expression-based.adoc

@@ -144,7 +144,7 @@ You could then refer to the method as follows:
 ----
 http
     .authorizeHttpRequests(authorize -> authorize
-        .antMatchers("/user/**").access("@webSecurity.check(authentication,request)")
+        .requestMatchers("/user/**").access("@webSecurity.check(authentication,request)")
         ...
     )
 ----
@@ -210,7 +210,7 @@ You could then refer to the method as follows:
 ----
 http
 	.authorizeHttpRequests(authorize -> authorize
-		.antMatchers("/user/{userId}/**").access("@webSecurity.checkUserId(authentication,#userId)")
+		.requestMatchers("/user/{userId}/**").access("@webSecurity.checkUserId(authentication,#userId)")
 		...
 	);
 ----

+ 20 - 14
docs/modules/ROOT/pages/servlet/integrations/mvc.adoc

@@ -151,8 +151,8 @@ To restrict access to this controller method to admin users, you can provide aut
 @Bean
 public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
 	http
-		.authorizeHttpRequests(authorize -> authorize
-			.antMatchers("/admin").hasRole("ADMIN")
+		.authorizeHttpRequests((authorize) -> authorize
+			.requestMatchers("/admin").hasRole("ADMIN")
 		);
 	return http.build();
 }
@@ -164,8 +164,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
 @Bean
 open fun filterChain(http: HttpSecurity): SecurityFilterChain {
     http {
-        authorizeRequests {
-            authorize(AntPathRequestMatcher("/admin"), hasRole("ADMIN"))
+        authorizeHttpRequests {
+            authorize("/admin", hasRole("ADMIN"))
         }
     }
     return http.build()
@@ -191,20 +191,24 @@ Additionally, depending on our Spring MVC configuration, the `/admin` URL also m
 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.
 
-Instead, we can use Spring Security's `MvcRequestMatcher`.
-The following configuration protects the same URLs that Spring MVC matches on by using Spring MVC to match on the URL.
+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 that you can use the `MvcRequestMatcher.Builder` to create multiple `MvcRequestMatcher` instances that share the same servlet path:
 
 ====
 .Java
 [source,java,role="primary"]
 ----
 @Bean
-public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+public SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {
+	MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector).servletPath("/path");
 	http
-		.authorizeHttpRequests(authorize -> authorize
-			.mvcMatchers("/admin").hasRole("ADMIN")
+		.authorizeHttpRequests((authorize) -> authorize
+			.requestMatchers(mvcMatcherBuilder.pattern("/admin")).hasRole("ADMIN")
+			.requestMatchers(mvcMatcherBuilder.pattern("/user")).hasRole("USER")
 		);
-	// ...
+	return http.build();
 }
 ----
 
@@ -212,13 +216,15 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
 [source,kotlin,role="secondary"]
 ----
 @Bean
-open fun filterChain(http: HttpSecurity): SecurityFilterChain {
+open fun filterChain(http: HttpSecurity, introspector: HandlerMappingIntrospector): SecurityFilterChain {
+    val mvcMatcherBuilder = MvcRequestMatcher.Builder(introspector)
     http {
-        authorizeRequests {
-            authorize("/admin", hasRole("ADMIN"))
+        authorizeHttpRequests {
+            authorize(mvcMatcherBuilder.pattern("/admin"), hasRole("ADMIN"))
+            authorize(mvcMatcherBuilder.pattern("/user"), hasRole("USER"))
         }
     }
-    // ...
+    return http.build()
 }
 ----
 ====

+ 2 - 2
docs/modules/ROOT/pages/servlet/integrations/websocket.adoc

@@ -584,7 +584,7 @@ public class WebSecurityConfig {
         http
             .csrf(csrf -> csrf
                 // ignore our stomp endpoints since they are protected using Stomp headers
-                .ignoringAntMatchers("/chat/**")
+                .ignoringRequestMatchers("/chat/**")
             )
             .headers(headers -> headers
                 // allow same origin to frame our site to support iframe SockJS
@@ -610,7 +610,7 @@ open class WebSecurityConfig {
     open fun filterChain(http: HttpSecurity): SecurityFilterChain {
         http {
             csrf {
-                ignoringAntMatchers("/chat/**")
+                ignoringRequestMatchers("/chat/**")
             }
             headers {
                 frameOptions {

+ 1 - 1
docs/modules/ROOT/pages/whats-new.adoc

@@ -17,4 +17,4 @@ Reorganize imports
 Reorganize imports
 * https://github.com/spring-projects/spring-security/issues/11026[gh-11026] - Use `RequestAttributeSecurityContextRepository` instead of `NullSecurityContextRepository`
 * https://github.com/spring-projects/spring-security/pull/11887[gh-11827] - Change default authority for `oauth2Login()`
-* https://github.com/spring-projects/spring-security/issues/10347[gh-10347] - Remove `UsernamePasswordAuthenticationToken` check in `BasicAuthenticationFilter`
+* https://github.com/spring-projects/spring-security/issues/10347[gh-10347] - Remove `UsernamePasswordAuthenticationToken` check in `BasicAuthenticationFilter`

+ 56 - 1
web/src/main/java/org/springframework/security/web/servlet/util/matcher/MvcRequestMatcher.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2012-2021 the original author or authors.
+ * Copyright 2012-2022 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.
@@ -172,4 +172,59 @@ public class MvcRequestMatcher implements RequestMatcher, RequestVariablesExtrac
 
 	}
 
+	/**
+	 * A builder for {@link MvcRequestMatcher}
+	 *
+	 * @author Marcus Da Coregio
+	 * @since 5.8
+	 */
+	public static final class Builder {
+
+		private final HandlerMappingIntrospector introspector;
+
+		private String servletPath;
+
+		/**
+		 * Construct a new instance of this builder
+		 */
+		public Builder(HandlerMappingIntrospector introspector) {
+			this.introspector = introspector;
+		}
+
+		/**
+		 * Sets the servlet path to be used by the {@link MvcRequestMatcher} generated by
+		 * this builder
+		 * @param servletPath the servlet path to use
+		 * @return the {@link Builder} for further configuration
+		 */
+		public Builder servletPath(String servletPath) {
+			this.servletPath = servletPath;
+			return this;
+		}
+
+		/**
+		 * Creates an {@link MvcRequestMatcher} that uses the provided pattern to match
+		 * @param pattern the pattern used to match
+		 * @return the generated {@link MvcRequestMatcher}
+		 */
+		public MvcRequestMatcher pattern(String pattern) {
+			return pattern(null, pattern);
+		}
+
+		/**
+		 * Creates an {@link MvcRequestMatcher} that uses the provided pattern and HTTP
+		 * method to match
+		 * @param method the {@link HttpMethod}, can be null
+		 * @param pattern the patterns used to match
+		 * @return the generated {@link MvcRequestMatcher}
+		 */
+		public MvcRequestMatcher pattern(HttpMethod method, String pattern) {
+			MvcRequestMatcher mvcRequestMatcher = new MvcRequestMatcher(this.introspector, pattern);
+			mvcRequestMatcher.setServletPath(this.servletPath);
+			mvcRequestMatcher.setMethod(method);
+			return mvcRequestMatcher;
+		}
+
+	}
+
 }

+ 28 - 1
web/src/test/java/org/springframework/security/web/servlet/util/matcher/MvcRequestMatcherTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2012-2021 the original author or authors.
+ * Copyright 2012-2022 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.
@@ -28,6 +28,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
 
 import org.springframework.http.HttpMethod;
 import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.test.util.ReflectionTestUtils;
 import org.springframework.web.HttpRequestMethodNotSupportedException;
 import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
 import org.springframework.web.servlet.handler.MatchableHandlerMapping;
@@ -245,4 +246,30 @@ public class MvcRequestMatcherTests {
 		assertThat(this.matcher.matcher(this.request).isMatch()).isTrue();
 	}
 
+	@Test
+	public void builderWhenServletPathThenServletPathPresent() {
+		MvcRequestMatcher matcher = new MvcRequestMatcher.Builder(this.introspector).servletPath("/path")
+				.pattern("/endpoint");
+		assertThat(matcher.getServletPath()).isEqualTo("/path");
+		assertThat(ReflectionTestUtils.getField(matcher, "pattern")).isEqualTo("/endpoint");
+		assertThat(ReflectionTestUtils.getField(matcher, "method")).isNull();
+	}
+
+	@Test
+	public void builderWhenPatternThenPatternPresent() {
+		MvcRequestMatcher matcher = new MvcRequestMatcher.Builder(this.introspector).pattern("/endpoint");
+		assertThat(matcher.getServletPath()).isNull();
+		assertThat(ReflectionTestUtils.getField(matcher, "pattern")).isEqualTo("/endpoint");
+		assertThat(ReflectionTestUtils.getField(matcher, "method")).isNull();
+	}
+
+	@Test
+	public void builderWhenMethodAndPatternThenMethodAndPatternPresent() {
+		MvcRequestMatcher matcher = new MvcRequestMatcher.Builder(this.introspector).pattern(HttpMethod.GET,
+				"/endpoint");
+		assertThat(matcher.getServletPath()).isNull();
+		assertThat(ReflectionTestUtils.getField(matcher, "pattern")).isEqualTo("/endpoint");
+		assertThat(ReflectionTestUtils.getField(matcher, "method")).isEqualTo(HttpMethod.GET);
+	}
+
 }