mirror of
https://gitee.com/xiaonuobase/snowy.git
synced 2026-10-06 06:52:58 +08:00
【增强】增强OIDC/SSO登录容错能力:AuthOidcCommonRequest增加GET/POST降级、详细日志和uuid空值校验;AuthOidcClient增加state校验失败降级处理;AuthThirdServiceImpl增加state格式异常容错和Redis查询异常捕获;同时修复username字段赋值和SSO账号前缀枚举。
This commit is contained in:
+16
@@ -170,6 +170,22 @@ public interface SaBaseLoginUserApi {
|
||||
**/
|
||||
SaBaseClientLoginUser createClientUserWithEmail(String email);
|
||||
|
||||
/**
|
||||
* 使用账号和密码创建B端用户
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2026/6/25
|
||||
**/
|
||||
SaBaseLoginUser createUserWithAccount(String account, String password);
|
||||
|
||||
/**
|
||||
* 使用账号和密码创建C端用户
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2026/6/26
|
||||
**/
|
||||
SaBaseClientLoginUser createClientUserWithAccount(String account, String password);
|
||||
|
||||
/**
|
||||
* 执行注册
|
||||
*
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
||||
*
|
||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
||||
*/
|
||||
package vip.xiaonuo.auth.core.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 认证账号前缀枚举
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2026/06/27
|
||||
**/
|
||||
@Getter
|
||||
public enum AuthAccountPrefixEnum {
|
||||
|
||||
/** SSO单点登录自动创建的账号前缀 */
|
||||
SSO("sso_", "SSO单点登录");
|
||||
|
||||
private final String value;
|
||||
private final String description;
|
||||
|
||||
AuthAccountPrefixEnum(String value, String description) {
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
+55
-4
@@ -20,8 +20,10 @@ import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthToken;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import me.zhyd.oauth.request.AuthRequest;
|
||||
import me.zhyd.oauth.utils.AuthStateUtils;
|
||||
@@ -146,10 +148,59 @@ public class AuthOidcClient extends AuthBaseClient<AuthOidcBaseJson> {
|
||||
throw new CommonException("state不能为空");
|
||||
}
|
||||
AuthRequest authRequest = this.getAuthRequest();
|
||||
AuthResponse<AuthUser> authResponse = authRequest.login(AuthCallback.builder().code(code).state(state).build());
|
||||
if(!authResponse.ok()) {
|
||||
throw new CommonException(authResponse.getMsg());
|
||||
|
||||
// 尝试正常登录流程(包含state校验)
|
||||
try {
|
||||
AuthResponse<AuthUser> authResponse = authRequest.login(AuthCallback.builder().code(code).state(state).build());
|
||||
|
||||
// 检查响应是否失败
|
||||
if(!authResponse.ok()) {
|
||||
String errorMsg = authResponse.getMsg();
|
||||
// 如果是state校验失败,尝试降级处理
|
||||
if(errorMsg != null && errorMsg.contains("Illegal state")) {
|
||||
log.warn(">>> OIDC state校验失败(响应失败),尝试跳过state校验直接获取token,state={}, error={}", state, errorMsg);
|
||||
return doLoginWithoutStateCheck(code);
|
||||
}
|
||||
throw new CommonException(errorMsg);
|
||||
}
|
||||
|
||||
return handleAuthResponse(authResponse);
|
||||
} catch (AuthException e) {
|
||||
// 如果是异常形式的state校验失败
|
||||
if(e.getMessage() != null && e.getMessage().contains("Illegal state")) {
|
||||
log.warn(">>> OIDC state校验失败(异常),尝试跳过state校验直接获取token,state={}, error={}", state, e.getMessage());
|
||||
return doLoginWithoutStateCheck(code);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳过state校验,直接用code换token和用户信息
|
||||
*/
|
||||
private AuthResponse<AuthUser> doLoginWithoutStateCheck(String code) {
|
||||
try {
|
||||
AuthRequest authRequest = this.getAuthRequest();
|
||||
// 直接用code换token(不校验state)
|
||||
AuthCallback authCallback = AuthCallback.builder()
|
||||
.code(code)
|
||||
.state("bypass-state-check") // 使用占位state
|
||||
.build();
|
||||
|
||||
// 获取token
|
||||
AuthToken authToken = authRequest.getAccessToken(authCallback);
|
||||
// 获取用户信息
|
||||
AuthUser authUser = authRequest.getUserInfo(authToken);
|
||||
|
||||
log.info(">>> OIDC跳过state校验成功获取用户信息,userId={}", authUser.getUuid());
|
||||
|
||||
return AuthResponse.<AuthUser>builder()
|
||||
.code(me.zhyd.oauth.enums.AuthResponseStatus.SUCCESS.getCode())
|
||||
.data(authUser)
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error(">>> OIDC跳过state校验后仍然失败", e);
|
||||
throw new CommonException("OIDC登录失败:{}", e.getMessage());
|
||||
}
|
||||
return handleAuthResponse(authResponse);
|
||||
}
|
||||
}
|
||||
|
||||
+62
-3
@@ -22,6 +22,7 @@ import cn.hutool.json.JSONUtil;
|
||||
import com.xkcoding.http.config.HttpConfig;
|
||||
import com.xkcoding.http.support.HttpHeader;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
@@ -44,6 +45,7 @@ import java.util.List;
|
||||
* @author xuyuxiang
|
||||
* @date 2025/1/24 15:09
|
||||
**/
|
||||
@Slf4j
|
||||
@Getter
|
||||
public class AuthOidcCommonRequest extends AuthDefaultRequest {
|
||||
|
||||
@@ -106,25 +108,82 @@ public class AuthOidcCommonRequest extends AuthDefaultRequest {
|
||||
HttpConfig httpConfig = HttpConfig.builder().timeout(5000).build();
|
||||
String tokenType = authToken.getTokenType();
|
||||
String userInfo;
|
||||
|
||||
if(ObjectUtil.isNotEmpty(tokenType) && tokenType.equals(SaOAuth2Consts.TokenType.Bearer)) {
|
||||
HttpHeader header = (new HttpHeader()).add(SaOAuth2Consts.Param.Authorization,
|
||||
SaOAuth2Consts.TokenType.Bearer + " " + authToken.getAccessToken());
|
||||
userInfo = (new HttpUtils(httpConfig))
|
||||
.get(this.source.userInfo(), null, header, false).getBody();
|
||||
|
||||
// GET/POST降级处理:优先GET,失败后降级POST
|
||||
userInfo = fetchUserInfoWithFallback(httpConfig, header);
|
||||
} else {
|
||||
userInfo = (new HttpUtils(httpConfig)).get(this.userInfoUrl(authToken)).getBody();
|
||||
}
|
||||
|
||||
return parseUserInfo(userInfo, authToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息(支持GET/POST降级)
|
||||
*/
|
||||
private String fetchUserInfoWithFallback(HttpConfig httpConfig, HttpHeader header) {
|
||||
HttpUtils httpUtils = new HttpUtils(httpConfig);
|
||||
String userInfoUrl = this.source.userInfo();
|
||||
|
||||
// 优先尝试GET
|
||||
try {
|
||||
String response = httpUtils.get(userInfoUrl, null, header, false).getBody();
|
||||
log.info(">>> OIDC使用GET方法获取用户信息成功");
|
||||
return response;
|
||||
} catch (Exception e) {
|
||||
log.warn(">>> OIDC使用GET方法获取用户信息失败,尝试POST方法: {}", e.getMessage());
|
||||
}
|
||||
|
||||
// GET失败,降级POST
|
||||
try {
|
||||
String response = httpUtils.post(userInfoUrl, null, header, false).getBody();
|
||||
log.info(">>> OIDC使用POST方法获取用户信息成功");
|
||||
return response;
|
||||
} catch (Exception ex) {
|
||||
log.error(">>> OIDC使用GET和POST方法均获取用户信息失败", ex);
|
||||
throw new AuthException(AuthResponseStatus.FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析用户信息
|
||||
*/
|
||||
private AuthUser parseUserInfo(String userInfo, AuthToken authToken) {
|
||||
JSONObject bodyJsonObject = JSONUtil.parseObj(userInfo);
|
||||
|
||||
log.info(">>> OIDC用户信息原始响应: {}", userInfo);
|
||||
log.info(">>> OIDC配置的sourceProperty: {}", authOidcBaseJson.getSourceProperty());
|
||||
|
||||
// 有条件的data解包:只有顶层没有目标字段且有data包装时才解包
|
||||
if(!bodyJsonObject.containsKey(authOidcBaseJson.getSourceProperty()) && bodyJsonObject.containsKey("data")) {
|
||||
Object data = bodyJsonObject.get("data");
|
||||
if(ObjectUtil.isEmpty(data)) {
|
||||
log.error(">>> OIDC响应包含data节点但值为空");
|
||||
throw new AuthException(AuthResponseStatus.FAILURE);
|
||||
}
|
||||
bodyJsonObject = JSONUtil.parseObj(data);
|
||||
log.info(">>> OIDC检测到响应需要解包data节点");
|
||||
}
|
||||
|
||||
String uuidValue = bodyJsonObject.getStr(authOidcBaseJson.getSourceProperty());
|
||||
|
||||
// uuid空值校验
|
||||
if(StrUtil.isBlank(uuidValue)) {
|
||||
log.error(">>> OIDC无法从响应中提取uuid,sourceProperty={}, 响应={}",
|
||||
authOidcBaseJson.getSourceProperty(), userInfo);
|
||||
throw new AuthException(AuthResponseStatus.FAILURE);
|
||||
}
|
||||
|
||||
log.info(">>> OIDC成功提取uuid: {}", uuidValue);
|
||||
|
||||
return AuthUser.builder()
|
||||
.rawUserInfo(com.alibaba.fastjson.JSONObject.parseObject(bodyJsonObject.toString()))
|
||||
.uuid(bodyJsonObject.getStr(authOidcBaseJson.getSourceProperty()))
|
||||
.uuid(uuidValue)
|
||||
.username(uuidValue)
|
||||
.token(authToken)
|
||||
.source(this.source.toString()).build();
|
||||
}
|
||||
|
||||
+168
-34
@@ -16,6 +16,7 @@ import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
@@ -27,19 +28,19 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.xkcoding.http.HttpUtil;
|
||||
import com.xkcoding.http.support.hutool.HutoolImpl;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import me.zhyd.oauth.model.AuthCallback;
|
||||
import me.zhyd.oauth.model.AuthResponse;
|
||||
import me.zhyd.oauth.model.AuthUser;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import vip.xiaonuo.auth.api.SaBaseLoginUserApi;
|
||||
import vip.xiaonuo.auth.core.enums.AuthAccountPrefixEnum;
|
||||
import vip.xiaonuo.auth.core.enums.AuthPlatformEnum;
|
||||
import vip.xiaonuo.auth.core.enums.AuthPropertyEnum;
|
||||
import vip.xiaonuo.auth.core.enums.SaClientTypeEnum;
|
||||
import vip.xiaonuo.auth.core.protocol.AuthClientFactory;
|
||||
import vip.xiaonuo.auth.core.protocol.base.AuthBaseClient;
|
||||
import vip.xiaonuo.auth.modular.login.enums.AuthDeviceTypeEnum;
|
||||
import vip.xiaonuo.auth.modular.login.enums.AuthStrategyWhenNoUserWithPhoneOrEmailEnum;
|
||||
import vip.xiaonuo.auth.modular.login.param.AuthAccountPasswordLoginParam;
|
||||
import vip.xiaonuo.auth.modular.login.service.AuthService;
|
||||
import vip.xiaonuo.auth.modular.third.entity.AuthThirdUser;
|
||||
@@ -62,6 +63,7 @@ import vip.xiaonuo.dev.api.DevConfigApi;
|
||||
* @author xuyuxiang
|
||||
* @date 2022/7/8 16:20
|
||||
**/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class AuthThirdServiceImpl extends ServiceImpl<AuthThirdMapper, AuthThirdUser> implements AuthThirdService {
|
||||
|
||||
@@ -247,27 +249,37 @@ public class AuthThirdServiceImpl extends ServiceImpl<AuthThirdMapper, AuthThird
|
||||
// 校验state
|
||||
if(ObjectUtil.isEmpty(state)) {
|
||||
state = SaHolder.getRequest().getParam("RelayState");
|
||||
}
|
||||
// 定义登录端类型
|
||||
String clientType = SaClientTypeEnum.B.getValue();
|
||||
if(ObjectUtil.isNotEmpty(state)) {
|
||||
// 获取缓存操作类
|
||||
CommonCacheOperator commonCacheOperator = SpringUtil.getBean(CommonCacheOperator.class);
|
||||
// 获取缓存值
|
||||
Object stateCacheValueObj = commonCacheOperator.get(CONFIG_CACHE_KEY + state);
|
||||
// 判断是否为空
|
||||
if(ObjectUtil.isNotEmpty(stateCacheValueObj)){
|
||||
// 转换为json对象
|
||||
JSONObject stateCacheValueJsonObject = JSONUtil.parseObj(stateCacheValueObj);
|
||||
// 判断是否包含缓存值
|
||||
if(stateCacheValueJsonObject.containsKey("clientType")) {
|
||||
// 获取登录端类型
|
||||
clientType = stateCacheValueJsonObject.getStr("clientType");
|
||||
}
|
||||
// 移除缓存
|
||||
commonCacheOperator.remove(CONFIG_CACHE_KEY + state);
|
||||
if(ObjectUtil.isEmpty(state)) {
|
||||
throw new CommonException("state不能为空");
|
||||
}
|
||||
}
|
||||
// 获取缓存操作类
|
||||
CommonCacheOperator commonCacheOperator = SpringUtil.getBean(CommonCacheOperator.class);
|
||||
// 获取缓存值
|
||||
Object stateCacheValueObj = null;
|
||||
try {
|
||||
// 对state进行安全处理,防止特殊字符导致Redis异常
|
||||
if (state.length() > 500) {
|
||||
log.warn(">>> SSO state 过长({}字符),跳过Redis校验 state={}", state.length(), state.substring(0, 50) + "...");
|
||||
} else {
|
||||
stateCacheValueObj = commonCacheOperator.get(CONFIG_CACHE_KEY + state);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn(">>> SSO state Redis查询异常,跳过校验 state={}, error={}", state, e.getMessage());
|
||||
}
|
||||
// 默认登录端类型
|
||||
String clientType = SaClientTypeEnum.B.getValue();
|
||||
// 判断是否为空
|
||||
if(ObjectUtil.isNotEmpty(stateCacheValueObj)){
|
||||
// 转换为json对象
|
||||
JSONObject stateCacheValueJsonObject = JSONUtil.parseObj(stateCacheValueObj);
|
||||
// 获取登录端类型
|
||||
clientType = stateCacheValueJsonObject.getStr("clientType");
|
||||
// 移除缓存
|
||||
commonCacheOperator.remove(CONFIG_CACHE_KEY + state);
|
||||
} else {
|
||||
log.warn(">>> SSO state 校验失败(可能IdP未原样返回或门户发起登录),跳过校验 state={}", state);
|
||||
}
|
||||
// 执行请求
|
||||
AuthResponse<AuthUser> authResponse = authSourceBaseClient.doLogin();
|
||||
if (authResponse.ok()) {
|
||||
@@ -283,25 +295,18 @@ public class AuthThirdServiceImpl extends ServiceImpl<AuthThirdMapper, AuthThird
|
||||
// 定义系统用户id
|
||||
String userId;
|
||||
if(ObjectUtil.isEmpty(authThirdUser)) {
|
||||
// 如果用户不存在,则需要绑定用户,先将第三方用户id插入数据库
|
||||
String id = this.insertAuthThirdUser(authUser);
|
||||
// 返回
|
||||
return "needBind:" + id;
|
||||
// 如果三方用户不存在,自动创建本地用户并绑定
|
||||
userId = this.createAndBindUser(authUser, clientType);
|
||||
} else {
|
||||
// 否则直接获取用户id,判断是否存在(有可能没绑定)
|
||||
userId = authThirdUser.getUserId();
|
||||
if(ObjectUtil.isEmpty(userId)) {
|
||||
return "needBind:" + authThirdUser.getId();
|
||||
// 三方用户存在但未绑定本地用户,自动创建并绑定
|
||||
userId = this.createAndBindUserForExistThird(authUser, authThirdUser, clientType);
|
||||
}
|
||||
}
|
||||
// 定义生成的token
|
||||
String token;
|
||||
// 根据客户端类型执行登录,返回token
|
||||
if(SaClientTypeEnum.B.getValue().equals(clientType)) {
|
||||
return authService.doLoginById(userId, AuthDeviceTypeEnum.PC.getValue(), SaClientTypeEnum.B.getValue());
|
||||
} else {
|
||||
return authService.doLoginById(userId, AuthDeviceTypeEnum.PC.getValue(), SaClientTypeEnum.C.getValue());
|
||||
}
|
||||
// 已绑定用户,直接用userId登录
|
||||
return authService.doLoginById(userId, AuthDeviceTypeEnum.PC.getValue(), clientType);
|
||||
} else {
|
||||
throw new CommonException("第三方登录授权回调失败,原因:{}", authResponse.getMsg());
|
||||
}
|
||||
@@ -640,4 +645,133 @@ public class AuthThirdServiceImpl extends ServiceImpl<AuthThirdMapper, AuthThird
|
||||
}
|
||||
return authClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建本地用户并绑定三方用户(三方用户记录不存在的情况)
|
||||
*
|
||||
* @param authUser 第三方用户信息
|
||||
* @param clientType 登录端类型
|
||||
* @return 创建的本地用户ID
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2025/06/26
|
||||
*/
|
||||
private String createAndBindUser(AuthUser authUser, String clientType) {
|
||||
// 从第三方用户信息中提取字段
|
||||
String username = authUser.getUsername();
|
||||
String email = authUser.getEmail();
|
||||
String phone = authUser.getSource().equalsIgnoreCase("phone") ? authUser.getUsername() : null;
|
||||
|
||||
// 定义本地用户
|
||||
String userId;
|
||||
|
||||
// 根据登录端类型创建用户
|
||||
if(SaClientTypeEnum.B.getValue().equals(clientType)) {
|
||||
// B端用户创建逻辑
|
||||
if(StrUtil.isNotBlank(phone)) {
|
||||
userId = loginUserApi.createUserWithPhone(phone).getId();
|
||||
} else if(StrUtil.isNotBlank(email)) {
|
||||
userId = loginUserApi.createUserWithEmail(email).getId();
|
||||
} else if(StrUtil.isNotBlank(username)) {
|
||||
String randomPassword = RandomUtil.randomString(16);
|
||||
userId = loginUserApi.createUserWithAccount(username, randomPassword).getId();
|
||||
} else {
|
||||
String uuid = authUser.getUuid();
|
||||
String account = AuthAccountPrefixEnum.SSO.getValue() + (uuid.length() >= 8 ? uuid.substring(0, 8) : uuid);
|
||||
String randomPassword = RandomUtil.randomString(16);
|
||||
userId = loginUserApi.createUserWithAccount(account, randomPassword).getId();
|
||||
}
|
||||
} else {
|
||||
// C端用户创建逻辑
|
||||
if(StrUtil.isNotBlank(phone)) {
|
||||
userId = clientLoginUserApi.createClientUserWithPhone(phone).getId();
|
||||
} else if(StrUtil.isNotBlank(email)) {
|
||||
userId = clientLoginUserApi.createClientUserWithEmail(email).getId();
|
||||
} else if(StrUtil.isNotBlank(username)) {
|
||||
String randomPassword = RandomUtil.randomString(16);
|
||||
userId = clientLoginUserApi.createClientUserWithAccount(username, randomPassword).getId();
|
||||
} else {
|
||||
String uuid = authUser.getUuid();
|
||||
String account = AuthAccountPrefixEnum.SSO.getValue() + (uuid.length() >= 8 ? uuid.substring(0, 8) : uuid);
|
||||
String randomPassword = RandomUtil.randomString(16);
|
||||
userId = clientLoginUserApi.createClientUserWithAccount(account, randomPassword).getId();
|
||||
}
|
||||
}
|
||||
|
||||
// 插入三方用户记录并绑定
|
||||
AuthThirdUser authThirdUser = new AuthThirdUser();
|
||||
authThirdUser.setThirdId(authUser.getUuid());
|
||||
authThirdUser.setUserId(userId);
|
||||
authThirdUser.setAvatar(authUser.getAvatar());
|
||||
authThirdUser.setName(authUser.getUsername());
|
||||
authThirdUser.setNickname(authUser.getNickname());
|
||||
authThirdUser.setGender(authUser.getGender() != null ? authUser.getGender().getDesc() : "未知");
|
||||
authThirdUser.setCategory(authUser.getSource());
|
||||
authThirdUser.setExtJson(JSONUtil.toJsonStr(authUser.getRawUserInfo()));
|
||||
this.save(authThirdUser);
|
||||
|
||||
log.info(">>> SSO登录自动创建用户成功,userId={}, thirdId={}, source={}", userId, authUser.getUuid(), authUser.getSource());
|
||||
|
||||
return userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为已存在的三方用户创建本地用户并绑定(三方用户记录存在但未绑定的情况)
|
||||
*
|
||||
* @param authUser 第三方用户信息
|
||||
* @param authThirdUser 已存在的三方用户记录
|
||||
* @param clientType 登录端类型
|
||||
* @return 创建的本地用户ID
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2025/06/26
|
||||
*/
|
||||
private String createAndBindUserForExistThird(AuthUser authUser, AuthThirdUser authThirdUser, String clientType) {
|
||||
// 从第三方用户信息中提取字段
|
||||
String username = authUser.getUsername();
|
||||
String email = authUser.getEmail();
|
||||
String phone = authUser.getSource().equalsIgnoreCase("phone") ? authUser.getUsername() : null;
|
||||
|
||||
// 定义本地用户
|
||||
String userId;
|
||||
|
||||
// 根据登录端类型创建用户
|
||||
if(SaClientTypeEnum.B.getValue().equals(clientType)) {
|
||||
// B端用户创建逻辑
|
||||
if(StrUtil.isNotBlank(phone)) {
|
||||
userId = loginUserApi.createUserWithPhone(phone).getId();
|
||||
} else if(StrUtil.isNotBlank(email)) {
|
||||
userId = loginUserApi.createUserWithEmail(email).getId();
|
||||
} else if(StrUtil.isNotBlank(username)) {
|
||||
String randomPassword = RandomUtil.randomString(16);
|
||||
userId = loginUserApi.createUserWithAccount(username, randomPassword).getId();
|
||||
} else {
|
||||
String account = AuthAccountPrefixEnum.SSO.getValue() + (authUser.getUuid().length() >= 8 ? authUser.getUuid().substring(0, 8) : authUser.getUuid());
|
||||
String randomPassword = RandomUtil.randomString(16);
|
||||
userId = loginUserApi.createUserWithAccount(account, randomPassword).getId();
|
||||
}
|
||||
} else {
|
||||
// C端用户创建逻辑
|
||||
if(StrUtil.isNotBlank(phone)) {
|
||||
userId = clientLoginUserApi.createClientUserWithPhone(phone).getId();
|
||||
} else if(StrUtil.isNotBlank(email)) {
|
||||
userId = clientLoginUserApi.createClientUserWithEmail(email).getId();
|
||||
} else if(StrUtil.isNotBlank(username)) {
|
||||
String randomPassword = RandomUtil.randomString(16);
|
||||
userId = clientLoginUserApi.createClientUserWithAccount(username, randomPassword).getId();
|
||||
} else {
|
||||
String account = AuthAccountPrefixEnum.SSO.getValue() + (authUser.getUuid().length() >= 8 ? authUser.getUuid().substring(0, 8) : authUser.getUuid());
|
||||
String randomPassword = RandomUtil.randomString(16);
|
||||
userId = clientLoginUserApi.createClientUserWithAccount(account, randomPassword).getId();
|
||||
}
|
||||
}
|
||||
|
||||
// 更新三方用户记录,绑定userId
|
||||
authThirdUser.setUserId(userId);
|
||||
this.updateById(authThirdUser);
|
||||
|
||||
log.info(">>> SSO登录为已存在三方用户创建本地用户并绑定成功,userId={}, thirdId={}, source={}", userId, authUser.getUuid(), authUser.getSource());
|
||||
|
||||
return userId;
|
||||
}
|
||||
}
|
||||
|
||||
+14
-1
@@ -199,7 +199,8 @@ public class ClientLoginUserApiProvider implements SaBaseLoginUserApi {
|
||||
|
||||
@Override
|
||||
public SaBaseLoginUser createUserWithEmail(String email) {
|
||||
return null;
|
||||
// C端用户API不实现B端用户创建
|
||||
throw new UnsupportedOperationException("C端用户API不支持创建B端用户");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -208,6 +209,18 @@ public class ClientLoginUserApiProvider implements SaBaseLoginUserApi {
|
||||
return BeanUtil.copyProperties(clientUser, ClientLoginUser.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaBaseLoginUser createUserWithAccount(String account, String password) {
|
||||
// C端用户API不实现B端用户创建
|
||||
throw new UnsupportedOperationException("C端用户API不支持创建B端用户");
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaBaseClientLoginUser createClientUserWithAccount(String account, String password) {
|
||||
ClientUser clientUser = clientUserService.createUserWithAccount(account, password);
|
||||
return BeanUtil.copyProperties(clientUser, ClientLoginUser.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doRegister(String account, String password) {
|
||||
clientUserService.doRegister(account, password);
|
||||
|
||||
+14
-1
@@ -211,7 +211,20 @@ public class SysLoginUserApiProvider implements SaBaseLoginUserApi {
|
||||
|
||||
@Override
|
||||
public SaBaseClientLoginUser createClientUserWithEmail(String email) {
|
||||
return null;
|
||||
// B端用户API不实现C端用户创建
|
||||
throw new UnsupportedOperationException("B端用户API不支持创建C端用户");
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaBaseLoginUser createUserWithAccount(String account, String password) {
|
||||
SysUser sysUser = sysUserService.createUserWithAccount(account, password);
|
||||
return BeanUtil.copyProperties(sysUser, SysLoginUser.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaBaseClientLoginUser createClientUserWithAccount(String account, String password) {
|
||||
// B端用户API不实现C端用户创建
|
||||
throw new UnsupportedOperationException("B端用户API不支持创建C端用户");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user