From 32976708e984d62df593e5025fc9049febe9cbe2 Mon Sep 17 00:00:00 2001
From: xieb <vip_xiaobin810@163.com>
Date: Tue, 12 Dec 2023 15:15:22 +0800
Subject: [PATCH] 手机验证码登录

---
 src/main/java/org/springblade/modules/auth/endpoint/BladeTokenEndPoint.java    |   12 +++
 src/main/java/org/springblade/modules/system/service/impl/UserServiceImpl.java |   11 +++
 src/main/java/org/springblade/modules/auth/granter/SmsCodeTokenGranter.java    |  111 +++++++++++++++++++++++++++++++++++++
 src/main/java/org/springblade/modules/auth/provider/TokenGranterBuilder.java   |    6 -
 src/main/java/org/springblade/modules/system/service/IUserService.java         |    4 +
 5 files changed, 138 insertions(+), 6 deletions(-)

diff --git a/src/main/java/org/springblade/modules/auth/endpoint/BladeTokenEndPoint.java b/src/main/java/org/springblade/modules/auth/endpoint/BladeTokenEndPoint.java
index 99eea8f..21075c4 100644
--- a/src/main/java/org/springblade/modules/auth/endpoint/BladeTokenEndPoint.java
+++ b/src/main/java/org/springblade/modules/auth/endpoint/BladeTokenEndPoint.java
@@ -38,6 +38,7 @@
 import org.springblade.core.tool.utils.RandomType;
 import org.springblade.core.tool.utils.StringUtil;
 import org.springblade.core.tool.utils.WebUtil;
+import org.springblade.modules.auth.granter.SmsCodeTokenGranter;
 import org.springblade.modules.auth.provider.ITokenGranter;
 import org.springblade.modules.auth.provider.TokenGranterBuilder;
 import org.springblade.modules.auth.provider.TokenParameter;
