Pick Up SecurityContextHolderStrategy Bean

This commit provides the SecurityContextHolderStrategy bean to
ProviderManager instances that the HttpSecurity DSL constructs.

Issue gh-17862
This commit is contained in:
Josh Cummings 2025-08-22 16:24:25 -06:00
parent 8468c6a805
commit 44fef786aa
6 changed files with 45 additions and 2 deletions

View File

@ -18,10 +18,14 @@ package org.springframework.security.config.annotation.authentication.builders;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Stream;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.security.authentication.AuthenticationEventPublisher; import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider;
@ -37,6 +41,8 @@ import org.springframework.security.config.annotation.authentication.configurers
import org.springframework.security.config.annotation.authentication.configurers.userdetails.DaoAuthenticationConfigurer; import org.springframework.security.config.annotation.authentication.configurers.userdetails.DaoAuthenticationConfigurer;
import org.springframework.security.config.annotation.authentication.configurers.userdetails.UserDetailsAwareConfigurer; import org.springframework.security.config.annotation.authentication.configurers.userdetails.UserDetailsAwareConfigurer;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
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.core.userdetails.UserDetailsService;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -235,6 +241,10 @@ public class AuthenticationManagerBuilder
if (this.eventPublisher != null) { if (this.eventPublisher != null) {
providerManager.setAuthenticationEventPublisher(this.eventPublisher); providerManager.setAuthenticationEventPublisher(this.eventPublisher);
} }
SecurityContextHolderStrategy securityContextHolderStrategy = getBeanProvider(
SecurityContextHolderStrategy.class)
.getIfUnique(SecurityContextHolder::getContextHolderStrategy);
providerManager.setSecurityContextHolderStrategy(securityContextHolderStrategy);
providerManager = postProcess(providerManager); providerManager = postProcess(providerManager);
return providerManager; return providerManager;
} }
@ -283,4 +293,24 @@ public class AuthenticationManagerBuilder
return configurer; return configurer;
} }
private <C> ObjectProvider<C> getBeanProvider(Class<C> clazz) {
BeanFactory beanFactory = getSharedObject(BeanFactory.class);
return (beanFactory != null) ? beanFactory.getBeanProvider(clazz) : new SingleObjectProvider<>(null);
}
private static final class SingleObjectProvider<O> implements ObjectProvider<O> {
private final @Nullable O object;
private SingleObjectProvider(@Nullable O object) {
this.object = object;
}
@Override
public Stream<O> stream() {
return Stream.ofNullable(this.object);
}
}
} }

View File

@ -27,6 +27,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.ProxyFactoryBean; import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.aop.target.LazyInitTargetSource; import org.springframework.aop.target.LazyInitTargetSource;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@ -83,6 +84,7 @@ public class AuthenticationConfiguration {
AuthenticationEventPublisher authenticationEventPublisher = getAuthenticationEventPublisher(context); AuthenticationEventPublisher authenticationEventPublisher = getAuthenticationEventPublisher(context);
DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder( DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(
objectPostProcessor, defaultPasswordEncoder); objectPostProcessor, defaultPasswordEncoder);
result.setSharedObject(BeanFactory.class, this.applicationContext);
if (authenticationEventPublisher != null) { if (authenticationEventPublisher != null) {
result.authenticationEventPublisher(authenticationEventPublisher); result.authenticationEventPublisher(authenticationEventPublisher);
} }

View File

@ -318,6 +318,7 @@ public class GlobalMethodSecurityConfiguration implements ImportAware, SmartInit
.postProcess(new DefaultAuthenticationEventPublisher()); .postProcess(new DefaultAuthenticationEventPublisher());
this.auth = new AuthenticationManagerBuilder(this.objectPostProcessor); this.auth = new AuthenticationManagerBuilder(this.objectPostProcessor);
this.auth.authenticationEventPublisher(eventPublisher); this.auth.authenticationEventPublisher(eventPublisher);
this.auth.setSharedObject(BeanFactory.class, this.context);
configure(this.auth); configure(this.auth);
this.authenticationManager = (this.disableAuthenticationRegistry) this.authenticationManager = (this.disableAuthenticationRegistry)
? getAuthenticationConfiguration().getAuthenticationManager() : this.auth.build(); ? getAuthenticationConfiguration().getAuthenticationManager() : this.auth.build();

View File

@ -21,6 +21,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@ -116,6 +117,7 @@ class HttpSecurityConfiguration {
LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(this.context); LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(this.context);
AuthenticationManagerBuilder authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder( AuthenticationManagerBuilder authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(
this.objectPostProcessor, passwordEncoder); this.objectPostProcessor, passwordEncoder);
authenticationBuilder.setSharedObject(BeanFactory.class, this.context);
authenticationBuilder.parentAuthenticationManager(authenticationManager()); authenticationBuilder.parentAuthenticationManager(authenticationManager());
authenticationBuilder.authenticationEventPublisher(getAuthenticationEventPublisher()); authenticationBuilder.authenticationEventPublisher(getAuthenticationEventPublisher());
HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects()); HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects());

View File

@ -162,8 +162,10 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
WebAuthnRelyingPartyOperations rpOperations = webAuthnRelyingPartyOperations(userEntities, userCredentials); WebAuthnRelyingPartyOperations rpOperations = webAuthnRelyingPartyOperations(userEntities, userCredentials);
PublicKeyCredentialCreationOptionsRepository creationOptionsRepository = creationOptionsRepository(); PublicKeyCredentialCreationOptionsRepository creationOptionsRepository = creationOptionsRepository();
WebAuthnAuthenticationFilter webAuthnAuthnFilter = new WebAuthnAuthenticationFilter(); WebAuthnAuthenticationFilter webAuthnAuthnFilter = new WebAuthnAuthenticationFilter();
webAuthnAuthnFilter.setAuthenticationManager( ProviderManager manager = new ProviderManager(
new ProviderManager(new WebAuthnAuthenticationProvider(rpOperations, userDetailsService))); new WebAuthnAuthenticationProvider(rpOperations, userDetailsService));
manager.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
webAuthnAuthnFilter.setAuthenticationManager(manager);
WebAuthnRegistrationFilter webAuthnRegistrationFilter = new WebAuthnRegistrationFilter(userCredentials, WebAuthnRegistrationFilter webAuthnRegistrationFilter = new WebAuthnRegistrationFilter(userCredentials,
rpOperations); rpOperations);
PublicKeyCredentialCreationOptionsFilter creationOptionsFilter = new PublicKeyCredentialCreationOptionsFilter( PublicKeyCredentialCreationOptionsFilter creationOptionsFilter = new PublicKeyCredentialCreationOptionsFilter(

View File

@ -30,6 +30,8 @@ import org.springframework.security.authentication.ObservationAuthenticationMana
import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.BeanIds; import org.springframework.security.config.BeanIds;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
@ -72,6 +74,10 @@ public class AuthenticationManagerFactoryBean implements FactoryBean<Authenticat
} }
provider.afterPropertiesSet(); provider.afterPropertiesSet();
ProviderManager manager = new ProviderManager(Arrays.asList(provider)); ProviderManager manager = new ProviderManager(Arrays.asList(provider));
SecurityContextHolderStrategy securityContextHolderStrategy = this.bf
.getBeanProvider(SecurityContextHolderStrategy.class)
.getIfUnique(SecurityContextHolder::getContextHolderStrategy);
manager.setSecurityContextHolderStrategy(securityContextHolderStrategy);
if (this.observationRegistry.isNoop()) { if (this.observationRegistry.isNoop()) {
return manager; return manager;
} }