Переглянути джерело

Avoid unnecessary instantiation of HttpSecurity when a SecurityFilterChain bean is provided

Signed-off-by: DingHao <dh.hiekn@gmail.com>
DingHao 7 місяців тому
батько
коміт
c631afcf5b

+ 7 - 8
config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.java

@@ -22,6 +22,7 @@ import java.util.Map;
 
 import jakarta.servlet.Filter;
 
+import org.springframework.beans.factory.ObjectProvider;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@@ -74,9 +75,6 @@ public class WebSecurityConfiguration implements ImportAware {
 
 	private List<WebSecurityCustomizer> webSecurityCustomizers = Collections.emptyList();
 
-	@Autowired(required = false)
-	private HttpSecurity httpSecurity;
-
 	@Bean
 	public static DelegatingApplicationListener delegatingApplicationListener() {
 		return new DelegatingApplicationListener();
@@ -94,14 +92,15 @@ public class WebSecurityConfiguration implements ImportAware {
 	 * @throws Exception
 	 */
 	@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
-	public Filter springSecurityFilterChain() throws Exception {
+	public Filter springSecurityFilterChain(ObjectProvider<HttpSecurity> provider) throws Exception {
 		boolean hasFilterChain = !this.securityFilterChains.isEmpty();
 		if (!hasFilterChain) {
 			this.webSecurity.addSecurityFilterChainBuilder(() -> {
-				this.httpSecurity.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated());
-				this.httpSecurity.formLogin(Customizer.withDefaults());
-				this.httpSecurity.httpBasic(Customizer.withDefaults());
-				return this.httpSecurity.build();
+				HttpSecurity httpSecurity = provider.getObject();
+				httpSecurity.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated());
+				httpSecurity.formLogin(Customizer.withDefaults());
+				httpSecurity.httpBasic(Customizer.withDefaults());
+				return httpSecurity.build();
 			});
 		}
 		for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {

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

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2024 the original author or authors.
+ * Copyright 2002-2025 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.
@@ -27,8 +27,10 @@ import jakarta.servlet.http.HttpServletRequest;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 
+import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.BeanCreationException;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.BeanPostProcessor;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
@@ -326,6 +328,12 @@ public class WebSecurityConfigurationTests {
 			.isInstanceOf(IllegalArgumentException.class);
 	}
 
+	@Test
+	public void avoidUnnecessaryHttpSecurityInstantiationWhenProvideOneSecurityFilterChain() {
+		this.spring.register(SecurityFilterChainConfig.class).autowire();
+		assertThat(this.spring.getContext().getBean(CustomBeanPostProcessor.class).instantiationCount).isEqualTo(1);
+	}
+
 	private void assertAnotherUserPermission(WebInvocationPrivilegeEvaluator privilegeEvaluator) {
 		Authentication anotherUser = new TestingAuthenticationToken("anotherUser", "password", "ROLE_ANOTHER");
 		assertThat(privilegeEvaluator.isAllowed("/user", anotherUser)).isFalse();
@@ -347,6 +355,32 @@ public class WebSecurityConfigurationTests {
 		assertThat(privilegeEvaluator.isAllowed("/another", user)).isTrue();
 	}
 
+	@Configuration
+	@EnableWebSecurity
+	@Import(CustomBeanPostProcessor.class)
+	static class SecurityFilterChainConfig {
+
+		@Bean
+		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+			return http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated()).build();
+		}
+
+	}
+
+	static class CustomBeanPostProcessor implements BeanPostProcessor {
+
+		int instantiationCount = 0;
+
+		@Override
+		public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+			if (bean instanceof HttpSecurity) {
+				this.instantiationCount++;
+			}
+			return bean;
+		}
+
+	}
+
 	@Configuration
 	@EnableWebSecurity
 	@Import(AuthenticationTestConfiguration.class)