@@ -75,6 +76,8 @@
 	public Kv token(@ApiParam(value = "租户ID", required = true) @RequestParam String tenantId,
 					@ApiParam(value = "账号", required = true) @RequestParam(required = false) String username,
 					@ApiParam(value = "密码", required = true) @RequestParam(required = false) String password,
+					@ApiParam(value = "手机号", required = true) @RequestParam(required = false) String phone,
+					@ApiParam(value = "手机验证码", required = true) @RequestParam(required = false) String code,
 					@ApiIgnore @RequestHeader(name = TokenUtil.DEPT_HEADER_KEY, required = false) String deptId,
 					@ApiIgnore @RequestHeader(name = TokenUtil.ROLE_HEADER_KEY, required = false) String roleId) {
 
@@ -92,13 +95,18 @@
 			.set("grantType", grantType)
 			.set("refreshToken", refreshToken)
 			.set("userType", userType)
-			.set("deptId", deptId).set("roleId", roleId);
+			.set("deptId", deptId).set("roleId", roleId)
+			.set("phone", phone)
+			.set("code", code);
 
 		ITokenGranter granter = TokenGranterBuilder.getGranter(grantType);
 		UserInfo userInfo = granter.grant(tokenParameter);
 
 		if (userInfo == null || userInfo.getUser() == null) {
-			return authInfo.set("error_code", HttpServletResponse.SC_BAD_REQUEST).set("error_description", "用户名或密码不正确");
+			if (grantType.equals(SmsCodeTokenGranter.GRANT_TYPE))
+				return authInfo.set("error_code", HttpServletResponse.SC_BAD_REQUEST).set("error_description", "手机号码未绑定账号");
+			else
+				return authInfo.set("error_code", HttpServletResponse.SC_BAD_REQUEST).set("error_description", "用户名或密码不正确");
 		}
 
 		if (Func.isEmpty(userInfo.getRoles())) {
diff --git a/src/main/java/org/springblade/modules/auth/granter/SmsCodeTokenGranter.java b/src/main/java/org/springblade/modules/auth/granter/SmsCodeTokenGranter.java
new file mode 100644
index 0000000..83a2b41
--- /dev/null
+++ b/src/main/java/org/springblade/modules/auth/granter/SmsCodeTokenGranter.java
@@ -0,0 +1,111 @@
+package org.springblade.modules.auth.granter;
+
+import lombok.AllArgsConstructor;
+import org.springblade.common.cache.CacheNames;
+import org.springblade.common.cache.ParamCache;
+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.core.tool.utils.WebUtil;
+import org.springblade.modules.auth.enums.UserEnum;
+import org.springblade.modules.auth.provider.ITokenGranter;
+import org.springblade.modules.auth.provider.TokenParameter;
+import org.springblade.modules.auth.sms.MobTechSmsVerify;
+import org.springblade.modules.auth.utils.TokenUtil;
+import org.springblade.modules.system.entity.Tenant;
+import org.springblade.modules.system.entity.UserInfo;
+import org.springblade.modules.system.service.IRoleService;
+import org.springblade.modules.system.service.ITenantService;
+import org.springblade.modules.system.service.IUserService;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.time.Duration;
+import java.util.List;
+
+/**
+ * @PROJECT_NAME: zttj-java-boot
+ * @DESCRIPTION: 短信验证码鉴权
+ * @USER: aix
+ * @DATE: 2023/12/12 14:25
+ */
+@Component
+@AllArgsConstructor
+public class SmsCodeTokenGranter implements ITokenGranter {
+
+	public static final String GRANT_TYPE = "smsCode";
+	public static final Integer FAIL_COUNT = 5;
+	public static final String FAIL_COUNT_VALUE = "account.failCount";
+	private final IUserService userService;
+	private final IRoleService roleService;
+	private final ITenantService tenantService;
+	private final BladeRedis bladeRedis;
+
+
+	@Override
+	public UserInfo grant(TokenParameter tokenParameter) {
+		HttpServletRequest request = WebUtil.getRequest();
+		// 获取用户绑定ID
+		String headerDept = request.getHeader(TokenUtil.DEPT_HEADER_KEY);
+		String headerRole = request.getHeader(TokenUtil.ROLE_HEADER_KEY);
+
+		// 获取用户信息
+		String tenantId = tokenParameter.getArgs().getStr("tenantId");
+		String phone = tokenParameter.getArgs().getStr("phone");
+		String code = tokenParameter.getArgs().getStr("code");
+
+		//短信验证api
+		String verifyStr = MobTechSmsVerify.requestData(phone, code);
+		if (verifyStr.indexOf("200") < 0) {
+			throw new ServiceException(TokenUtil.CAPTCHA_NOT_CORRECT);
+		}
+
+		// 判断登录是否锁定
+		int cnt = Func.toInt(bladeRedis.get(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, phone)), 0);
+		int failCount = Func.toInt(ParamCache.getValue(FAIL_COUNT_VALUE), FAIL_COUNT);
+		if (cnt >= failCount) {
+			throw new ServiceException(TokenUtil.USER_HAS_TOO_MANY_FAILS);
+		}
+
+		UserInfo userInfo = null;
+		if (Func.isNoneBlank(phone, code)) {
+			// 获取租户信息
+			Tenant tenant = tenantService.getByTenantId(tenantId);
+			if (TokenUtil.judgeTenant(tenant)) {
+				throw new ServiceException(TokenUtil.USER_HAS_NO_TENANT_PERMISSION);
+			}
+			// 获取用户类型
+			String userType = tokenParameter.getArgs().getStr("userType");
+			// 根据不同用户类型调用对应的接口返回数据,用户可自行拓展
+			if (userType.equals(UserEnum.WEB.getName())) {
+				userInfo = userService.userInfo(tenantId, phone);
+			} else if (userType.equals(UserEnum.APP.getName())) {
+				userInfo = userService.userInfo(tenantId, phone);
+			} else {
+				userInfo = userService.userInfo(tenantId, phone);
+			}
+		}
+
+		if (userInfo == null || userInfo.getUser() == null) {
+			// 增加错误锁定次数
+			bladeRedis.setEx(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, phone), cnt + 1, Duration.ofMinutes(30));
+		} else {
+			// 成功则清除登录缓存
+			bladeRedis.del(CacheNames.tenantKey(tenantId, CacheNames.USER_FAIL_KEY, phone));
+		}
+		// 多部门情况下指定单部门
+		if (Func.isNotEmpty(headerDept) && userInfo != null && userInfo.getUser().getDeptId().contains(headerDept)) {
+			userInfo.getUser().setDeptId(headerDept);
+		}
+		// 多角色情况下指定单角色
+		if (Func.isNotEmpty(headerRole) && userInfo != null && userInfo.getUser().getRoleId().contains(headerRole)) {
+			List<String> roleResult = roleService.getRoleAliases(headerRole);
+			userInfo.setRoles(roleResult);
+			userInfo.getUser().setRoleId(headerRole);
+		}
+
+		return userInfo;
+
+	}
+}
diff --git a/src/main/java/org/springblade/modules/auth/provider/TokenGranterBuilder.java b/src/main/java/org/springblade/modules/auth/provider/TokenGranterBuilder.java
index 7e6177c..ac35501 100644
--- a/src/main/java/org/springblade/modules/auth/provider/TokenGranterBuilder.java
+++ b/src/main/java/org/springblade/modules/auth/provider/TokenGranterBuilder.java
@@ -20,10 +20,7 @@
 import org.springblade.core.secure.exception.SecureException;
 import org.springblade.core.tool.utils.Func;
 import org.springblade.core.tool.utils.SpringUtil;
