Add UserDetailsService Constructor

Closes gh-15973
This commit is contained in:
Josh Cummings 2024-10-23 18:06:48 -06:00
parent 8bac87fb20
commit 9e2547019b
No known key found for this signature in database
GPG Key ID: A306A51F43B8E5A5
5 changed files with 36 additions and 25 deletions

View File

@ -96,14 +96,10 @@ class InitializeUserDetailsBeanManagerConfigurer extends GlobalAuthenticationCon
PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class); PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class); UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
CompromisedPasswordChecker passwordChecker = getBeanOrNull(CompromisedPasswordChecker.class); CompromisedPasswordChecker passwordChecker = getBeanOrNull(CompromisedPasswordChecker.class);
DaoAuthenticationProvider provider; DaoAuthenticationProvider provider = new DaoAuthenticationProvider(userDetailsService);
if (passwordEncoder != null) { if (passwordEncoder != null) {
provider = new DaoAuthenticationProvider(passwordEncoder); provider.setPasswordEncoder(passwordEncoder);
} }
else {
provider = new DaoAuthenticationProvider();
}
provider.setUserDetailsService(userDetailsService);
if (passwordManager != null) { if (passwordManager != null) {
provider.setUserDetailsPasswordService(passwordManager); provider.setUserDetailsPasswordService(passwordManager);
} }

View File

@ -36,7 +36,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
public abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, C extends AbstractDaoAuthenticationConfigurer<B, C, U>, U extends UserDetailsService> public abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, C extends AbstractDaoAuthenticationConfigurer<B, C, U>, U extends UserDetailsService>
extends UserDetailsAwareConfigurer<B, U> { extends UserDetailsAwareConfigurer<B, U> {
private DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); private DaoAuthenticationProvider provider;
private final U userDetailsService; private final U userDetailsService;
@ -46,7 +46,7 @@ public abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderMana
*/ */
AbstractDaoAuthenticationConfigurer(U userDetailsService) { AbstractDaoAuthenticationConfigurer(U userDetailsService) {
this.userDetailsService = userDetailsService; this.userDetailsService = userDetailsService;
this.provider.setUserDetailsService(userDetailsService); this.provider = new DaoAuthenticationProvider(userDetailsService);
if (userDetailsService instanceof UserDetailsPasswordService) { if (userDetailsService instanceof UserDetailsPasswordService) {
this.provider.setUserDetailsPasswordService((UserDetailsPasswordService) userDetailsService); this.provider.setUserDetailsPasswordService((UserDetailsPasswordService) userDetailsService);
} }

View File

@ -66,8 +66,7 @@ public class AuthenticationManagerFactoryBean implements FactoryBean<Authenticat
if (uds == null) { if (uds == null) {
throw new NoSuchBeanDefinitionException(BeanIds.AUTHENTICATION_MANAGER, MISSING_BEAN_ERROR_MESSAGE); throw new NoSuchBeanDefinitionException(BeanIds.AUTHENTICATION_MANAGER, MISSING_BEAN_ERROR_MESSAGE);
} }
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); DaoAuthenticationProvider provider = new DaoAuthenticationProvider(uds);
provider.setUserDetailsService(uds);
PasswordEncoder passwordEncoder = this.bf.getBeanProvider(PasswordEncoder.class).getIfUnique(); PasswordEncoder passwordEncoder = this.bf.getBeanProvider(PasswordEncoder.class).getIfUnique();
if (passwordEncoder != null) { if (passwordEncoder != null) {
provider.setPasswordEncoder(passwordEncoder); provider.setPasswordEncoder(passwordEncoder);

View File

@ -16,6 +16,8 @@
package org.springframework.security.authentication.dao; package org.springframework.security.authentication.dao;
import java.util.function.Supplier;
import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException; import org.springframework.security.authentication.InternalAuthenticationServiceException;
@ -31,6 +33,7 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.function.SingletonSupplier;
/** /**
* An {@link AuthenticationProvider} implementation that retrieves user details from a * An {@link AuthenticationProvider} implementation that retrieves user details from a
@ -48,7 +51,8 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
*/ */
private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword"; private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";
private PasswordEncoder passwordEncoder; private Supplier<PasswordEncoder> passwordEncoder = SingletonSupplier
.of(PasswordEncoderFactories::createDelegatingPasswordEncoder);
/** /**
* The password used to perform {@link PasswordEncoder#matches(CharSequence, String)} * The password used to perform {@link PasswordEncoder#matches(CharSequence, String)}
@ -64,15 +68,25 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
private CompromisedPasswordChecker compromisedPasswordChecker; private CompromisedPasswordChecker compromisedPasswordChecker;
/**
* @deprecated Please provide the {@link UserDetailsService} in the constructor
*/
@Deprecated
public DaoAuthenticationProvider() { public DaoAuthenticationProvider() {
this(PasswordEncoderFactories.createDelegatingPasswordEncoder()); }
public DaoAuthenticationProvider(UserDetailsService userDetailsService) {
setUserDetailsService(userDetailsService);
} }
/** /**
* Creates a new instance using the provided {@link PasswordEncoder} * Creates a new instance using the provided {@link PasswordEncoder}
* @param passwordEncoder the {@link PasswordEncoder} to use. Cannot be null. * @param passwordEncoder the {@link PasswordEncoder} to use. Cannot be null.
* @since 6.0.3 * @since 6.0.3
* @deprecated Please provide the {@link UserDetailsService} in the constructor
* followed by {@link #setPasswordEncoder(PasswordEncoder)} instead
*/ */
@Deprecated
public DaoAuthenticationProvider(PasswordEncoder passwordEncoder) { public DaoAuthenticationProvider(PasswordEncoder passwordEncoder) {
setPasswordEncoder(passwordEncoder); setPasswordEncoder(passwordEncoder);
} }
@ -87,7 +101,7 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
} }
String presentedPassword = authentication.getCredentials().toString(); String presentedPassword = authentication.getCredentials().toString();
if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) { if (!this.passwordEncoder.get().matches(presentedPassword, userDetails.getPassword())) {
this.logger.debug("Failed to authenticate since password does not match stored value"); this.logger.debug("Failed to authenticate since password does not match stored value");
throw new BadCredentialsException(this.messages throw new BadCredentialsException(this.messages
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
@ -133,9 +147,9 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
throw new CompromisedPasswordException("The provided password is compromised, please change your password"); throw new CompromisedPasswordException("The provided password is compromised, please change your password");
} }
boolean upgradeEncoding = this.userDetailsPasswordService != null boolean upgradeEncoding = this.userDetailsPasswordService != null
&& this.passwordEncoder.upgradeEncoding(user.getPassword()); && this.passwordEncoder.get().upgradeEncoding(user.getPassword());
if (upgradeEncoding) { if (upgradeEncoding) {
String newPassword = this.passwordEncoder.encode(presentedPassword); String newPassword = this.passwordEncoder.get().encode(presentedPassword);
user = this.userDetailsPasswordService.updatePassword(user, newPassword); user = this.userDetailsPasswordService.updatePassword(user, newPassword);
} }
return super.createSuccessAuthentication(principal, authentication, user); return super.createSuccessAuthentication(principal, authentication, user);
@ -143,14 +157,14 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
private void prepareTimingAttackProtection() { private void prepareTimingAttackProtection() {
if (this.userNotFoundEncodedPassword == null) { if (this.userNotFoundEncodedPassword == null) {
this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD); this.userNotFoundEncodedPassword = this.passwordEncoder.get().encode(USER_NOT_FOUND_PASSWORD);
} }
} }
private void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) { private void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) {
if (authentication.getCredentials() != null) { if (authentication.getCredentials() != null) {
String presentedPassword = authentication.getCredentials().toString(); String presentedPassword = authentication.getCredentials().toString();
this.passwordEncoder.matches(presentedPassword, this.userNotFoundEncodedPassword); this.passwordEncoder.get().matches(presentedPassword, this.userNotFoundEncodedPassword);
} }
} }
@ -163,14 +177,19 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
*/ */
public void setPasswordEncoder(PasswordEncoder passwordEncoder) { public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
Assert.notNull(passwordEncoder, "passwordEncoder cannot be null"); Assert.notNull(passwordEncoder, "passwordEncoder cannot be null");
this.passwordEncoder = passwordEncoder; this.passwordEncoder = () -> passwordEncoder;
this.userNotFoundEncodedPassword = null; this.userNotFoundEncodedPassword = null;
} }
protected PasswordEncoder getPasswordEncoder() { protected PasswordEncoder getPasswordEncoder() {
return this.passwordEncoder; return this.passwordEncoder.get();
} }
/**
* @param userDetailsService
* @deprecated Please provide the {@link UserDetailsService} in the constructor
*/
@Deprecated
public void setUserDetailsService(UserDetailsService userDetailsService) { public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService; this.userDetailsService = userDetailsService;
} }

