mirror of
https://gitee.com/xiaonuobase/snowy.git
synced 2026-03-22 10:47:16 +08:00
【升级】嵌入模式完成登录交互,可以将本系统嵌入到三方系统内
This commit is contained in:
@@ -182,13 +182,26 @@ public class AuthController {
|
||||
return CommonResult.data(authService.doLoginByOtp(authOtpLoginParam, SaClientTypeEnum.B.getValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* B端第三方Token交换登录(用于iframe嵌入免登)
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2026/2/11
|
||||
**/
|
||||
@ApiOperationSupport(order = 11)
|
||||
@Operation(summary = "B端第三方Token交换登录(iframe嵌入免登)")
|
||||
@PostMapping("/auth/b/doLoginByThirdToken")
|
||||
public CommonResult<String> doLoginByThirdToken(@RequestBody @Valid AuthThirdTokenLoginParam authThirdTokenLoginParam) {
|
||||
return CommonResult.data(authService.doLoginByThirdToken(authThirdTokenLoginParam));
|
||||
}
|
||||
|
||||
/**
|
||||
* B端判断是否登录
|
||||
*
|
||||
* @author xuyuxiang
|
||||
* @date 2021/10/15 13:12
|
||||
**/
|
||||
@ApiOperationSupport(order = 11)
|
||||
@ApiOperationSupport(order = 12)
|
||||
@Operation(summary = "B端判断是否登录")
|
||||
@GetMapping("/auth/b/isLogin")
|
||||
public CommonResult<Boolean> isLogin() {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.modular.login.param;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* 第三方Token交换登录参数(用于iframe嵌入免登)
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2026/2/11
|
||||
**/
|
||||
@Getter
|
||||
@Setter
|
||||
public class AuthThirdTokenLoginParam {
|
||||
|
||||
/** 第三方系统的accessToken */
|
||||
@Schema(description = "第三方系统的accessToken", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotBlank(message = "accessToken不能为空")
|
||||
private String accessToken;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.modular.login.prop;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 第三方系统Token交换配置属性(用于iframe嵌入免登)
|
||||
*
|
||||
* 对接规范:甲方提供一个用户信息接口,GET请求,Header传 Authorization: Bearer {accessToken}
|
||||
* 必须返回如下JSON格式:
|
||||
* {
|
||||
* "code": 200,
|
||||
* "data": {
|
||||
* "account": "zhangsan",
|
||||
* "email": "zhangsan@xxx.com",
|
||||
* "phone": "13800138000"
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @author yubaoshan
|
||||
* @date 2026/2/11
|
||||
**/
|
||||
@Getter
|
||||
@Setter
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "snowy.third-auth")
|
||||
public class AuthThirdClientProperties {
|
||||
|
||||
/** 第三方用户信息接口地址(甲方提供),配置了即代表开启,不配置即关闭 */
|
||||
private String userInfoUrl;
|
||||
}
|
||||
@@ -145,6 +145,16 @@ public interface AuthService {
|
||||
**/
|
||||
void validValidCode(String phoneOrEmail, String validCode, String validCodeReqNo);
|
||||
|
||||
/**
|
||||
* 通过第三方Token交换登录(用于iframe嵌入免登)
|
||||
*
|
||||
* @param authThirdTokenLoginParam 第三方Token登录参数
|
||||
* @return 本系统Token
|
||||
* @author yubaoshan
|
||||
* @date 2026/2/11
|
||||
*/
|
||||
String doLoginByThirdToken(AuthThirdTokenLoginParam authThirdTokenLoginParam);
|
||||
|
||||
/**
|
||||
* 获取B端验证码是否开启
|
||||
*
|
||||
|
||||
@@ -22,6 +22,8 @@ import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.PhoneUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
@@ -54,6 +56,7 @@ import vip.xiaonuo.dev.api.DevConfigApi;
|
||||
import vip.xiaonuo.dev.api.DevEmailApi;
|
||||
import vip.xiaonuo.dev.api.DevSmsApi;
|
||||
import vip.xiaonuo.sys.api.SysUserApi;
|
||||
import vip.xiaonuo.auth.modular.login.prop.AuthThirdClientProperties;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -175,6 +178,9 @@ public class AuthServiceImpl implements AuthService {
|
||||
@Resource
|
||||
private ClientUserApi clientUserApi;
|
||||
|
||||
@Resource
|
||||
private AuthThirdClientProperties authThirdClientProperties;
|
||||
|
||||
@Override
|
||||
public AuthPicValidCodeResult getPicCaptcha(String type) {
|
||||
// 生成验证码,随机4位字符
|
||||
@@ -1178,6 +1184,92 @@ public class AuthServiceImpl implements AuthService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String doLoginByThirdToken(AuthThirdTokenLoginParam authThirdTokenLoginParam) {
|
||||
// 校验是否配置了第三方用户信息接口地址(配置了即开启)
|
||||
String userInfoUrl = authThirdClientProperties.getUserInfoUrl();
|
||||
if(ObjectUtil.isEmpty(userInfoUrl)) {
|
||||
throw new CommonException("未配置第三方用户信息接口地址,无法使用Token交换登录");
|
||||
}
|
||||
// 用accessToken调用第三方用户信息接口
|
||||
JSONObject thirdUserInfo = requestThirdUserInfo(userInfoUrl, authThirdTokenLoginParam.getAccessToken());
|
||||
// 从第三方返回中解析用户信息(固定字段:account、email、phone)
|
||||
String thirdAccount = thirdUserInfo.getStr("account");
|
||||
String thirdEmail = thirdUserInfo.getStr("email");
|
||||
String thirdPhone = thirdUserInfo.getStr("phone");
|
||||
// 三级降级匹配本地用户:account → email → phone
|
||||
SaBaseLoginUser saBaseLoginUser = matchLocalUser(thirdAccount, thirdEmail, thirdPhone);
|
||||
// 执行B端登录
|
||||
return execLoginB(saBaseLoginUser, AuthDeviceTypeEnum.PC.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用第三方用户信息接口
|
||||
* 规范:GET请求,Header传Authorization: Bearer xxx
|
||||
* 返回格式:{code:200, data:{account,email,phone}}
|
||||
*/
|
||||
private JSONObject requestThirdUserInfo(String userInfoUrl, String accessToken) {
|
||||
try {
|
||||
HttpResponse response = HttpRequest.get(userInfoUrl)
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.timeout(10000)
|
||||
.execute();
|
||||
if(!response.isOk()) {
|
||||
throw new CommonException("调用第三方用户信息接口失败,HTTP状态码:{}", response.getStatus());
|
||||
}
|
||||
String body = response.body();
|
||||
if(ObjectUtil.isEmpty(body)) {
|
||||
throw new CommonException("第三方用户信息接口返回为空");
|
||||
}
|
||||
JSONObject resultJson = JSONUtil.parseObj(body);
|
||||
// 校验code
|
||||
Integer code = resultJson.getInt("code");
|
||||
if(code == null || code != 200) {
|
||||
throw new CommonException("第三方用户信息接口返回业务异常,code={}", code);
|
||||
}
|
||||
// 提取data
|
||||
JSONObject dataJson = resultJson.getJSONObject("data");
|
||||
if(ObjectUtil.isEmpty(dataJson)) {
|
||||
throw new CommonException("第三方用户信息接口返回数据中未找到data字段");
|
||||
}
|
||||
return dataJson;
|
||||
} catch (CommonException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new CommonException("调用第三方用户信息接口异常:{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 三级降级匹配本地用户:account → email → phone
|
||||
*/
|
||||
private SaBaseLoginUser matchLocalUser(String thirdAccount, String thirdEmail, String thirdPhone) {
|
||||
// 优先通过account查找
|
||||
if(StrUtil.isNotBlank(thirdAccount)) {
|
||||
SaBaseLoginUser user = loginUserApi.getUserByAccount(thirdAccount);
|
||||
if(ObjectUtil.isNotEmpty(user)) {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
// 其次通过email查找
|
||||
if(StrUtil.isNotBlank(thirdEmail)) {
|
||||
SaBaseLoginUser user = loginUserApi.getUserByEmail(thirdEmail);
|
||||
if(ObjectUtil.isNotEmpty(user)) {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
// 最后通过phone查找
|
||||
if(StrUtil.isNotBlank(thirdPhone)) {
|
||||
SaBaseLoginUser user = loginUserApi.getUserByPhone(thirdPhone);
|
||||
if(ObjectUtil.isNotEmpty(user)) {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
// 三种方式都找不到
|
||||
throw new CommonException("第三方用户(account={}, email={}, phone={})在本系统中未找到对应用户,请联系管理员同步用户",
|
||||
thirdAccount, thirdEmail, thirdPhone);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验是否开启注册
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user