Avoid unnecessary instantiation of HttpSecurity when a SecurityFilterChain bean is provided

Signed-off-by: DingHao <dh.hiekn@gmail.com>
This commit is contained in:
DingHao 2025-01-08 10:18:19 +08:00 committed by Rob Winch
parent 6cfc372f70
commit c631afcf5b
2 changed files with 42 additions and 9 deletions

View File

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

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired; 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.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
@ -326,6 +328,12 @@ public class WebSecurityConfigurationTests {
.isInstanceOf(IllegalArgumentException.class); .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) { private void assertAnotherUserPermission(WebInvocationPrivilegeEvaluator privilegeEvaluator) {
Authentication anotherUser = new TestingAuthenticationToken("anotherUser", "password", "ROLE_ANOTHER"); Authentication anotherUser = new TestingAuthenticationToken("anotherUser", "password", "ROLE_ANOTHER");
assertThat(privilegeEvaluator.isAllowed("/user", anotherUser)).isFalse(); assertThat(privilegeEvaluator.isAllowed("/user", anotherUser)).isFalse();
@ -347,6 +355,32 @@ public class WebSecurityConfigurationTests {
assertThat(privilegeEvaluator.isAllowed("/another", user)).isTrue(); 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 @Configuration
@EnableWebSecurity @EnableWebSecurity
@Import(AuthenticationTestConfiguration.class) @Import(AuthenticationTestConfiguration.class)