|
|
|
@ -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; |
|
|
|
} |
|
|
|
} |
|
|
|
|