浏览代码

Add support checking AnyRequestMatcher securityFilterChains

Closes gh-15220
Max Batischev 1 年之前
父节点
当前提交
4c780bf8d4

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

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2024 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.
@@ -63,6 +63,7 @@ import org.springframework.security.web.firewall.HttpStatusRequestRejectedHandle
 import org.springframework.security.web.firewall.ObservationMarkingRequestRejectedHandler;
 import org.springframework.security.web.firewall.RequestRejectedHandler;
 import org.springframework.security.web.firewall.StrictHttpFirewall;
+import org.springframework.security.web.util.matcher.AnyRequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcherEntry;
 import org.springframework.util.Assert;
@@ -296,8 +297,20 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
 			requestMatcherPrivilegeEvaluatorsEntries
 				.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
 		}
+		boolean anyRequestConfigured = false;
+		RequestMatcher matcher = null;
 		for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
 			SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
+			Assert.isTrue(!anyRequestConfigured,
+					"A filter chain that matches any request has already been configured, which means that this filter chain for ["
+							+ matcher
+							+ "] will never get invoked. Please use `HttpSecurity#securityMatcher` to ensure that there is only one filter chain configured for 'any request' and that the 'any request' filter chain is published last.");
+			if (securityFilterChain instanceof DefaultSecurityFilterChain defaultSecurityFilterChain) {
+				matcher = defaultSecurityFilterChain.getRequestMatcher();
+				if (matcher instanceof AnyRequestMatcher) {
+					anyRequestConfigured = true;
+				}
+			}
 			securityFilterChains.add(securityFilterChain);
 			requestMatcherPrivilegeEvaluatorsEntries
 				.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));

+ 31 - 1
config/src/test/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurationTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2024 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.
@@ -318,6 +318,14 @@ public class WebSecurityConfigurationTests {
 		assertThat(privilegeEvaluator.isAllowed("/ignoring1/child", null)).isTrue();
 	}
 
+	@Test
+	public void loadConfigWhenTwoSecurityFilterChainsPresentAndSecondWithAnyRequestThenException() {
+		assertThatExceptionOfType(BeanCreationException.class)
+			.isThrownBy(() -> this.spring.register(MultipleAnyRequestSecurityFilterChainConfig.class).autowire())
+			.havingRootCause()
+			.isExactlyInstanceOf(IllegalArgumentException.class);
+	}
+
 	private void assertAnotherUserPermission(WebInvocationPrivilegeEvaluator privilegeEvaluator) {
 		Authentication anotherUser = new TestingAuthenticationToken("anotherUser", "password", "ROLE_ANOTHER");
 		assertThat(privilegeEvaluator.isAllowed("/user", anotherUser)).isFalse();
@@ -819,4 +827,26 @@ public class WebSecurityConfigurationTests {
 
 	}
 
+	@Configuration
+	@EnableWebSecurity
+	@EnableWebMvc
+	@Import(AuthenticationTestConfiguration.class)
+	static class MultipleAnyRequestSecurityFilterChainConfig {
+
+		@Bean
+		@Order(0)
+		SecurityFilterChain api1(HttpSecurity http) throws Exception {
+			http.authorizeHttpRequests((auth) -> auth.anyRequest().authenticated());
+			return http.build();
+		}
+
+		@Bean
+		@Order(1)
+		SecurityFilterChain api2(HttpSecurity http) throws Exception {
+			http.securityMatcher("/app/**").authorizeHttpRequests((auth) -> auth.anyRequest().authenticated());
+			return http.build();
+		}
+
+	}
+
 }