Răsfoiți Sursa

:heavy_plus_sign: 添加登录错误次数锁定功能

smallchill 4 ani în urmă
părinte
comite
7ef2af6ad0

+ 33 - 0
src/main/java/org/springblade/common/cache/CacheNames.java

@@ -16,6 +16,8 @@
  */
 package org.springblade.common.cache;
 
+import org.springblade.core.tool.utils.StringPool;
+
 /**
  * 缓存名
  *
@@ -23,6 +25,37 @@ package org.springblade.common.cache;
  */
 public interface CacheNames {
 
+	/**
+	 * 返回拼接后的key
+	 *
+	 * @param cacheKey      缓存key
+	 * @param cacheKeyValue 缓存key值
+	 * @return tenantKey
+	 */
+	static String cacheKey(String cacheKey, String cacheKeyValue) {
+		return cacheKey.concat(cacheKeyValue);
+	}
+
+	/**
+	 * 返回租户格式的key
+	 *
+	 * @param tenantId      租户编号
+	 * @param cacheKey      缓存key
+	 * @param cacheKeyValue 缓存key值
+	 * @return tenantKey
+	 */
+	static String tenantKey(String tenantId, String cacheKey, String cacheKeyValue) {
+		return tenantId.concat(StringPool.COLON).concat(cacheKey).concat(cacheKeyValue);
+	}
+
+	/**
+	 * 验证码key
+	 */
 	String CAPTCHA_KEY = "blade:auth::blade:captcha:";
 
+	/**
+	 * 登录失败key
+	 */
+	String USER_FAIL_KEY = "blade:user::blade:fail:";
+
 }

+ 14 - 0
src/main/java/org/springblade/modules/auth/granter/CaptchaTokenGranter.java

@@ -35,6 +35,7 @@ import org.springblade.modules.system.service.IUserService;
 import org.springframework.stereotype.Component;
 
 import javax.servlet.http.HttpServletRequest;
+import java.time.Duration;
 
 /**
  * 验证码TokenGranter
@@ -46,6 +47,7 @@ import javax.servlet.http.HttpServletRequest;
 public class CaptchaTokenGranter implements ITokenGranter {
 
 	public static final String GRANT_TYPE = "captcha";
+	public static final Integer FAIL_COUNT = 5;
 
 	private final IUserService userService;
 	private final ITenantService tenantService;
@@ -67,6 +69,14 @@ public class CaptchaTokenGranter implements ITokenGranter {
 		String tenantId = tokenParameter.getArgs().getStr("tenantId");
 		String username = tokenParameter.getArgs().getStr("username");
 		String password = tokenParameter.getArgs().getStr("password");
+
+		// 判断登录是否锁定
+		// TODO 2.8.3版本将增加:1.参数管理读取配置 2.用户管理增加解封按钮
+		int cnt = Func.toInt(bladeRedis.get(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username)), 0);
+		if (cnt >= FAIL_COUNT) {
+			throw new ServiceException(TokenUtil.USER_HAS_TOO_MANY_FAILS);
+		}
+
 		UserInfo userInfo = null;
 		if (Func.isNoneBlank(username, password)) {
 			// 获取租户信息
@@ -85,6 +95,10 @@ public class CaptchaTokenGranter implements ITokenGranter {
 				userInfo = userService.userInfo(tenantId, username, DigestUtil.hex(password), UserEnum.OTHER);
 			}
 		}
+		if (userInfo == null || userInfo.getUser() == null) {
+			// 错误次数锁定
+			bladeRedis.setEx(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username), cnt + 1, Duration.ofMinutes(30));
+		}
 		return userInfo;
 	}
 

+ 18 - 0
src/main/java/org/springblade/modules/auth/granter/PasswordTokenGranter.java

@@ -17,7 +17,9 @@
 package org.springblade.modules.auth.granter;
 
 import lombok.AllArgsConstructor;
+import org.springblade.common.cache.CacheNames;
 import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.redis.cache.BladeRedis;
 import org.springblade.core.tool.utils.DigestUtil;
 import org.springblade.core.tool.utils.Func;
 import org.springblade.modules.auth.enums.UserEnum;
@@ -30,6 +32,8 @@ import org.springblade.modules.system.service.ITenantService;
 import org.springblade.modules.system.service.IUserService;
 import org.springframework.stereotype.Component;
 
+import java.time.Duration;
+
 /**
  * PasswordTokenGranter
  *
@@ -40,15 +44,25 @@ import org.springframework.stereotype.Component;
 public class PasswordTokenGranter implements ITokenGranter {
 
 	public static final String GRANT_TYPE = "password";
+	public static final Integer FAIL_COUNT = 5;
 
 	private final IUserService userService;
 	private final ITenantService tenantService;
+	private final BladeRedis bladeRedis;
 
 	@Override
 	public UserInfo grant(TokenParameter tokenParameter) {
 		String tenantId = tokenParameter.getArgs().getStr("tenantId");
 		String username = tokenParameter.getArgs().getStr("username");
 		String password = tokenParameter.getArgs().getStr("password");
+
+		// 判断登录是否锁定
+		// TODO 2.8.3版本将增加:1.参数管理读取配置 2.用户管理增加解封按钮
+		int cnt = Func.toInt(bladeRedis.get(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username)), 0);
+		if (cnt >= FAIL_COUNT) {
+			throw new ServiceException(TokenUtil.USER_HAS_TOO_MANY_FAILS);
+		}
+
 		UserInfo userInfo = null;
 		if (Func.isNoneBlank(username, password)) {
 			// 获取租户信息
@@ -67,6 +81,10 @@ public class PasswordTokenGranter implements ITokenGranter {
 				userInfo = userService.userInfo(tenantId, username, DigestUtil.hex(password), UserEnum.OTHER);
 			}
 		}
+		if (userInfo == null || userInfo.getUser() == null) {
+			// 错误次数锁定
+			bladeRedis.setEx(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, username), cnt + 1, Duration.ofMinutes(30));
+		}
 		return userInfo;
 	}
 

+ 1 - 0
src/main/java/org/springblade/modules/auth/utils/TokenUtil.java

@@ -53,6 +53,7 @@ public class TokenUtil {
 	public final static String USER_HAS_NO_ROLE = "未获得用户的角色信息";
 	public final static String USER_HAS_NO_TENANT = "未获得用户的租户信息";
 	public final static String USER_HAS_NO_TENANT_PERMISSION = "租户授权已过期,请联系管理员";
+	public final static String USER_HAS_TOO_MANY_FAILS = "登录错误次数过多,请稍后再试";
 	public final static String HEADER_KEY = "Authorization";
 	public final static String HEADER_PREFIX = "Basic ";
 	public final static String DEFAULT_AVATAR = "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png";