From d737a229856becf58cbc7b61b34426caf9056553 Mon Sep 17 00:00:00 2001
From: guoshilong <123456>
Date: Tue, 25 Apr 2023 15:27:25 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/jtdev' into jtdev

---
 skjcmanager/skjcmanager-auth/src/main/java/cn/gistack/auth/config/MyAuthenticationProvider.java |  120 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 120 insertions(+), 0 deletions(-)

diff --git a/skjcmanager/skjcmanager-auth/src/main/java/cn/gistack/auth/config/MyAuthenticationProvider.java b/skjcmanager/skjcmanager-auth/src/main/java/cn/gistack/auth/config/MyAuthenticationProvider.java
new file mode 100644
index 0000000..ac0ac7e
--- /dev/null
+++ b/skjcmanager/skjcmanager-auth/src/main/java/cn/gistack/auth/config/MyAuthenticationProvider.java
@@ -0,0 +1,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.security.oauth2.common.exceptions.UserDeniedAuthorizationException;
+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 UserDeniedAuthorizationException(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));
+	}
+}

--
Gitblit v1.9.3