zhongrj
2023-04-24 90389c7d1ff29b39cc8224fbc070b675d918e0f6
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package cn.gistack.auth.config;
 
import cn.gistack.auth.utils.TokenUtil;
import cn.gistack.common.cache.CacheNames;
import com.alibaba.nacos.common.utils.StringUtils;
import lombok.SneakyThrows;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.tool.utils.DigestUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.WebUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
 
/**
 * 自定义安全认证
 * @author zhongrj
 * @time 2022-9-2
 */
@Component
public class MyAuthenticationProvider extends DaoAuthenticationProvider {
 
    /**
     * 免密常量
     */
    public static final String CUSTOM_LOGIN_SMS = "CUSTOM_LOGIN_SMS";
 
    @Autowired
    private BladeRedis bladeRedis;
 
    /**
     * 设置 userDetailsService
     * @param userDetailsService
     */
    public MyAuthenticationProvider(UserDetailsService userDetailsService) {
        super();
        setUserDetailsService(userDetailsService);
    }
 
    /**
     * 自定义处理密码校验逻辑
     * @param userDetails 用户信息
     * @param authentication 认证对象信息
     * @throws AuthenticationException 认证异常对象信息
     */
    @SneakyThrows
    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        if (authentication.getCredentials() == null) {
            this.logger.debug("Authentication failed: no credentials provided");
            throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        } else {
            String password = authentication.getCredentials().toString();
            // 手机号登录时不验证密码
            if(!CUSTOM_LOGIN_SMS.equals(password)){
                HttpServletRequest request = WebUtil.getRequest();
                String grantType = request.getParameter(TokenUtil.GRANT_TYPE_KEY);
                // 获取租户ID
                String headerTenant = request.getHeader(TokenUtil.TENANT_HEADER_KEY);
                String paramTenant = request.getParameter(TokenUtil.TENANT_PARAM_KEY);
                // 指定租户ID
                String tenantId = StringUtils.isBlank(headerTenant) ? paramTenant : headerTenant;
                String detailsPassword = userDetails.getPassword().substring(7);
                if (!detailsPassword.equals(DigestUtil.hex(password))) {
 
                    int count = getFailCount(tenantId, userDetails.getUsername());
                    // 用户存在但密码错误,超过次数则锁定账号
                    if (grantType != null && !grantType.equals(TokenUtil.REFRESH_TOKEN_KEY)) {
                        setFailCount(tenantId, userDetails.getUsername(), count);
                        throw new UsernameNotFoundException(TokenUtil.USER_NOT_FOUND);
                    }
                    this.logger.debug("Failed to authenticate since password does not match stored value");
                    throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
                }
                // 成功则清除登录错误次数
                delFailCount(tenantId, userDetails.getUsername());
            }
        }
    }
 
    /**
     * 获取账号错误次数
     *
     * @param tenantId 租户id
     * @param username 账号
     * @return int
     */
    private int getFailCount(String tenantId, String username) {
        return Func.toInt(bladeRedis.get(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username)), 0);
    }
 
    /**
     * 设置账号错误次数
     *
     * @param tenantId 租户id
     * @param username 账号
     * @param count    次数
     */
    private void setFailCount(String tenantId, String username, int count) {
        bladeRedis.setEx(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username), count + 1, Duration.ofMinutes(30));
    }
 
    /**
     * 清空账号错误次数
     *
     * @param tenantId 租户id
     * @param username 账号
     */
    private void delFailCount(String tenantId, String username) {
        bladeRedis.del(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username));
    }
}