From 595ceac03403952e10f1878c409b2092f2eb124b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BF=9E=E5=AE=9D=E5=B1=B1?= <1253070437@qq.com> Date: Sat, 27 Jun 2026 16:18:30 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E5=A2=9E=E5=BC=BA=E3=80=91=E5=A2=9E?= =?UTF-8?q?=E5=BC=BAOIDC/SSO=E7=99=BB=E5=BD=95=E5=AE=B9=E9=94=99=E8=83=BD?= =?UTF-8?q?=E5=8A=9B=EF=BC=9AAuthOidcCommonRequest=E5=A2=9E=E5=8A=A0GET/PO?= =?UTF-8?q?ST=E9=99=8D=E7=BA=A7=E3=80=81=E8=AF=A6=E7=BB=86=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E5=92=8Cuuid=E7=A9=BA=E5=80=BC=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=EF=BC=9BAuthOidcClient=E5=A2=9E=E5=8A=A0state=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E5=A4=B1=E8=B4=A5=E9=99=8D=E7=BA=A7=E5=A4=84=E7=90=86?= =?UTF-8?q?=EF=BC=9BAuthThirdServiceImpl=E5=A2=9E=E5=8A=A0state=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E5=BC=82=E5=B8=B8=E5=AE=B9=E9=94=99=E5=92=8CRedis?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E5=BC=82=E5=B8=B8=E6=8D=95=E8=8E=B7=EF=BC=9B?= =?UTF-8?q?=E5=90=8C=E6=97=B6=E4=BF=AE=E5=A4=8Dusername=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E8=B5=8B=E5=80=BC=E5=92=8CSSO=E8=B4=A6=E5=8F=B7=E5=89=8D?= =?UTF-8?q?=E7=BC=80=E6=9E=9A=E4=B8=BE=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xiaonuo/auth/api/SaBaseLoginUserApi.java | 16 ++ .../core/enums/AuthAccountPrefixEnum.java | 36 ++++ .../core/protocol/oidc/AuthOidcClient.java | 59 ++++- .../oidc/common/AuthOidcCommonRequest.java | 65 +++++- .../service/impl/AuthThirdServiceImpl.java | 202 +++++++++++++++--- .../provider/ClientLoginUserApiProvider.java | 15 +- .../provider/SysLoginUserApiProvider.java | 15 +- 7 files changed, 365 insertions(+), 43 deletions(-) create mode 100644 snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/core/enums/AuthAccountPrefixEnum.java diff --git a/snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/api/SaBaseLoginUserApi.java b/snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/api/SaBaseLoginUserApi.java index 1dad9799..4335ecc6 100644 --- a/snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/api/SaBaseLoginUserApi.java +++ b/snowy-plugin-api/snowy-plugin-auth-api/src/main/java/vip/xiaonuo/auth/api/SaBaseLoginUserApi.java @@ -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); + /** * 执行注册 * diff --git a/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/core/enums/AuthAccountPrefixEnum.java b/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/core/enums/AuthAccountPrefixEnum.java new file mode 100644 index 00000000..52a43cab --- /dev/null +++ b/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/core/enums/AuthAccountPrefixEnum.java @@ -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; + } +} diff --git a/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/core/protocol/oidc/AuthOidcClient.java b/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/core/protocol/oidc/AuthOidcClient.java index 34913b6e..e204e79e 100644 --- a/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/core/protocol/oidc/AuthOidcClient.java +++ b/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/core/protocol/oidc/AuthOidcClient.java @@ -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 { throw new CommonException("state不能为空"); } AuthRequest authRequest = this.getAuthRequest(); - AuthResponse authResponse = authRequest.login(AuthCallback.builder().code(code).state(state).build()); - if(!authResponse.ok()) { - throw new CommonException(authResponse.getMsg()); + + // 尝试正常登录流程(包含state校验) + try { + AuthResponse 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 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.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); } } diff --git a/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/core/protocol/oidc/common/AuthOidcCommonRequest.java b/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/core/protocol/oidc/common/AuthOidcCommonRequest.java index b6c75d0b..dbdb1397 100644 --- a/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/core/protocol/oidc/common/AuthOidcCommonRequest.java +++ b/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/core/protocol/oidc/common/AuthOidcCommonRequest.java @@ -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(); } diff --git a/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/service/impl/AuthThirdServiceImpl.java b/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/service/impl/AuthThirdServiceImpl.java index 96f977a3..8e009812 100644 --- a/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/service/impl/AuthThirdServiceImpl.java +++ b/snowy-plugin/snowy-plugin-auth/src/main/java/vip/xiaonuo/auth/modular/third/service/impl/AuthThirdServiceImpl.java @@ -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 implements AuthThirdService { @@ -247,27 +249,37 @@ public class AuthThirdServiceImpl extends ServiceImpl 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 authResponse = authSourceBaseClient.doLogin(); if (authResponse.ok()) { @@ -283,25 +295,18 @@ public class AuthThirdServiceImpl extends ServiceImpl= 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; + } } diff --git a/snowy-plugin/snowy-plugin-client/src/main/java/vip/xiaonuo/client/modular/user/provider/ClientLoginUserApiProvider.java b/snowy-plugin/snowy-plugin-client/src/main/java/vip/xiaonuo/client/modular/user/provider/ClientLoginUserApiProvider.java index 27c5cc13..2c9009d7 100644 --- a/snowy-plugin/snowy-plugin-client/src/main/java/vip/xiaonuo/client/modular/user/provider/ClientLoginUserApiProvider.java +++ b/snowy-plugin/snowy-plugin-client/src/main/java/vip/xiaonuo/client/modular/user/provider/ClientLoginUserApiProvider.java @@ -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); diff --git a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/user/provider/SysLoginUserApiProvider.java b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/user/provider/SysLoginUserApiProvider.java index 03a27d12..a67198ee 100644 --- a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/user/provider/SysLoginUserApiProvider.java +++ b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/user/provider/SysLoginUserApiProvider.java @@ -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