package com.jilongda.applet.security;
|
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.security.authentication.*;
|
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
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.UsernameNotFoundException;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
|
import java.util.Objects;
|
|
/**
|
* 身份验证提供者--->登录验证逻辑
|
* <p>
|
* 不管是 implements AuthenticationProvider 还是 extends DaoAuthenticationProvider 区别不大
|
* <p>
|
* DaoAuthenticationProvider 实现了 AuthenticationProvider 类
|
* <p>
|
* 验证逻辑都是在 public Authentication authenticate(Authentication authentication) throws AuthenticationException {} 方法中
|
*
|
* @author xiaochen
|
* @date 2021-01-02 20:17
|
*/
|
@Slf4j
|
public class AuthenticationProvider extends DaoAuthenticationProvider {
|
|
private SysUserDetailsService loadUserDetailsService;
|
private PasswordEncoder passwordEncoder;
|
|
public AuthenticationProvider(SysUserDetailsService loadUserDetailsService, PasswordEncoder passwordEncoder) {
|
this.loadUserDetailsService = loadUserDetailsService;
|
this.passwordEncoder = passwordEncoder;
|
setUserDetailsService(loadUserDetailsService);
|
setPasswordEncoder(passwordEncoder);
|
}
|
|
@Override
|
public Authentication authenticate(Authentication authentication) {
|
// 可以在此处覆写整个登录认证逻辑
|
// [1] 获取 username 和 password
|
String userName = (String) authentication.getPrincipal();
|
String password = (String) authentication.getCredentials();
|
// [2] 使用用户名从数据库读取用户信息
|
SecurityUserDetails userDetails = this.loadUserDetailsService.loadUserByUsername(userName);
|
// 判断是否被封禁
|
// userDetails.setEnabled(userDetails.getStatus());
|
// [3] 检查用户信息
|
if (Objects.isNull(userDetails)) {
|
throw new UsernameNotFoundException("用户账户不存在");
|
}
|
else if (userDetails.getState()) {
|
throw new DisabledException("用户已被禁用或删除,请联系管理员");
|
}
|
// else if (!userDetails.isAccountNonExpired()) {
|
// throw new AccountExpiredException(userName + " 账号已过期");
|
// } else if (!userDetails.isAccountNonLocked()) {
|
// throw new LockedException(userName + " 账号已被锁定");
|
// } else if (!userDetails.isCredentialsNonExpired()) {
|
// throw new LockedException(userName + " 凭证已过期");
|
// }
|
// [4] 数据库用户的密码,一般都是加密过的
|
String encryptedPassword = userDetails.getPassword();
|
|
// 根据加密算法加密用户输入的密码,然后和数据库中保存的密码进行比较
|
if (!this.passwordEncoder.matches(password, encryptedPassword)) {
|
throw new BadCredentialsException("密码错误");
|
}
|
// [5] 成功登陆,把用户信息提交给 Spring Security
|
// 把 userDetails 作为 principal 的好处是可以放自定义的 UserDetails,这样可以存储更多有用的信息,而不只是 username,
|
// 默认只有 username,这里的密码使用数据库中保存的密码,而不是用户输入的明文密码,否则就暴露了密码的明文
|
// 不暴露密码
|
userDetails.setPassword(null);
|
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails.getUsername(), null, null);
|
// 设置详情
|
//String userInfo = JsonUtils.toJsonString(userDetails);
|
usernamePasswordAuthenticationToken.setDetails(userDetails);
|
return usernamePasswordAuthenticationToken;
|
}
|
|
@Override
|
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication)
|
throws AuthenticationException {
|
// 可以在此处覆写密码验证逻辑
|
super.additionalAuthenticationChecks(userDetails, authentication);
|
}
|
}
|