无关风月
2024-12-24 f58421b4f18660e5387149128c82cfcdcb003cda
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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();
        log.info("用户数据查询======================================");
        // [2] 使用用户名从数据库读取用户信息
        SecurityUserDetails userDetails = this.loadUserDetailsService.loadUserByUsername(userName);
        log.info("用户数据查询======================================:{}", userDetails);
        // 判断是否被封禁
//        userDetails.setEnabled(userDetails.getState());
        // [3] 检查用户信息
        if (Objects.isNull(userDetails)) {
            throw new UsernameNotFoundException("用户账户不存在");
//        } else if (userDetails.isEnabled() || userDetails.getState()) {
//            throw new DisabledException(userName + " 用户已被禁用或删除,请联系管理员");
        } 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);
    }
}