瀏覽代碼

Move PathPatternRequestMatcher.Builder to Shared Object

This commit changes the DSL to look for a shared object
instead of publishing a bean for PathPatternRequestMatcher.Builder.

Closes gh-17746
Josh Cummings 5 天之前
父節點
當前提交
aeb2dbc2b6

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

@@ -27,11 +27,13 @@ import org.apache.commons.logging.LogFactory;
 import org.springframework.context.ApplicationContext;
 import org.springframework.http.HttpMethod;
 import org.springframework.lang.Nullable;
+import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
 import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
 import org.springframework.security.web.util.matcher.AnyRequestMatcher;
 import org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcher;
 import org.springframework.util.Assert;
+import org.springframework.util.function.ThrowingSupplier;
 
 /**
  * A base class for registering {@link RequestMatcher}'s. For example, it might allow for
@@ -52,6 +54,8 @@ public abstract class AbstractRequestMatcherRegistry<C> {
 
 	private final Log logger = LogFactory.getLog(getClass());
 
+	private PathPatternRequestMatcher.Builder requestMatcherBuilder;
+
 	protected final void setApplicationContext(ApplicationContext context) {
 		this.context = context;
 	}
@@ -140,7 +144,7 @@ public abstract class AbstractRequestMatcherRegistry<C> {
 					+ "Spring Security, leaving out the leading slash will result in an exception.");
 		}
 		Assert.state(!this.anyRequestConfigured, "Can't configure requestMatchers after anyRequest");
-		PathPatternRequestMatcher.Builder builder = this.context.getBean(PathPatternRequestMatcher.Builder.class);
+		PathPatternRequestMatcher.Builder builder = getRequestMatcherBuilder();
 		List<RequestMatcher> matchers = new ArrayList<>();
 		for (String pattern : patterns) {
 			matchers.add(builder.matcher(method, pattern));
@@ -148,6 +152,23 @@ public abstract class AbstractRequestMatcherRegistry<C> {
 		return requestMatchers(matchers.toArray(new RequestMatcher[0]));
 	}
 
+	private PathPatternRequestMatcher.Builder getRequestMatcherBuilder() {
+		if (this.requestMatcherBuilder != null) {
+			return this.requestMatcherBuilder;
+		}
+		this.requestMatcherBuilder = this.context.getBeanProvider(PathPatternRequestMatcher.Builder.class)
+			.getIfUnique(() -> constructRequestMatcherBuilder(this.context));
+		return this.requestMatcherBuilder;
+	}
+
+	private PathPatternRequestMatcher.Builder constructRequestMatcherBuilder(ApplicationContext context) {
+		PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder = new PathPatternRequestMatcherBuilderFactoryBean();
+		requestMatcherBuilder.setApplicationContext(context);
+		requestMatcherBuilder.setBeanFactory(context.getAutowireCapableBeanFactory());
+		requestMatcherBuilder.setBeanName(requestMatcherBuilder.toString());
+		return ThrowingSupplier.of(requestMatcherBuilder::getObject).get();
+	}
+
 	private boolean anyPathsDontStartWithLeadingSlash(String... patterns) {
 		for (String pattern : patterns) {
 			if (!pattern.startsWith("/")) {

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

@@ -2058,7 +2058,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
 	 */
 	public HttpSecurity securityMatcher(String... patterns) {
 		List<RequestMatcher> matchers = new ArrayList<>();
-		PathPatternRequestMatcher.Builder builder = getContext().getBean(PathPatternRequestMatcher.Builder.class);
+		PathPatternRequestMatcher.Builder builder = getSharedObject(PathPatternRequestMatcher.Builder.class);
 		for (String pattern : patterns) {
 			matchers.add(builder.matcher(pattern));
 		}

+ 0 - 31
config/src/main/java/org/springframework/security/config/annotation/web/configuration/AuthorizationConfiguration.java

@@ -1,31 +0,0 @@
-/*
- * Copyright 2004-present 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.configuration;
-
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Fallback;
-import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
-
-class AuthorizationConfiguration {
-
-	@Bean
-	@Fallback
-	PathPatternRequestMatcherBuilderFactoryBean pathPatternRequestMatcherBuilder() {
-		return new PathPatternRequestMatcherBuilderFactoryBean();
-	}
-
-}

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

@@ -83,7 +83,7 @@ import org.springframework.security.web.SecurityFilterChain;
 @Target(ElementType.TYPE)
 @Documented
 @Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class,
-		HttpSecurityConfiguration.class, ObservationImportSelector.class, AuthorizationConfiguration.class })
+		HttpSecurityConfiguration.class, ObservationImportSelector.class })
 @EnableGlobalAuthentication
 public @interface EnableWebSecurity {
 

+ 12 - 0
config/src/main/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.java

@@ -38,12 +38,15 @@ import org.springframework.security.config.annotation.authentication.configurers
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
 import org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;
+import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.context.SecurityContextHolderStrategy;
 import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.crypto.factory.PasswordEncoderFactories;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
+import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
+import org.springframework.util.function.ThrowingSupplier;
 import org.springframework.web.accept.ContentNegotiationStrategy;
 import org.springframework.web.accept.HeaderContentNegotiationStrategy;
 import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@@ -161,9 +164,18 @@ class HttpSecurityConfiguration {
 		Map<Class<?>, Object> sharedObjects = new HashMap<>();
 		sharedObjects.put(ApplicationContext.class, this.context);
 		sharedObjects.put(ContentNegotiationStrategy.class, this.contentNegotiationStrategy);
+		sharedObjects.put(PathPatternRequestMatcher.Builder.class, constructRequestMatcherBuilder(this.context));
 		return sharedObjects;
 	}
 
+	private PathPatternRequestMatcher.Builder constructRequestMatcherBuilder(ApplicationContext context) {
+		PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder = new PathPatternRequestMatcherBuilderFactoryBean();
+		requestMatcherBuilder.setApplicationContext(context);
+		requestMatcherBuilder.setBeanFactory(context.getAutowireCapableBeanFactory());
+		requestMatcherBuilder.setBeanName(requestMatcherBuilder.toString());
+		return ThrowingSupplier.of(requestMatcherBuilder::getObject).get();
+	}
+
 	static class DefaultPasswordEncoderAuthenticationManagerBuilder extends AuthenticationManagerBuilder {
 
 		private PasswordEncoder defaultPasswordEncoder;

+ 1 - 8
config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractHttpConfigurer.java

@@ -39,8 +39,6 @@ public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T,
 
 	private SecurityContextHolderStrategy securityContextHolderStrategy;
 
-	private PathPatternRequestMatcher.Builder requestMatcherBuilder;
-
 	/**
 	 * Disables the {@link AbstractHttpConfigurer} by removing it. After doing so a fresh
 	 * version of the configuration can be applied.
@@ -69,12 +67,7 @@ public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T,
 	}
 
 	protected PathPatternRequestMatcher.Builder getRequestMatcherBuilder() {
-		if (this.requestMatcherBuilder != null) {
-			return this.requestMatcherBuilder;
-		}
-		ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);
-		this.requestMatcherBuilder = context.getBean(PathPatternRequestMatcher.Builder.class);
-		return this.requestMatcherBuilder;
+		return getBuilder().getSharedObject(PathPatternRequestMatcher.Builder.class);
 	}
 
 }

+ 18 - 2
config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryTests.java

@@ -17,6 +17,7 @@
 package org.springframework.security.config.annotation.web;
 
 import java.util.List;
+import java.util.stream.Stream;
 
 import jakarta.servlet.DispatcherType;
 import org.junit.jupiter.api.BeforeEach;
@@ -68,8 +69,8 @@ public class AbstractRequestMatcherRegistryTests {
 		ObjectProvider<ObjectPostProcessor<Object>> given = this.context.getBeanProvider(type);
 		given(given).willReturn(postProcessors);
 		given(postProcessors.getObject()).willReturn(NO_OP_OBJECT_POST_PROCESSOR);
-		given(this.context.getBean(PathPatternRequestMatcher.Builder.class))
-			.willReturn(PathPatternRequestMatcher.withDefaults());
+		given(this.context.getBeanProvider(PathPatternRequestMatcher.Builder.class))
+			.willReturn(new SingleObjectProvider<>(PathPatternRequestMatcher.withDefaults()));
 		this.matcherRegistry.setApplicationContext(this.context);
 	}
 
@@ -165,4 +166,19 @@ public class AbstractRequestMatcherRegistryTests {
 
 	}
 
+	private static final class SingleObjectProvider<T> implements ObjectProvider<T> {
+
+		private final T object;
+
+		private SingleObjectProvider(T object) {
+			this.object = object;
+		}
+
+		@Override
+		public Stream<T> stream() {
+			return Stream.of(this.object);
+		}
+
+	}
+
 }

+ 9 - 1
config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java

@@ -54,6 +54,7 @@ import org.springframework.security.config.core.GrantedAuthorityDefaults;
 import org.springframework.security.config.observation.SecurityObservationSettings;
 import org.springframework.security.config.test.SpringTestContext;
 import org.springframework.security.config.test.SpringTestContextExtension;
+import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.authority.AuthorityUtils;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
@@ -1051,12 +1052,19 @@ public class AuthorizeHttpRequestsConfigurerTests {
 	@EnableWebSecurity
 	static class ServletPathConfig {
 
+		@Bean
+		PathPatternRequestMatcherBuilderFactoryBean requesMatcherBuilder() {
+			PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
+			bean.setBasePath("/spring");
+			return bean;
+		}
+
 		@Bean
 		SecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
 			// @formatter:off
 			return http
 					.authorizeHttpRequests((authorize) -> authorize
-						.requestMatchers(builder.basePath("/spring").matcher("/")).hasRole("ADMIN")
+						.requestMatchers(builder.matcher("/")).hasRole("ADMIN")
 					)
 					.build();
 			// @formatter:on

+ 36 - 0
config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpSecurityRequestMatchersTests.java

@@ -32,6 +32,7 @@ import org.springframework.mock.web.MockHttpServletResponse;
 import org.springframework.mock.web.MockServletContext;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
 import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.provisioning.InMemoryUserDetailsManager;
 import org.springframework.security.web.FilterChainProxy;
@@ -157,6 +158,11 @@ public class HttpSecurityRequestMatchersTests {
 	@EnableWebMvc
 	static class MultiMvcMatcherInLambdaConfig {
 
+		@Bean
+		PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
+			return new PathPatternRequestMatcherBuilderFactoryBean();
+		}
+
 		@Bean
 		@Order(Ordered.HIGHEST_PRECEDENCE)
 		SecurityFilterChain first(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
@@ -204,6 +210,11 @@ public class HttpSecurityRequestMatchersTests {
 	@EnableWebMvc
 	static class MultiMvcMatcherConfig {
 
+		@Bean
+		PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
+			return new PathPatternRequestMatcherBuilderFactoryBean();
+		}
+
 		@Bean
 		@Order(Ordered.HIGHEST_PRECEDENCE)
 		SecurityFilterChain first(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
@@ -249,6 +260,11 @@ public class HttpSecurityRequestMatchersTests {
 	@EnableWebMvc
 	static class MvcMatcherConfig {
 
+		@Bean
+		PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
+			return new PathPatternRequestMatcherBuilderFactoryBean();
+		}
+
 		@Bean
 		SecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
 			// @formatter:off
@@ -283,6 +299,11 @@ public class HttpSecurityRequestMatchersTests {
 	@EnableWebMvc
 	static class RequestMatchersMvcMatcherConfig {
 
+		@Bean
+		PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
+			return new PathPatternRequestMatcherBuilderFactoryBean();
+		}
+
 		@Bean
 		SecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
 			// @formatter:off
@@ -318,6 +339,11 @@ public class HttpSecurityRequestMatchersTests {
 	@EnableWebMvc
 	static class RequestMatchersMvcMatcherInLambdaConfig {
 
+		@Bean
+		PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
+			return new PathPatternRequestMatcherBuilderFactoryBean();
+		}
+
 		@Bean
 		SecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
 			// @formatter:off
@@ -350,6 +376,11 @@ public class HttpSecurityRequestMatchersTests {
 	@EnableWebMvc
 	static class RequestMatchersMvcMatcherServeltPathConfig {
 
+		@Bean
+		PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
+			return new PathPatternRequestMatcherBuilderFactoryBean();
+		}
+
 		@Bean
 		SecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
 			// @formatter:off
@@ -386,6 +417,11 @@ public class HttpSecurityRequestMatchersTests {
 	@EnableWebMvc
 	static class RequestMatchersMvcMatcherServletPathInLambdaConfig {
 
+		@Bean
+		PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
+			return new PathPatternRequestMatcherBuilderFactoryBean();
+		}
+
 		@Bean
 		SecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
 			// @formatter:off

+ 19 - 6
config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpSecuritySecurityMatchersTests.java

@@ -32,6 +32,7 @@ import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
 import org.springframework.security.core.userdetails.User;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.core.userdetails.UserDetailsService;
@@ -354,14 +355,20 @@ public class HttpSecuritySecurityMatchersTests {
 	@Import(UsersConfig.class)
 	static class SecurityMatchersMvcMatcherServletPathConfig {
 
+		@Bean
+		PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
+			PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
+			bean.setBasePath("/spring");
+			return bean;
+		}
+
 		@Bean
 		SecurityFilterChain appSecurity(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
-			PathPatternRequestMatcher.Builder spring = builder.basePath("/spring");
 			// @formatter:off
 			http
 				.securityMatchers((security) -> security
-					.requestMatchers(spring.matcher("/path"))
-					.requestMatchers(spring.matcher("/never-match"))
+					.requestMatchers(builder.matcher("/path"))
+					.requestMatchers(builder.matcher("/never-match"))
 				)
 				.httpBasic(withDefaults())
 				.authorizeHttpRequests((authorize) -> authorize
@@ -388,14 +395,20 @@ public class HttpSecuritySecurityMatchersTests {
 	@Import(UsersConfig.class)
 	static class SecurityMatchersMvcMatcherServletPathInLambdaConfig {
 
+		@Bean
+		PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
+			PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
+			bean.setBasePath("/spring");
+			return bean;
+		}
+
 		@Bean
 		SecurityFilterChain appSecurity(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
-			PathPatternRequestMatcher.Builder spring = builder.basePath("/spring");
 			// @formatter:off
 			http
 				.securityMatchers((matchers) -> matchers
-					.requestMatchers(spring.matcher("/path"))
-					.requestMatchers(spring.matcher("/never-match"))
+					.requestMatchers(builder.matcher("/path"))
+					.requestMatchers(builder.matcher("/never-match"))
 				)
 				.httpBasic(withDefaults())
 				.authorizeHttpRequests((authorize) -> authorize