View File

@ -148,8 +148,7 @@ public class SecurityConfig {
public AuthenticationManager authenticationManager( public AuthenticationManager authenticationManager(
UserDetailsService userDetailsService, UserDetailsService userDetailsService,
PasswordEncoder passwordEncoder) { PasswordEncoder passwordEncoder) {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(userDetailsService);
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder); authenticationProvider.setPasswordEncoder(passwordEncoder);
return new ProviderManager(authenticationProvider); return new ProviderManager(authenticationProvider);
@ -229,8 +228,7 @@ class SecurityConfig {
fun authenticationManager( fun authenticationManager(
userDetailsService: UserDetailsService, userDetailsService: UserDetailsService,
passwordEncoder: PasswordEncoder): AuthenticationManager { passwordEncoder: PasswordEncoder): AuthenticationManager {
val authenticationProvider = DaoAuthenticationProvider() val authenticationProvider = DaoAuthenticationProvider(userDetailsService)
authenticationProvider.setUserDetailsService(userDetailsService)
authenticationProvider.setPasswordEncoder(passwordEncoder) authenticationProvider.setPasswordEncoder(passwordEncoder)
return ProviderManager(authenticationProvider) return ProviderManager(authenticationProvider)
@ -501,8 +499,7 @@ class SecurityConfig {
@Bean @Bean
fun authenticationManager(): AuthenticationManager { fun authenticationManager(): AuthenticationManager {
val authenticationProvider = DaoAuthenticationProvider() val authenticationProvider = DaoAuthenticationProvider(userDetailsService())
authenticationProvider.setUserDetailsService(userDetailsService())
authenticationProvider.setPasswordEncoder(passwordEncoder()) authenticationProvider.setPasswordEncoder(passwordEncoder())
val providerManager = ProviderManager(authenticationProvider) val providerManager = ProviderManager(authenticationProvider)