♻️ 微信与短信登录改走MpLoginSupport自动建号建档

This commit is contained in:
2026-10-23 15:58:42 +08:00
parent a8e059e92e
commit 924134226f
2 changed files with 28 additions and 30 deletions
@@ -27,6 +27,7 @@ package org.springblade.auth.granter;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import org.springblade.auth.constant.BladeAuthConstant; import org.springblade.auth.constant.BladeAuthConstant;
import org.springblade.auth.service.MpLoginSupport;
import org.springblade.core.oauth2.constant.OAuth2TokenConstant; import org.springblade.core.oauth2.constant.OAuth2TokenConstant;
import org.springblade.core.oauth2.exception.UserInvalidException; import org.springblade.core.oauth2.exception.UserInvalidException;
import org.springblade.core.oauth2.granter.AbstractTokenGranter; import org.springblade.core.oauth2.granter.AbstractTokenGranter;
@@ -43,10 +44,15 @@ import org.springblade.core.tool.utils.SM2Util;
import org.springblade.core.tool.utils.StringUtil; import org.springblade.core.tool.utils.StringUtil;
import org.springblade.core.tool.utils.WebUtil; import org.springblade.core.tool.utils.WebUtil;
import org.springblade.resource.feign.ISmsClient; import org.springblade.resource.feign.ISmsClient;
import org.springblade.system.pojo.enums.MpUserIdentity;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
* SmsTokenGranter * SmsTokenGranter
* <p>
* 除验证码校验外,还要按请求来源分流:小程序(User-Type: mp)登录时账号不存在要自动建,
* Web 短信登录保持原样不建号。建号逻辑不能下沉到 {@code BladeUserDetailService.loadByPhone},
* 那条方法还被「发送短信验证码」复用,会在发码时就建出账号(刷号)。
* *
* @author BladeX * @author BladeX
*/ */
@@ -56,12 +62,14 @@ public class SmsTokenGranter extends AbstractTokenGranter {
private final OAuth2UserService userService; private final OAuth2UserService userService;
private final ISmsClient smsClient; private final ISmsClient smsClient;
private final OAuth2Properties properties; private final OAuth2Properties properties;
private final MpLoginSupport mpLoginSupport;
public SmsTokenGranter(OAuth2ClientService clientService, OAuth2UserService userService, PasswordHandler passwordHandler, ISmsClient smsClient, OAuth2Properties properties) { public SmsTokenGranter(OAuth2ClientService clientService, OAuth2UserService userService, PasswordHandler passwordHandler, ISmsClient smsClient, OAuth2Properties properties, MpLoginSupport mpLoginSupport) {
super(clientService, userService, passwordHandler); super(clientService, userService, passwordHandler);
this.userService = userService; this.userService = userService;
this.smsClient = smsClient; this.smsClient = smsClient;
this.properties = properties; this.properties = properties;
this.mpLoginSupport = mpLoginSupport;
} }
@Override @Override
@@ -86,8 +94,10 @@ public class SmsTokenGranter extends AbstractTokenGranter {
if (!result.isSuccess()) { if (!result.isSuccess()) {
throw new UserInvalidException(OAuth2TokenConstant.CAPTCHA_NOT_CORRECT); throw new UserInvalidException(OAuth2TokenConstant.CAPTCHA_NOT_CORRECT);
} }
// 获取用户信息 // 获取用户信息:只有小程序登录才允许自动建号,Web 短信登录仍走原查询
OAuth2User user = userService.loadByPhone(decryptedPhone, request); OAuth2User user = isMpLoginRequest(request)
? mpLoginSupport.ensureLoginAccount(tenantId, decryptedPhone, null)
: userService.loadByPhone(decryptedPhone, request);
// 校验用户信息 // 校验用户信息
if (!userService.validateUser(user)) { if (!userService.validateUser(user)) {
throw new UserInvalidException(OAuth2TokenConstant.TOKEN_NOT_CORRECT); throw new UserInvalidException(OAuth2TokenConstant.TOKEN_NOT_CORRECT);
@@ -97,6 +107,13 @@ public class SmsTokenGranter extends AbstractTokenGranter {
return user; return user;
} }
/**
* 是否小程序登录:请求头 User-Type=mp(Web 与小程序共用客户端,不能按 clientId 分流)
*/
private boolean isMpLoginRequest(OAuth2Request request) {
return request != null && MpUserIdentity.DETAIL_TYPE.equalsIgnoreCase(Func.toStr(request.getUserType()));
}
private SmsCode buildSmsCode() { private SmsCode buildSmsCode() {
HttpServletRequest request = WebUtil.getRequest(); HttpServletRequest request = WebUtil.getRequest();
return new SmsCode().setId(request.getParameter("id")) return new SmsCode().setId(request.getParameter("id"))
@@ -2,7 +2,7 @@ package org.springblade.auth.granter;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springblade.auth.utils.TokenUtil; import org.springblade.auth.service.MpLoginSupport;
import org.springblade.core.oauth2.exception.UserInvalidException; import org.springblade.core.oauth2.exception.UserInvalidException;
import org.springblade.core.oauth2.granter.AbstractTokenGranter; import org.springblade.core.oauth2.granter.AbstractTokenGranter;
import org.springblade.core.oauth2.handler.PasswordHandler; import org.springblade.core.oauth2.handler.PasswordHandler;
@@ -11,12 +11,9 @@ import org.springblade.core.oauth2.service.OAuth2Client;
import org.springblade.core.oauth2.service.OAuth2ClientService; import org.springblade.core.oauth2.service.OAuth2ClientService;
import org.springblade.core.oauth2.service.OAuth2User; import org.springblade.core.oauth2.service.OAuth2User;
import org.springblade.core.oauth2.service.OAuth2UserService; import org.springblade.core.oauth2.service.OAuth2UserService;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func; import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringUtil; import org.springblade.core.tool.utils.StringUtil;
import org.springblade.core.tool.utils.WebUtil; import org.springblade.core.tool.utils.WebUtil;
import org.springblade.system.feign.IMpUserClient;
import org.springblade.system.pojo.entity.MpUserInfo;
import org.springblade.thirdparty.wechat.constant.WechatMiniConstant; import org.springblade.thirdparty.wechat.constant.WechatMiniConstant;
import org.springblade.thirdparty.wechat.exception.WechatMiniException; import org.springblade.thirdparty.wechat.exception.WechatMiniException;
import org.springblade.thirdparty.wechat.pojo.vo.WechatPhoneVO; import org.springblade.thirdparty.wechat.pojo.vo.WechatPhoneVO;
@@ -28,8 +25,8 @@ import org.springframework.stereotype.Component;
* 微信小程序手机号一键登录。 * 微信小程序手机号一键登录。
* <p> * <p>
* grant_type=wechat_applet,参数:loginCode(wx.login)、phoneCode(getPhoneNumber)。 * grant_type=wechat_applet,参数:loginCode(wx.login)、phoneCode(getPhoneNumber)。
* 流程:换 openid + 手机号 → 按手机号查小程序用户(blade_mp_user,不存在则拒绝) * 流程:换 openid + 手机号 → 由 {@link MpLoginSupport} 找到或创建账号并补齐档案
* → 记录 openid → 按身份发令牌(不依赖系统角色)。 * → 按身份发令牌(不依赖系统角色)。
*/ */
@Slf4j @Slf4j
@Component @Component
@@ -37,17 +34,17 @@ public class WechatMiniTokenGranter extends AbstractTokenGranter {
private final OAuth2UserService userService; private final OAuth2UserService userService;
private final IWechatMiniService wechatMiniService; private final IWechatMiniService wechatMiniService;
private final IMpUserClient mpUserClient; private final MpLoginSupport mpLoginSupport;
public WechatMiniTokenGranter(OAuth2ClientService clientService, public WechatMiniTokenGranter(OAuth2ClientService clientService,
OAuth2UserService userService, OAuth2UserService userService,
PasswordHandler passwordHandler, PasswordHandler passwordHandler,
IWechatMiniService wechatMiniService, IWechatMiniService wechatMiniService,
IMpUserClient mpUserClient) { MpLoginSupport mpLoginSupport) {
super(clientService, userService, passwordHandler); super(clientService, userService, passwordHandler);
this.userService = userService; this.userService = userService;
this.wechatMiniService = wechatMiniService; this.wechatMiniService = wechatMiniService;
this.mpUserClient = mpUserClient; this.mpLoginSupport = mpLoginSupport;
} }
@Override @Override
@@ -82,28 +79,12 @@ public class WechatMiniTokenGranter extends AbstractTokenGranter {
throw new UserInvalidException("未获取到微信手机号"); throw new UserInvalidException("未获取到微信手机号");
} }
R<MpUserInfo> mpUserResult = mpUserClient.userInfoByPhone(request.getTenantId(), phone); // 账号不存在时自动建号,建档与绑 openid 由编排层尽力而为,失败不阻断登录
if (mpUserResult == null || !mpUserResult.isSuccess() || mpUserResult.getData() == null OAuth2User user = mpLoginSupport.ensureLoginAccount(request.getTenantId(), phone, session.getOpenid());
|| mpUserResult.getData().getUser() == null) {
throw new UserInvalidException("用户不存在,无法登录");
}
OAuth2User user = TokenUtil.convertMpUser(mpUserResult.getData());
if (!userService.validateUser(user)) { if (!userService.validateUser(user)) {
throw new UserInvalidException("用户身份无效,无法登录"); throw new UserInvalidException("用户身份无效,无法登录");
} }
R<Boolean> bindResult = mpUserClient.bindOpenId(
request.getTenantId(),
Func.toLong(user.getUserId()),
session.getOpenid()
);
if (bindResult == null || !bindResult.isSuccess()) {
String msg = bindResult != null ? bindResult.getMsg() : "绑定 openid 失败";
log.warn("绑定微信 openid 失败 userId={} openid={} msg={}", user.getUserId(), session.getOpenid(), msg);
throw new UserInvalidException(StringUtil.isBlank(msg) ? "绑定 openid 失败" : msg);
}
user.setClient(oauthClient); user.setClient(oauthClient);
return user; return user;
} }