|
|
|
package com.zhonglai.luhui.security.service;
|
|
|
|
|
|
|
|
import org.springframework.security.authentication.BadCredentialsException;
|
|
|
|
import org.springframework.security.authentication.InternalAuthenticationServiceException;
|
|
|
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
|
|
|
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
|
|
|
|
import org.springframework.security.core.Authentication;
|
|
|
|
import org.springframework.security.core.AuthenticationException;
|
|
|
|
import org.springframework.security.core.userdetails.UserDetails;
|
|
|
|
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
|
|
|
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
|
|
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
|
|
|
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
|
|
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
|
|
|
import org.springframework.util.Assert;
|
|
|
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
public class CustomAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The plaintext password used to perform PasswordEncoder#matches(CharSequence,
|
|
|
|
* String)} on when the user is not found to avoid SEC-2056.
|
|
|
|
*/
|
|
|
|
private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";
|
|
|
|
|
|
|
|
private PasswordEncoder passwordEncoder;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The password used to perform {@link PasswordEncoder#matches(CharSequence, String)}
|
|
|
|
* on when the user is not found to avoid SEC-2056. This is necessary, because some
|
|
|
|
* {@link PasswordEncoder} implementations will short circuit if the password is not
|
|
|
|
* in a valid format.
|
|
|
|
*/
|
|
|
|
private volatile String userNotFoundEncodedPassword;
|
|
|
|
|
|
|
|
private CustomUserDetailsService userDetailsService;
|
|
|
|
|
|
|
|
private UserDetailsPasswordService userDetailsPasswordService;
|
|
|
|
|
|
|
|
public CustomAuthenticationProvider() {
|
|
|
|
setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@SuppressWarnings("deprecation")
|
|
|
|
protected void additionalAuthenticationChecks(UserDetails userDetails,
|
|
|
|
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
|
|
|
|
if (authentication.getCredentials() == null) {
|
|
|
|
this.logger.debug("Failed to authenticate since no credentials provided");
|
|
|
|
throw new BadCredentialsException(this.messages
|
|
|
|
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
|
|
|
|
}
|
|
|
|
String presentedPassword = authentication.getCredentials().toString();
|
|
|
|
if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
|
|
|
|
this.logger.debug("Failed to authenticate since password does not match stored value");
|
|
|
|
throw new BadCredentialsException(this.messages
|
|
|
|
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void doAfterPropertiesSet() {
|
|
|
|
Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
|
|
|
|
throws AuthenticationException {
|
|
|
|
this.prepareTimingAttackProtection();
|
|
|
|
Map<String,String> map = (Map<String, String>) authentication.getDetails(); // 自定义添加
|
|
|
|
try {
|
|
|
|
String userType = map.get("userType"); // 自定义添加
|
|
|
|
UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username, userType); // 自定义添加userType参数
|
|
|
|
if (loadedUser == null) {
|
|
|
|
throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
|
|
|
|
} else {
|
|
|
|
return loadedUser;
|
|
|
|
}
|
|
|
|
} catch (UsernameNotFoundException var4) {
|
|
|
|
this.mitigateAgainstTimingAttack(authentication);
|
|
|
|
throw var4;
|
|
|
|
} catch (InternalAuthenticationServiceException var5) {
|
|
|
|
throw var5;
|
|
|
|
} catch (Exception var6) {
|
|
|
|
throw new InternalAuthenticationServiceException(var6.getMessage(), var6);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected Authentication createSuccessAuthentication(Object principal, Authentication authentication,
|
|
|
|
UserDetails user) {
|
|
|
|
boolean upgradeEncoding = this.userDetailsPasswordService != null
|
|
|
|
&& this.passwordEncoder.upgradeEncoding(user.getPassword());
|
|
|
|
if (upgradeEncoding) {
|
|
|
|
String presentedPassword = authentication.getCredentials().toString();
|
|
|
|
String newPassword = this.passwordEncoder.encode(presentedPassword);
|
|
|
|
user = this.userDetailsPasswordService.updatePassword(user, newPassword);
|
|
|
|
}
|
|
|
|
return super.createSuccessAuthentication(principal, authentication, user);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void prepareTimingAttackProtection() {
|
|
|
|
if (this.userNotFoundEncodedPassword == null) {
|
|
|
|
this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) {
|
|
|
|
if (authentication.getCredentials() != null) {
|
|
|
|
String presentedPassword = authentication.getCredentials().toString();
|
|
|
|
this.passwordEncoder.matches(presentedPassword, this.userNotFoundEncodedPassword);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the PasswordEncoder instance to be used to encode and validate passwords. If
|
|
|
|
* not set, the password will be compared using
|
|
|
|
* {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}
|
|
|
|
* @param passwordEncoder must be an instance of one of the {@code PasswordEncoder}
|
|
|
|
* types.
|
|
|
|
*/
|
|
|
|
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
|
|
|
|
Assert.notNull(passwordEncoder, "passwordEncoder cannot be null");
|
|
|
|
this.passwordEncoder = passwordEncoder;
|
|
|
|
this.userNotFoundEncodedPassword = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected PasswordEncoder getPasswordEncoder() {
|
|
|
|
return this.passwordEncoder;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setUserDetailsService(CustomUserDetailsService userDetailsService) {
|
|
|
|
this.userDetailsService = userDetailsService;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected CustomUserDetailsService getUserDetailsService() {
|
|
|
|
return this.userDetailsService;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setUserDetailsPasswordService(UserDetailsPasswordService userDetailsPasswordService) {
|
|
|
|
this.userDetailsPasswordService = userDetailsPasswordService;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
...
|
...
|
|