✨ 小程序用户独立为 blade_mp_user,并兼容司机/车辆 OCR 宽松日期
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -51,6 +51,7 @@ import org.springblade.core.oauth2.service.OAuth2UserService;
|
||||
import org.springblade.system.feign.IAuthLockClient;
|
||||
import org.springblade.system.feign.IAuthLogClient;
|
||||
import org.springblade.core.tenant.BladeTenantProperties;
|
||||
import org.springblade.system.feign.IMpUserClient;
|
||||
import org.springblade.system.feign.IUserClient;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
@@ -117,8 +118,8 @@ public class BladeAuthConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public OAuth2UserService oAuth2UserService(IUserClient userClient) {
|
||||
return new BladeUserDetailService(userClient);
|
||||
public OAuth2UserService oAuth2UserService(IUserClient userClient, IMpUserClient mpUserClient) {
|
||||
return new BladeUserDetailService(userClient, mpUserClient);
|
||||
}
|
||||
|
||||
@Primary
|
||||
|
||||
+19
-1
@@ -30,9 +30,11 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.oauth2.endpoint.OAuth2TokenEndPoint;
|
||||
import org.springblade.core.secure.BladeUser;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.support.Kv;
|
||||
import org.springblade.core.tool.utils.BeanUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.system.feign.ISysClient;
|
||||
import org.springblade.system.pojo.enums.MpUserIdentity;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
@@ -72,10 +74,26 @@ public class OAuth2UserInfoResponseAdvice implements ResponseBodyAdvice<Object>
|
||||
if (userInfo == null) {
|
||||
return body;
|
||||
}
|
||||
userInfo.setPermission(loadPermission(bladeUser.getRoleId()));
|
||||
if (isMpUser(bladeUser)) {
|
||||
// 小程序用户:按身份直接生成权限,不走系统角色菜单
|
||||
userInfo.setPermission(MpUserIdentity.toPermissions(bladeUser.getRoleName()));
|
||||
} else {
|
||||
userInfo.setPermission(loadPermission(bladeUser.getRoleId()));
|
||||
}
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
private boolean isMpUser(BladeUser bladeUser) {
|
||||
Kv detail = bladeUser.getDetail();
|
||||
if (detail != null && MpUserIdentity.DETAIL_TYPE.equalsIgnoreCase(Func.toStr(detail.get("type")))) {
|
||||
return true;
|
||||
}
|
||||
// 兼容:无系统角色且 roleName 为小程序身份
|
||||
return Func.isBlank(bladeUser.getRoleId())
|
||||
&& Func.isNotBlank(bladeUser.getRoleName())
|
||||
&& !MpUserIdentity.split(bladeUser.getRoleName()).isEmpty();
|
||||
}
|
||||
|
||||
private List<String> loadPermission(String roleId) {
|
||||
if (Func.isBlank(roleId)) {
|
||||
return Collections.emptyList();
|
||||
|
||||
@@ -2,10 +2,12 @@ package org.springblade.auth.granter;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.auth.utils.TokenUtil;
|
||||
import org.springblade.core.oauth2.exception.UserInvalidException;
|
||||
import org.springblade.core.oauth2.granter.AbstractTokenGranter;
|
||||
import org.springblade.core.oauth2.handler.PasswordHandler;
|
||||
import org.springblade.core.oauth2.provider.OAuth2Request;
|
||||
import org.springblade.core.oauth2.service.OAuth2Client;
|
||||
import org.springblade.core.oauth2.service.OAuth2ClientService;
|
||||
import org.springblade.core.oauth2.service.OAuth2User;
|
||||
import org.springblade.core.oauth2.service.OAuth2UserService;
|
||||
@@ -13,7 +15,8 @@ import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.core.tool.utils.WebUtil;
|
||||
import org.springblade.system.feign.IUserClient;
|
||||
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.exception.WechatMiniException;
|
||||
import org.springblade.thirdparty.wechat.pojo.vo.WechatPhoneVO;
|
||||
@@ -25,7 +28,8 @@ import org.springframework.stereotype.Component;
|
||||
* 微信小程序手机号一键登录。
|
||||
* <p>
|
||||
* grant_type=wechat_applet,参数:loginCode(wx.login)、phoneCode(getPhoneNumber)。
|
||||
* 流程:换 openid + 手机号 → 按手机号查用户(不存在则拒绝)→ 记录 openid → 发令牌(与密码登录一致)。
|
||||
* 流程:换 openid + 手机号 → 按手机号查小程序用户(blade_mp_user,不存在则拒绝)
|
||||
* → 记录 openid → 按身份发令牌(不依赖系统角色)。
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@@ -33,17 +37,17 @@ public class WechatMiniTokenGranter extends AbstractTokenGranter {
|
||||
|
||||
private final OAuth2UserService userService;
|
||||
private final IWechatMiniService wechatMiniService;
|
||||
private final IUserClient userClient;
|
||||
private final IMpUserClient mpUserClient;
|
||||
|
||||
public WechatMiniTokenGranter(OAuth2ClientService clientService,
|
||||
OAuth2UserService userService,
|
||||
PasswordHandler passwordHandler,
|
||||
IWechatMiniService wechatMiniService,
|
||||
IUserClient userClient) {
|
||||
IMpUserClient mpUserClient) {
|
||||
super(clientService, userService, passwordHandler);
|
||||
this.userService = userService;
|
||||
this.wechatMiniService = wechatMiniService;
|
||||
this.userClient = userClient;
|
||||
this.mpUserClient = mpUserClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,7 +58,7 @@ public class WechatMiniTokenGranter extends AbstractTokenGranter {
|
||||
@Override
|
||||
public OAuth2User user(OAuth2Request request) {
|
||||
// 先校验客户端是否允许该授权类型,避免先调微信再因客户端配置失败
|
||||
var oauthClient = client(request);
|
||||
OAuth2Client oauthClient = client(request);
|
||||
|
||||
HttpServletRequest httpRequest = WebUtil.getRequest();
|
||||
String loginCode = httpRequest.getParameter("loginCode");
|
||||
@@ -78,16 +82,21 @@ public class WechatMiniTokenGranter extends AbstractTokenGranter {
|
||||
throw new UserInvalidException("未获取到微信手机号");
|
||||
}
|
||||
|
||||
OAuth2User user = userService.loadByPhone(phone, request);
|
||||
if (!userService.validateUser(user)) {
|
||||
R<MpUserInfo> mpUserResult = mpUserClient.userInfoByPhone(request.getTenantId(), phone);
|
||||
if (mpUserResult == null || !mpUserResult.isSuccess() || mpUserResult.getData() == null
|
||||
|| mpUserResult.getData().getUser() == null) {
|
||||
throw new UserInvalidException("用户不存在,无法登录");
|
||||
}
|
||||
|
||||
R<Boolean> bindResult = userClient.bindWxMiniOpenId(
|
||||
OAuth2User user = TokenUtil.convertMpUser(mpUserResult.getData());
|
||||
if (!userService.validateUser(user)) {
|
||||
throw new UserInvalidException("用户身份无效,无法登录");
|
||||
}
|
||||
|
||||
R<Boolean> bindResult = mpUserClient.bindOpenId(
|
||||
request.getTenantId(),
|
||||
Func.toLong(user.getUserId()),
|
||||
session.getOpenid(),
|
||||
phone
|
||||
session.getOpenid()
|
||||
);
|
||||
if (bindResult == null || !bindResult.isSuccess()) {
|
||||
String msg = bindResult != null ? bindResult.getMsg() : "绑定 openid 失败";
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.auth.service;
|
||||
@@ -35,8 +32,11 @@ import org.springblade.core.oauth2.utils.OAuth2ExceptionUtil;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.system.feign.IMpUserClient;
|
||||
import org.springblade.system.feign.IUserClient;
|
||||
import org.springblade.system.pojo.entity.MpUserInfo;
|
||||
import org.springblade.system.pojo.entity.UserInfo;
|
||||
import org.springblade.system.pojo.enums.MpUserIdentity;
|
||||
import org.springblade.system.pojo.enums.UserType;
|
||||
|
||||
import java.util.Optional;
|
||||
@@ -49,6 +49,7 @@ import java.util.Optional;
|
||||
@RequiredArgsConstructor
|
||||
public class BladeUserDetailService implements OAuth2UserService {
|
||||
private final IUserClient userClient;
|
||||
private final IMpUserClient mpUserClient;
|
||||
|
||||
@Override
|
||||
public OAuth2User loadByUserId(String userId, OAuth2Request request) {
|
||||
@@ -57,13 +58,29 @@ public class BladeUserDetailService implements OAuth2UserService {
|
||||
.filter(s -> !StringUtil.isBlank(s))
|
||||
.orElse(UserType.WEB.getName());
|
||||
|
||||
// 获取用户信息
|
||||
R<UserInfo> result = userClient.userInfo(Func.toLong(userId), userType);
|
||||
if (!result.isSuccess()) {
|
||||
OAuth2ExceptionUtil.throwFromCode(OAuth2ErrorCode.INVALID_USER);
|
||||
// 小程序刷新/鉴权:直接查 blade_mp_user
|
||||
if (MpUserIdentity.DETAIL_TYPE.equalsIgnoreCase(userType)) {
|
||||
OAuth2User mpUser = loadMpUserById(userId);
|
||||
if (mpUser == null) {
|
||||
OAuth2ExceptionUtil.throwFromCode(OAuth2ErrorCode.INVALID_USER);
|
||||
}
|
||||
return mpUser;
|
||||
}
|
||||
// 构建oauth2用户信息
|
||||
return TokenUtil.convertUser(result.getData(), request);
|
||||
|
||||
// 系统用户
|
||||
R<UserInfo> result = userClient.userInfo(Func.toLong(userId), userType);
|
||||
if (result != null && result.isSuccess() && result.getData() != null
|
||||
&& result.getData().getUser() != null) {
|
||||
return TokenUtil.convertUser(result.getData(), request);
|
||||
}
|
||||
|
||||
// 兼容:未带 userType=mp 时,回落查询小程序用户(令牌刷新)
|
||||
OAuth2User mpUser = loadMpUserById(userId);
|
||||
if (mpUser != null) {
|
||||
return mpUser;
|
||||
}
|
||||
OAuth2ExceptionUtil.throwFromCode(OAuth2ErrorCode.INVALID_USER);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,6 +91,15 @@ public class BladeUserDetailService implements OAuth2UserService {
|
||||
.orElse(UserType.WEB.getName());
|
||||
String tenantId = request.getTenantId();
|
||||
|
||||
// 小程序用户:按账号查 blade_mp_user
|
||||
if (MpUserIdentity.DETAIL_TYPE.equalsIgnoreCase(userType)) {
|
||||
R<MpUserInfo> mpResult = mpUserClient.userInfoByAccount(tenantId, username);
|
||||
if (mpResult == null || !mpResult.isSuccess() || mpResult.getData() == null) {
|
||||
return null;
|
||||
}
|
||||
return TokenUtil.convertMpUser(mpResult.getData());
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
R<UserInfo> result = userClient.userInfo(tenantId, username, userType);
|
||||
if (!result.isSuccess()) {
|
||||
@@ -91,6 +117,11 @@ public class BladeUserDetailService implements OAuth2UserService {
|
||||
.orElse(UserType.WEB.getName());
|
||||
String tenantId = request.getTenantId();
|
||||
|
||||
// 小程序用户类型:仅查 blade_mp_user
|
||||
if (MpUserIdentity.DETAIL_TYPE.equalsIgnoreCase(userType)) {
|
||||
return loadMpUserByPhone(tenantId, phone);
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
R<UserInfo> result = userClient.userInfoByPhone(tenantId, phone, userType);
|
||||
if (!result.isSuccess()) {
|
||||
@@ -108,4 +139,28 @@ public class BladeUserDetailService implements OAuth2UserService {
|
||||
.isPresent(); // 如果上述条件都满足,则返回true,否则返回false
|
||||
}
|
||||
|
||||
private OAuth2User loadMpUserById(String userId) {
|
||||
if (Func.isBlank(userId)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
R<MpUserInfo> result = mpUserClient.userInfoById(Func.toLong(userId));
|
||||
if (result != null && result.isSuccess() && result.getData() != null
|
||||
&& result.getData().getUser() != null) {
|
||||
return TokenUtil.convertMpUser(result.getData());
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
// 非小程序用户场景忽略,继续走系统用户
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private OAuth2User loadMpUserByPhone(String tenantId, String phone) {
|
||||
R<MpUserInfo> result = mpUserClient.userInfoByPhone(tenantId, phone);
|
||||
if (result == null || !result.isSuccess() || result.getData() == null) {
|
||||
return null;
|
||||
}
|
||||
return TokenUtil.convertMpUser(result.getData());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,12 +28,14 @@ package org.springblade.auth.utils;
|
||||
import org.springblade.core.oauth2.provider.OAuth2Request;
|
||||
import org.springblade.core.oauth2.service.OAuth2User;
|
||||
import org.springblade.core.oauth2.service.impl.OAuth2UserDetail;
|
||||
import org.springblade.core.tool.support.Kv;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.system.cache.SysCache;
|
||||
import org.springblade.system.pojo.entity.MpUser;
|
||||
import org.springblade.system.pojo.entity.MpUserInfo;
|
||||
import org.springblade.system.pojo.entity.User;
|
||||
import org.springblade.system.pojo.entity.UserInfo;
|
||||
|
||||
import java.util.Collections;
|
||||
import org.springblade.system.pojo.enums.MpUserIdentity;
|
||||
|
||||
/**
|
||||
* 认证工具类
|
||||
@@ -75,6 +77,7 @@ public class TokenUtil {
|
||||
userDetail.setRealName(user.getRealName());
|
||||
userDetail.setAccount(user.getAccount());
|
||||
userDetail.setPassword(user.getPassword());
|
||||
userDetail.setPhone(user.getPhone());
|
||||
userDetail.setDeptId(user.getDeptId());
|
||||
userDetail.setPostId(user.getPostId());
|
||||
userDetail.setRoleId(user.getRoleId());
|
||||
@@ -85,4 +88,37 @@ public class TokenUtil {
|
||||
return userDetail;
|
||||
}
|
||||
|
||||
/**
|
||||
* 小程序用户转换为OAuth2标准用户(身份直接写入 authorities,不走系统角色)
|
||||
*
|
||||
* @param userInfo 小程序用户信息
|
||||
* @return OAuth2User
|
||||
*/
|
||||
public static OAuth2User convertMpUser(MpUserInfo userInfo) {
|
||||
if (userInfo == null || userInfo.getUser() == null) {
|
||||
return null;
|
||||
}
|
||||
MpUser user = userInfo.getUser();
|
||||
OAuth2UserDetail userDetail = new OAuth2UserDetail();
|
||||
userDetail.setUserId(String.valueOf(user.getId()));
|
||||
userDetail.setTenantId(user.getTenantId());
|
||||
userDetail.setName(user.getName());
|
||||
userDetail.setRealName(user.getRealName());
|
||||
userDetail.setAccount(user.getAccount());
|
||||
userDetail.setPassword(user.getPassword());
|
||||
userDetail.setPhone(user.getPhone());
|
||||
userDetail.setDeptId(user.getDeptId());
|
||||
userDetail.setPostId(user.getPostId());
|
||||
// 小程序用户不绑定系统角色,roleId 留空;身份放入 roleName/authorities
|
||||
userDetail.setRoleId("");
|
||||
userDetail.setRoleName(Func.join(userInfo.getIdentities()));
|
||||
userDetail.setAvatar(user.getAvatar());
|
||||
userDetail.setAuthorities(userInfo.getIdentities());
|
||||
userDetail.setPermissions(userInfo.getPermissions());
|
||||
userDetail.setDetail(userInfo.getDetail() == null
|
||||
? Kv.create().set("type", MpUserIdentity.DETAIL_TYPE)
|
||||
: userInfo.getDetail());
|
||||
return userDetail;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user