-import org.springblade.modules.auth.granter.CaptchaTokenGranter;
-import org.springblade.modules.auth.granter.PasswordTokenGranter;
-import org.springblade.modules.auth.granter.RefreshTokenGranter;
-import org.springblade.modules.auth.granter.SocialTokenGranter;
+import org.springblade.modules.auth.granter.*;
 
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
@@ -46,6 +43,7 @@
 		GRANTER_POOL.put(CaptchaTokenGranter.GRANT_TYPE, SpringUtil.getBean(CaptchaTokenGranter.class));
 		GRANTER_POOL.put(RefreshTokenGranter.GRANT_TYPE, SpringUtil.getBean(RefreshTokenGranter.class));
 		GRANTER_POOL.put(SocialTokenGranter.GRANT_TYPE, SpringUtil.getBean(SocialTokenGranter.class));
+		GRANTER_POOL.put(SmsCodeTokenGranter.GRANT_TYPE, SpringUtil.getBean(SmsCodeTokenGranter.class));
 	}
 
 	/**
diff --git a/src/main/java/org/springblade/modules/system/service/IUserService.java b/src/main/java/org/springblade/modules/system/service/IUserService.java
index 932d0a5..3718a2b 100644
--- a/src/main/java/org/springblade/modules/system/service/IUserService.java
+++ b/src/main/java/org/springblade/modules/system/service/IUserService.java
@@ -90,6 +90,8 @@
 	 */
 	User userByAccount(String tenantId, String account);
 
+	User userByPhone(String tenantId, String phone);
+
 	/**
 	 * 用户信息
 	 *
@@ -98,6 +100,8 @@
 	 */
 	UserInfo userInfo(Long userId);
 
+	UserInfo userInfo(String tenantId, String phone);
+
 	/**
 	 * 用户信息
 	 *
diff --git a/src/main/java/org/springblade/modules/system/service/impl/UserServiceImpl.java b/src/main/java/org/springblade/modules/system/service/impl/UserServiceImpl.java
index 6dc569d..4c07c77 100644
--- a/src/main/java/org/springblade/modules/system/service/impl/UserServiceImpl.java
+++ b/src/main/java/org/springblade/modules/system/service/impl/UserServiceImpl.java
@@ -183,12 +183,23 @@
 	}
 
 	@Override
+	public User userByPhone(String tenantId, String phone) {
+		return baseMapper.selectOne(Wrappers.<User>query().lambda().eq(User::getTenantId, tenantId).eq(User::getPhone, phone).eq(User::getIsDeleted, BladeConstant.DB_NOT_DELETED));
+	}
+
+	@Override
 	public UserInfo userInfo(Long userId) {
 		User user = baseMapper.selectById(userId);
 		return buildUserInfo(user);
 	}
 
 	@Override
+	public UserInfo userInfo(String tenantId, String phone) {
+		User user = userByPhone(tenantId, phone);
+		return buildUserInfo(user);
+	}
+
+	@Override
 	public UserInfo userInfo(String tenantId, String account, String password) {
 		User user = baseMapper.getUser(tenantId, account, password);
 		return buildUserInfo(user);

--
Gitblit v1.9.3