Commit 705a5ff6459ec811264c66e501eae7c11fbcd93b
1 parent
6e404697
重构社交登录的时候,增加独立的社交绑定表
Showing
12 changed files
with
324 additions
and
368 deletions
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/social/SocialTypeEnum.java
| @@ -22,45 +22,48 @@ public enum SocialTypeEnum implements IntArrayValuable { | @@ -22,45 +22,48 @@ public enum SocialTypeEnum implements IntArrayValuable { | ||
| 22 | * Gitee | 22 | * Gitee |
| 23 | * 文档链接:https://gitee.com/api/v5/oauth_doc#/ | 23 | * 文档链接:https://gitee.com/api/v5/oauth_doc#/ |
| 24 | */ | 24 | */ |
| 25 | - GITEE(10, "GITEE"), | 25 | + GITEE(10, 1, "GITEE"), |
| 26 | /** | 26 | /** |
| 27 | * 钉钉 | 27 | * 钉钉 |
| 28 | * 文档链接:https://developers.dingtalk.com/document/app/obtain-identity-credentials | 28 | * 文档链接:https://developers.dingtalk.com/document/app/obtain-identity-credentials |
| 29 | */ | 29 | */ |
| 30 | - DINGTALK(20, "DINGTALK"), | 30 | + DINGTALK(20, 2, "DINGTALK"), |
| 31 | 31 | ||
| 32 | /** | 32 | /** |
| 33 | * 企业微信 | 33 | * 企业微信 |
| 34 | * 文档链接:https://xkcoding.com/2019/08/06/use-justauth-integration-wechat-enterprise.html | 34 | * 文档链接:https://xkcoding.com/2019/08/06/use-justauth-integration-wechat-enterprise.html |
| 35 | */ | 35 | */ |
| 36 | - WECHAT_ENTERPRISE(30, "WECHAT_ENTERPRISE"), | 36 | + WECHAT_ENTERPRISE(30, 3, "WECHAT_ENTERPRISE"), |
| 37 | /** | 37 | /** |
| 38 | * 微信公众平台 - 移动端 H5 | 38 | * 微信公众平台 - 移动端 H5 |
| 39 | * 文档链接:https://www.cnblogs.com/juewuzhe/p/11905461.html | 39 | * 文档链接:https://www.cnblogs.com/juewuzhe/p/11905461.html |
| 40 | */ | 40 | */ |
| 41 | - WECHAT_MP(31, "WECHAT_MP"), | 41 | + WECHAT_MP(31, 3, "WECHAT_MP"), |
| 42 | /** | 42 | /** |
| 43 | * 微信开放平台 - 网站应用 PC 端扫码授权登录 | 43 | * 微信开放平台 - 网站应用 PC 端扫码授权登录 |
| 44 | * 文档链接:https://justauth.wiki/guide/oauth/wechat_open/#_2-申请开发者资质认证 | 44 | * 文档链接:https://justauth.wiki/guide/oauth/wechat_open/#_2-申请开发者资质认证 |
| 45 | */ | 45 | */ |
| 46 | - WECHAT_OPEN(32, "WECHAT_OPEN"), | 46 | + WECHAT_OPEN(32, 3, "WECHAT_OPEN"), |
| 47 | /** | 47 | /** |
| 48 | * 微信小程序 | 48 | * 微信小程序 |
| 49 | * 文档链接:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html | 49 | * 文档链接:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html |
| 50 | */ | 50 | */ |
| 51 | - WECHAT_MINI_PROGRAM(33, "WECHAT_MINI_PROGRAM"), | 51 | + WECHAT_MINI_PROGRAM(33, 3, "WECHAT_MINI_PROGRAM"), |
| 52 | ; | 52 | ; |
| 53 | 53 | ||
| 54 | public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SocialTypeEnum::getType).toArray(); | 54 | public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SocialTypeEnum::getType).toArray(); |
| 55 | 55 | ||
| 56 | - public static final List<Integer> WECHAT_ALL = ListUtil.toList(WECHAT_ENTERPRISE.type, WECHAT_MP.type, WECHAT_OPEN.type, | ||
| 57 | - WECHAT_MINI_PROGRAM.type); | ||
| 58 | - | ||
| 59 | /** | 56 | /** |
| 60 | * 类型 | 57 | * 类型 |
| 61 | */ | 58 | */ |
| 62 | private final Integer type; | 59 | private final Integer type; |
| 63 | /** | 60 | /** |
| 61 | + * 平台 | ||
| 62 | + * | ||
| 63 | + * 例如说,微信平台下,有企业微信、公众平台、开放平台、小程序等 | ||
| 64 | + */ | ||
| 65 | + private final Integer platform; | ||
| 66 | + /** | ||
| 64 | * 类型的标识 | 67 | * 类型的标识 |
| 65 | */ | 68 | */ |
| 66 | private final String source; | 69 | private final String source; |
| @@ -74,11 +77,4 @@ public enum SocialTypeEnum implements IntArrayValuable { | @@ -74,11 +77,4 @@ public enum SocialTypeEnum implements IntArrayValuable { | ||
| 74 | return ArrayUtil.firstMatch(o -> o.getType().equals(type), values()); | 77 | return ArrayUtil.firstMatch(o -> o.getType().equals(type), values()); |
| 75 | } | 78 | } |
| 76 | 79 | ||
| 77 | - public static List<Integer> getRelationTypes(Integer type) { | ||
| 78 | - if (WECHAT_ALL.contains(type)) { | ||
| 79 | - return WECHAT_ALL; | ||
| 80 | - } | ||
| 81 | - return ListUtil.toList(type); | ||
| 82 | - } | ||
| 83 | - | ||
| 84 | } | 80 | } |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApiImpl.java
| @@ -38,7 +38,7 @@ public class SocialUserApiImpl implements SocialUserApi { | @@ -38,7 +38,7 @@ public class SocialUserApiImpl implements SocialUserApi { | ||
| 38 | 38 | ||
| 39 | @Override | 39 | @Override |
| 40 | public void checkSocialUser(Integer type, String code, String state) { | 40 | public void checkSocialUser(Integer type, String code, String state) { |
| 41 | - socialUserService.checkSocialUser(type, code, state); | 41 | + socialUserService.authSocialUser(type, code, state); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | @Override | 44 | @Override |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserBindDO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.dal.dataobject.social; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | ||
| 5 | +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; | ||
| 6 | +import com.baomidou.mybatisplus.annotation.TableName; | ||
| 7 | +import lombok.*; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 社交用户的绑定 | ||
| 11 | + * 即 {@link SocialUserDO} 与 UserDO 的关联表 | ||
| 12 | + * | ||
| 13 | + * @author 芋道源码 | ||
| 14 | + */ | ||
| 15 | +@TableName(value = "system_social_user_bind", autoResultMap = true) | ||
| 16 | +@Data | ||
| 17 | +@EqualsAndHashCode(callSuper = true) | ||
| 18 | +@Builder | ||
| 19 | +@NoArgsConstructor | ||
| 20 | +@AllArgsConstructor | ||
| 21 | +public class SocialUserBindDO extends BaseDO { | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * 关联的用户编号 | ||
| 25 | + */ | ||
| 26 | + private Long userId; | ||
| 27 | + /** | ||
| 28 | + * 用户类型 | ||
| 29 | + * | ||
| 30 | + * 枚举 {@link UserTypeEnum} | ||
| 31 | + */ | ||
| 32 | + private Integer userType; | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 社交平台 | ||
| 36 | + * | ||
| 37 | + * 枚举 {@link SocialTypeEnum#getPlatform()} | ||
| 38 | + */ | ||
| 39 | + private Integer platform; | ||
| 40 | + /** | ||
| 41 | + * 社交的全局编号 | ||
| 42 | + */ | ||
| 43 | + private String unionId; | ||
| 44 | + | ||
| 45 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserDO.java
| @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.dal.dataobject.social; | @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.dal.dataobject.social; | ||
| 3 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; | 3 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; |
| 4 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | 4 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 5 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 5 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 6 | +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; | ||
| 6 | import com.baomidou.mybatisplus.annotation.TableId; | 7 | import com.baomidou.mybatisplus.annotation.TableId; |
| 7 | import com.baomidou.mybatisplus.annotation.TableName; | 8 | import com.baomidou.mybatisplus.annotation.TableName; |
| 8 | import lombok.*; | 9 | import lombok.*; |
| @@ -27,20 +28,9 @@ public class SocialUserDO extends BaseDO { | @@ -27,20 +28,9 @@ public class SocialUserDO extends BaseDO { | ||
| 27 | @TableId | 28 | @TableId |
| 28 | private Long id; | 29 | private Long id; |
| 29 | /** | 30 | /** |
| 30 | - * 关联的用户编号 | ||
| 31 | - */ | ||
| 32 | - private Long userId; | ||
| 33 | - /** | ||
| 34 | - * 用户类型 | ||
| 35 | - * | ||
| 36 | - * 枚举 {@link UserTypeEnum} | ||
| 37 | - */ | ||
| 38 | - private Integer userType; | ||
| 39 | - | ||
| 40 | - /** | ||
| 41 | * 社交平台的类型 | 31 | * 社交平台的类型 |
| 42 | * | 32 | * |
| 43 | - * 枚举 {@link UserTypeEnum} | 33 | + * 枚举 {@link SocialTypeEnum} |
| 44 | */ | 34 | */ |
| 45 | private Integer type; | 35 | private Integer type; |
| 46 | 36 | ||
| @@ -77,6 +67,15 @@ public class SocialUserDO extends BaseDO { | @@ -77,6 +67,15 @@ public class SocialUserDO extends BaseDO { | ||
| 77 | */ | 67 | */ |
| 78 | private String rawUserInfo; | 68 | private String rawUserInfo; |
| 79 | 69 | ||
| 70 | + /** | ||
| 71 | + * 最后一次的认证 code | ||
| 72 | + */ | ||
| 73 | + private String code; | ||
| 74 | + /** | ||
| 75 | + * 最后一次的认证 state | ||
| 76 | + */ | ||
| 77 | + private String state; | ||
| 78 | + | ||
| 80 | } | 79 | } |
| 81 | 80 | ||
| 82 | 81 |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserBindMapper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.dal.mysql.social; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | ||
| 5 | +import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO; | ||
| 6 | +import org.apache.ibatis.annotations.Mapper; | ||
| 7 | + | ||
| 8 | +@Mapper | ||
| 9 | +public interface SocialUserBindMapper extends BaseMapperX<SocialUserBindDO> { | ||
| 10 | + | ||
| 11 | + default void deleteByUserTypeAndUserIdAndPlatformAndUnionId(Integer userType, Long userId, | ||
| 12 | + Integer platform, String unionId) { | ||
| 13 | + delete(new LambdaQueryWrapperX<SocialUserBindDO>() | ||
| 14 | + .eq(SocialUserBindDO::getUserType, userType) | ||
| 15 | + .eq(SocialUserBindDO::getUserId, userId) | ||
| 16 | + .eq(SocialUserBindDO::getPlatform, platform) | ||
| 17 | + .eq(SocialUserBindDO::getUnionId, unionId)); | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + default SocialUserBindDO selectByUserTypeAndPlatformAndUnionId(Integer userType, | ||
| 21 | + Integer platform, String unionId) { | ||
| 22 | + return selectOne(new LambdaQueryWrapperX<SocialUserBindDO>() | ||
| 23 | + .eq(SocialUserBindDO::getUserType, userType) | ||
| 24 | + .eq(SocialUserBindDO::getPlatform, platform) | ||
| 25 | + .eq(SocialUserBindDO::getUnionId, unionId)); | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserMapper.java
| @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.dal.mysql.social; | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.dal.mysql.social; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; | 3 | import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 5 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | ||
| 5 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | 6 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| 6 | import org.apache.ibatis.annotations.Mapper; | 7 | import org.apache.ibatis.annotations.Mapper; |
| 7 | 8 | ||
| @@ -11,14 +12,17 @@ import java.util.List; | @@ -11,14 +12,17 @@ import java.util.List; | ||
| 11 | @Mapper | 12 | @Mapper |
| 12 | public interface SocialUserMapper extends BaseMapperX<SocialUserDO> { | 13 | public interface SocialUserMapper extends BaseMapperX<SocialUserDO> { |
| 13 | 14 | ||
| 14 | - default List<SocialUserDO> selectListByTypeAndUnionId(Integer userType, Collection<Integer> types, String unionId) { | ||
| 15 | - return selectList(new QueryWrapper<SocialUserDO>().eq("user_type", userType) | ||
| 16 | - .in("type", types).eq("union_id", unionId)); | 15 | + default SocialUserDO selectByTypeAndCodeAnState(Integer type, String code, String state) { |
| 16 | + return selectOne(new LambdaQueryWrapper<SocialUserDO>() | ||
| 17 | + .eq(SocialUserDO::getType, type) | ||
| 18 | + .eq(SocialUserDO::getCode, code) | ||
| 19 | + .eq(SocialUserDO::getState, state)); | ||
| 17 | } | 20 | } |
| 18 | 21 | ||
| 19 | - default List<SocialUserDO> selectListByTypeAndUserId(Integer userType, Collection<Integer> types, Long userId) { | ||
| 20 | - return selectList(new QueryWrapper<SocialUserDO>().eq("user_type", userType) | ||
| 21 | - .in("type", types).eq("user_id", userId)); | 22 | + default SocialUserDO selectByTypeAndOpenid(Integer type, String openid) { |
| 23 | + return selectOne(new LambdaQueryWrapper<SocialUserDO>() | ||
| 24 | + .eq(SocialUserDO::getType, type) | ||
| 25 | + .eq(SocialUserDO::getCode, openid)); | ||
| 22 | } | 26 | } |
| 23 | 27 | ||
| 24 | default List<SocialUserDO> selectListByUserId(Integer userType, Long userId) { | 28 | default List<SocialUserDO> selectListByUserId(Integer userType, Long userId) { |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java
| @@ -23,10 +23,6 @@ public interface RedisKeyConstants { | @@ -23,10 +23,6 @@ public interface RedisKeyConstants { | ||
| 23 | "login_user:%s", // 参数为 sessionId | 23 | "login_user:%s", // 参数为 sessionId |
| 24 | STRING, LoginUser.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); | 24 | STRING, LoginUser.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); |
| 25 | 25 | ||
| 26 | - RedisKeyDefine SOCIAL_AUTH_USER = new RedisKeyDefine("社交登陆的授权用户", | ||
| 27 | - "social_auth_user:%d:%s", // 参数为 type,code | ||
| 28 | - STRING, AuthUser.class, Duration.ofDays(1)); | ||
| 29 | - | ||
| 30 | RedisKeyDefine SOCIAL_AUTH_STATE = new RedisKeyDefine("社交登陆的 state", // 注意,它是被 JustAuth 的 justauth.type.prefix 使用到 | 26 | RedisKeyDefine SOCIAL_AUTH_STATE = new RedisKeyDefine("社交登陆的 state", // 注意,它是被 JustAuth 的 justauth.type.prefix 使用到 |
| 31 | "social_auth_state:%s", // 参数为 state | 27 | "social_auth_state:%s", // 参数为 state |
| 32 | STRING, String.class, Duration.ofHours(24)); // 值为 state | 28 | STRING, String.class, Duration.ofHours(24)); // 值为 state |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/social/SocialAuthUserRedisDAO.java deleted
| 1 | -package cn.iocoder.yudao.module.system.dal.redis.social; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||
| 4 | -import me.zhyd.oauth.model.AuthCallback; | ||
| 5 | -import me.zhyd.oauth.model.AuthUser; | ||
| 6 | -import org.springframework.data.redis.core.StringRedisTemplate; | ||
| 7 | -import org.springframework.stereotype.Repository; | ||
| 8 | - | ||
| 9 | -import javax.annotation.Resource; | ||
| 10 | - | ||
| 11 | -import static cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants.SOCIAL_AUTH_USER; | ||
| 12 | - | ||
| 13 | - | ||
| 14 | -/** | ||
| 15 | - * 社交 {@link me.zhyd.oauth.model.AuthUser} 的 RedisDAO | ||
| 16 | - * | ||
| 17 | - * @author 芋道源码 | ||
| 18 | - */ | ||
| 19 | -@Repository | ||
| 20 | -public class SocialAuthUserRedisDAO { | ||
| 21 | - | ||
| 22 | - @Resource | ||
| 23 | - private StringRedisTemplate stringRedisTemplate; | ||
| 24 | - | ||
| 25 | - public AuthUser get(Integer type, AuthCallback authCallback) { | ||
| 26 | - String redisKey = formatKey(type, authCallback.getCode()); | ||
| 27 | - return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), AuthUser.class); | ||
| 28 | - } | ||
| 29 | - | ||
| 30 | - public void set(Integer type, AuthCallback authCallback, AuthUser authUser) { | ||
| 31 | - String redisKey = formatKey(type, authCallback.getCode()); | ||
| 32 | - stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(authUser), SOCIAL_AUTH_USER.getTimeout()); | ||
| 33 | - } | ||
| 34 | - | ||
| 35 | - private static String formatKey(Integer type, String code) { | ||
| 36 | - return String.format(SOCIAL_AUTH_USER.getKeyTemplate(), type, code); | ||
| 37 | - } | ||
| 38 | - | ||
| 39 | -} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
| @@ -219,8 +219,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { | @@ -219,8 +219,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { | ||
| 219 | @Override | 219 | @Override |
| 220 | public String socialLogin2(AuthSocialLogin2ReqVO reqVO, String userIp, String userAgent) { | 220 | public String socialLogin2(AuthSocialLogin2ReqVO reqVO, String userIp, String userAgent) { |
| 221 | // 使用 code 授权码,进行登录 | 221 | // 使用 code 授权码,进行登录 |
| 222 | - AuthUser authUser = socialUserService.getAuthUser(reqVO.getType(), reqVO.getCode(), reqVO.getState()); | ||
| 223 | - Assert.notNull(authUser, "授权用户不为空"); | 222 | + socialUserService.authSocialUser(reqVO.getType(), reqVO.getCode(), reqVO.getState()); |
| 224 | 223 | ||
| 225 | // 使用账号密码,进行登录。 | 224 | // 使用账号密码,进行登录。 |
| 226 | LoginUser loginUser = this.login0(reqVO.getUsername(), reqVO.getPassword()); | 225 | LoginUser loginUser = this.login0(reqVO.getUsername(), reqVO.getPassword()); |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserService.java
| 1 | package cn.iocoder.yudao.module.system.service.social; | 1 | package cn.iocoder.yudao.module.system.service.social; |
| 2 | 2 | ||
| 3 | -import cn.hutool.core.util.StrUtil; | ||
| 4 | import cn.iocoder.yudao.framework.common.exception.ServiceException; | 3 | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| 5 | import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; | 4 | import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; |
| 6 | import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; | 5 | import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; |
| 7 | import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; | 6 | import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; |
| 8 | -import me.zhyd.oauth.model.AuthUser; | ||
| 9 | 7 | ||
| 10 | import javax.validation.Valid; | 8 | import javax.validation.Valid; |
| 11 | import javax.validation.constraints.NotNull; | 9 | import javax.validation.constraints.NotNull; |
| @@ -28,7 +26,7 @@ public interface SocialUserService { | @@ -28,7 +26,7 @@ public interface SocialUserService { | ||
| 28 | String getAuthorizeUrl(Integer type, String redirectUri); | 26 | String getAuthorizeUrl(Integer type, String redirectUri); |
| 29 | 27 | ||
| 30 | /** | 28 | /** |
| 31 | - * 获得授权的用户 | 29 | + * 授权获得对应的社交用户 |
| 32 | * 如果授权失败,则会抛出 {@link ServiceException} 异常 | 30 | * 如果授权失败,则会抛出 {@link ServiceException} 异常 |
| 33 | * | 31 | * |
| 34 | * @param type 社交平台的类型 {@link SocialTypeEnum} | 32 | * @param type 社交平台的类型 {@link SocialTypeEnum} |
| @@ -37,17 +35,7 @@ public interface SocialUserService { | @@ -37,17 +35,7 @@ public interface SocialUserService { | ||
| 37 | * @return 授权用户 | 35 | * @return 授权用户 |
| 38 | */ | 36 | */ |
| 39 | @NotNull | 37 | @NotNull |
| 40 | - AuthUser getAuthUser(Integer type, String code, String state); | ||
| 41 | - | ||
| 42 | - /** | ||
| 43 | - * 获得社交用户的 unionId 编号 | ||
| 44 | - * | ||
| 45 | - * @param authUser 社交用户 | ||
| 46 | - * @return unionId 编号 | ||
| 47 | - */ | ||
| 48 | - default String getAuthUserUnionId(AuthUser authUser) { | ||
| 49 | - return StrUtil.blankToDefault(authUser.getToken().getUnionId(), authUser.getUuid()); | ||
| 50 | - } | 38 | + SocialUserDO authSocialUser(Integer type, String code, String state); |
| 51 | 39 | ||
| 52 | /** | 40 | /** |
| 53 | * 获得指定用户的社交用户列表 | 41 | * 获得指定用户的社交用户列表 |
| @@ -71,25 +59,14 @@ public interface SocialUserService { | @@ -71,25 +59,14 @@ public interface SocialUserService { | ||
| 71 | * @param userId 用户编号 | 59 | * @param userId 用户编号 |
| 72 | * @param userType 全局用户类型 | 60 | * @param userType 全局用户类型 |
| 73 | * @param type 社交平台的类型 {@link SocialTypeEnum} | 61 | * @param type 社交平台的类型 {@link SocialTypeEnum} |
| 74 | - * @param unionId 社交平台的 unionId | ||
| 75 | - */ | ||
| 76 | - void unbindSocialUser(Long userId, Integer userType, Integer type, String unionId); | ||
| 77 | - | ||
| 78 | - /** | ||
| 79 | - * 校验社交用户的认证信息是否正确 | ||
| 80 | - * 如果校验不通过,则抛出 {@link ServiceException} 业务异常 | ||
| 81 | - * | ||
| 82 | - * @param type 社交平台的类型 | ||
| 83 | - * @param code 授权码 | ||
| 84 | - * @param state state | 62 | + * @param openid 社交平台的 openid |
| 85 | */ | 63 | */ |
| 86 | - void checkSocialUser(Integer type, String code, String state); | 64 | + void unbindSocialUser(Long userId, Integer userType, Integer type, String openid); |
| 87 | 65 | ||
| 88 | /** | 66 | /** |
| 89 | * 获得社交用户的绑定用户编号 | 67 | * 获得社交用户的绑定用户编号 |
| 90 | * 注意,返回的是 MemberUser 或者 AdminUser 的 id 编号! | 68 | * 注意,返回的是 MemberUser 或者 AdminUser 的 id 编号! |
| 91 | - * 该方法会执行和 {@link #checkSocialUser(Integer, String, String)} 一样的逻辑。 | ||
| 92 | - * 所以在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常 | 69 | + * 在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常 |
| 93 | * | 70 | * |
| 94 | * @param userType 用户类型 | 71 | * @param userType 用户类型 |
| 95 | * @param type 社交平台的类型 | 72 | * @param type 社交平台的类型 |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java
| 1 | package cn.iocoder.yudao.module.system.service.social; | 1 | package cn.iocoder.yudao.module.system.service.social; |
| 2 | 2 | ||
| 3 | -import cn.hutool.core.collection.CollUtil; | ||
| 4 | -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | 3 | +import cn.hutool.core.lang.Assert; |
| 4 | +import cn.hutool.core.util.StrUtil; | ||
| 5 | import cn.iocoder.yudao.framework.common.util.http.HttpUtils; | 5 | import cn.iocoder.yudao.framework.common.util.http.HttpUtils; |
| 6 | import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; | 6 | import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; |
| 7 | +import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO; | ||
| 7 | import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; | 8 | import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; |
| 9 | +import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper; | ||
| 8 | import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper; | 10 | import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper; |
| 9 | -import cn.iocoder.yudao.module.system.dal.redis.social.SocialAuthUserRedisDAO; | ||
| 10 | import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; | 11 | import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; |
| 11 | -import com.google.common.annotations.VisibleForTesting; | ||
| 12 | import com.xkcoding.justauth.AuthRequestFactory; | 12 | import com.xkcoding.justauth.AuthRequestFactory; |
| 13 | import lombok.extern.slf4j.Slf4j; | 13 | import lombok.extern.slf4j.Slf4j; |
| 14 | import me.zhyd.oauth.model.AuthCallback; | 14 | import me.zhyd.oauth.model.AuthCallback; |
| @@ -22,7 +22,6 @@ import org.springframework.validation.annotation.Validated; | @@ -22,7 +22,6 @@ import org.springframework.validation.annotation.Validated; | ||
| 22 | 22 | ||
| 23 | import javax.annotation.Resource; | 23 | import javax.annotation.Resource; |
| 24 | import java.util.List; | 24 | import java.util.List; |
| 25 | -import java.util.Objects; | ||
| 26 | 25 | ||
| 27 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 26 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 28 | import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; | 27 | import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; |
| @@ -42,8 +41,7 @@ public class SocialUserServiceImpl implements SocialUserService { | @@ -42,8 +41,7 @@ public class SocialUserServiceImpl implements SocialUserService { | ||
| 42 | private AuthRequestFactory authRequestFactory; | 41 | private AuthRequestFactory authRequestFactory; |
| 43 | 42 | ||
| 44 | @Resource | 43 | @Resource |
| 45 | - private SocialAuthUserRedisDAO authSocialUserRedisDAO; | ||
| 46 | - | 44 | + private SocialUserBindMapper socialUserBindMapper; |
| 47 | @Resource | 45 | @Resource |
| 48 | private SocialUserMapper socialUserMapper; | 46 | private SocialUserMapper socialUserMapper; |
| 49 | 47 | ||
| @@ -57,149 +55,85 @@ public class SocialUserServiceImpl implements SocialUserService { | @@ -57,149 +55,85 @@ public class SocialUserServiceImpl implements SocialUserService { | ||
| 57 | } | 55 | } |
| 58 | 56 | ||
| 59 | @Override | 57 | @Override |
| 60 | - public AuthUser getAuthUser(Integer type, String code, String state) { | ||
| 61 | - AuthCallback authCallback = buildAuthCallback(code, state); | ||
| 62 | - // 从缓存中获取 | ||
| 63 | - AuthUser authUser = authSocialUserRedisDAO.get(type, authCallback); | ||
| 64 | - if (authUser != null) { | ||
| 65 | - return authUser; | 58 | + public SocialUserDO authSocialUser(Integer type, String code, String state) { |
| 59 | + // 优先从 DB 中获取,因为 code 有且可以使用一次。 | ||
| 60 | + // 在社交登录时,当未绑定 User 时,需要绑定登录,此时需要 code 使用两次 | ||
| 61 | + SocialUserDO socialUser = socialUserMapper.selectByTypeAndCodeAnState(type, code, state); | ||
| 62 | + if (socialUser != null) { | ||
| 63 | + return socialUser; | ||
| 66 | } | 64 | } |
| 67 | 65 | ||
| 68 | // 请求获取 | 66 | // 请求获取 |
| 69 | - authUser = this.getAuthUser0(type, authCallback); | ||
| 70 | - // 缓存。原因是 code 有且可以使用一次。在社交登录时,当未绑定 User 时,需要绑定登录,此时需要 code 使用两次 | ||
| 71 | - authSocialUserRedisDAO.set(type, authCallback, authUser); | ||
| 72 | - return authUser; | ||
| 73 | - } | ||
| 74 | - | ||
| 75 | - /** | ||
| 76 | - * 获得 unionId 对应的某个社交平台的“所有”社交用户 | ||
| 77 | - * 注意,这里的“所有”,指的是类似【微信】平台,包括了小程序、公众号、PC 网站,他们的 unionId 是一致的 | ||
| 78 | - * | ||
| 79 | - * @param type 社交平台的类型 {@link SocialTypeEnum} | ||
| 80 | - * @param unionId 社交平台的 unionId | ||
| 81 | - * @param userType 全局用户类型 | ||
| 82 | - * @return 社交用户列表 | ||
| 83 | - */ | ||
| 84 | - private List<SocialUserDO> getAllSocialUserList(Integer type, String unionId, Integer userType) { | ||
| 85 | - List<Integer> types = SocialTypeEnum.getRelationTypes(type); | ||
| 86 | - return socialUserMapper.selectListByTypeAndUnionId(userType, types, unionId); | ||
| 87 | - } | ||
| 88 | - | ||
| 89 | - @Override | ||
| 90 | - public List<SocialUserDO> getSocialUserList(Long userId, Integer userType) { | ||
| 91 | - return socialUserMapper.selectListByUserId(userType, userId); | ||
| 92 | - } | ||
| 93 | - | ||
| 94 | - @Override | ||
| 95 | - public void bindSocialUser(SocialUserBindReqDTO reqDTO) { | ||
| 96 | - // 使用 code 授权 | ||
| 97 | - AuthUser authUser = getAuthUser(reqDTO.getType(), reqDTO.getCode(), | ||
| 98 | - reqDTO.getState()); | 67 | + AuthUser authUser = getAuthUser(type, buildAuthCallback(code, state)); |
| 99 | if (authUser == null) { | 68 | if (authUser == null) { |
| 100 | throw exception(SOCIAL_USER_NOT_FOUND); | 69 | throw exception(SOCIAL_USER_NOT_FOUND); |
| 101 | } | 70 | } |
| 102 | 71 | ||
| 103 | - // 绑定社交用户(新增) | ||
| 104 | - bindSocialUser(reqDTO.getUserId(), reqDTO.getUserType(), | ||
| 105 | - reqDTO.getType(), authUser); | ||
| 106 | - } | ||
| 107 | - | ||
| 108 | - /** | ||
| 109 | - * 绑定社交用户 | ||
| 110 | - * @param userId 用户编号 | ||
| 111 | - * @param userType 用户类型 | ||
| 112 | - * @param type 社交平台的类型 {@link SocialTypeEnum} | ||
| 113 | - * @param authUser 授权用户 | ||
| 114 | - */ | ||
| 115 | - @Transactional(rollbackFor = Exception.class) | ||
| 116 | - protected void bindSocialUser(Long userId, Integer userType, Integer type, AuthUser authUser) { | ||
| 117 | - // 获得 unionId 对应的 SocialUserDO 列表 | ||
| 118 | - String unionId = getAuthUserUnionId(authUser); | ||
| 119 | - List<SocialUserDO> socialUsers = this.getAllSocialUserList(type, unionId, userType); | ||
| 120 | - | ||
| 121 | - // 逻辑一:如果 userId 之前绑定过该 type 的其它账号,需要进行解绑 | ||
| 122 | - this.unbindOldSocialUser(userId, userType, type, unionId); | ||
| 123 | - | ||
| 124 | - // 逻辑二:如果 socialUsers 指定的 userId 改变,需要进行更新 | ||
| 125 | - // 例如说,一个微信 unionId 对应了多个社交账号,结果其中有个关联了新的 userId,则其它也要跟着修改 | ||
| 126 | - // 考虑到 socialUsers 一般比较少,直接 for 循环更新即可 | ||
| 127 | - socialUsers.forEach(socialUser -> { | ||
| 128 | - if (Objects.equals(socialUser.getUserId(), userId)) { | ||
| 129 | - return; | ||
| 130 | - } | ||
| 131 | - socialUserMapper.updateById(new SocialUserDO().setId(socialUser.getId()).setUserId(userId)); | ||
| 132 | - }); | ||
| 133 | - | ||
| 134 | - // 逻辑三:如果 authUser 不存在于 socialUsers 中,则进行新增;否则,进行更新 | ||
| 135 | - SocialUserDO socialUser = CollUtil.findOneByField(socialUsers, "openid", authUser.getUuid()); | ||
| 136 | - SocialUserDO saveSocialUser = SocialUserDO.builder() // 新增和更新的通用属性 | ||
| 137 | - .token(authUser.getToken().getAccessToken()).rawTokenInfo(toJsonString(authUser.getToken())) | ||
| 138 | - .nickname(authUser.getNickname()).avatar(authUser.getAvatar()).rawUserInfo(toJsonString(authUser.getRawUserInfo())) | ||
| 139 | - .build(); | 72 | + // 保存到 DB 中 |
| 73 | + socialUser = socialUserMapper.selectByTypeAndOpenid(type, authUser.getUuid()); | ||
| 140 | if (socialUser == null) { | 74 | if (socialUser == null) { |
| 141 | - saveSocialUser.setUserId(userId).setUserType(userType) | ||
| 142 | - .setType(type).setOpenid(authUser.getUuid()).setUnionId(unionId); | ||
| 143 | - socialUserMapper.insert(saveSocialUser); | 75 | + socialUser = new SocialUserDO(); |
| 76 | + } | ||
| 77 | + socialUser.setOpenid(authUser.getUuid()).setToken(authUser.getToken().getAccessToken()).setRawTokenInfo((toJsonString(authUser.getToken()))) | ||
| 78 | + .setUnionId(StrUtil.blankToDefault(authUser.getToken().getUnionId(), authUser.getUuid())) // unionId 识别多个用户 | ||
| 79 | + .setNickname(authUser.getNickname()).setAvatar(authUser.getAvatar()).setRawUserInfo(toJsonString(authUser.getRawUserInfo())) | ||
| 80 | + .setCode(code).setState(state); // 需要保存 code + state 字段,保证后续可查询 | ||
| 81 | + if (socialUser.getId() == null) { | ||
| 82 | + socialUserMapper.insert(socialUser); | ||
| 144 | } else { | 83 | } else { |
| 145 | - saveSocialUser.setId(socialUser.getId()); | ||
| 146 | - socialUserMapper.updateById(saveSocialUser); | 84 | + socialUserMapper.updateById(socialUser); |
| 147 | } | 85 | } |
| 86 | + return socialUser; | ||
| 148 | } | 87 | } |
| 149 | 88 | ||
| 150 | @Override | 89 | @Override |
| 151 | - public void unbindSocialUser(Long userId, Integer userType, Integer type, String unionId) { | ||
| 152 | - // 获得 unionId 对应的所有 SocialUserDO 社交用户 | ||
| 153 | - List<SocialUserDO> socialUsers = this.getAllSocialUserList(type, unionId, userType); | ||
| 154 | - if (CollUtil.isEmpty(socialUsers)) { | ||
| 155 | - return; | ||
| 156 | - } | ||
| 157 | - // 校验,是否解绑的是非自己的 | ||
| 158 | - socialUsers.forEach(socialUser -> { | ||
| 159 | - if (!Objects.equals(socialUser.getUserId(), userId)) { | ||
| 160 | - throw exception(SOCIAL_USER_UNBIND_NOT_SELF); | ||
| 161 | - } | ||
| 162 | - }); | ||
| 163 | - | ||
| 164 | - // 解绑 | ||
| 165 | - socialUserMapper.deleteBatchIds(CollectionUtils.convertSet(socialUsers, SocialUserDO::getId)); | 90 | + public List<SocialUserDO> getSocialUserList(Long userId, Integer userType) { |
| 91 | + return socialUserMapper.selectListByUserId(userType, userId); | ||
| 166 | } | 92 | } |
| 167 | 93 | ||
| 168 | @Override | 94 | @Override |
| 169 | - public void checkSocialUser(Integer type, String code, String state) { | ||
| 170 | - AuthUser authUser = getAuthUser(type, code, state); | ||
| 171 | - if (authUser == null) { | ||
| 172 | - throw exception(SOCIAL_USER_NOT_FOUND); | ||
| 173 | - } | 95 | + @Transactional |
| 96 | + public void bindSocialUser(SocialUserBindReqDTO reqDTO) { | ||
| 97 | + // 获得社交用户 | ||
| 98 | + SocialUserDO socialUser = authSocialUser(reqDTO.getType(), reqDTO.getCode(), reqDTO.getState()); | ||
| 99 | + Assert.notNull(socialUser, "社交用户不能为空"); | ||
| 100 | + | ||
| 101 | + // 如果 userId 之前绑定过该 type 的其它账号,需要进行解绑 | ||
| 102 | + socialUserBindMapper.deleteByUserTypeAndUserIdAndPlatformAndUnionId(reqDTO.getUserType(), reqDTO.getUserId(), | ||
| 103 | + SocialTypeEnum.valueOfType(socialUser.getType()).getPlatform(), socialUser.getUnionId()); | ||
| 104 | + | ||
| 105 | + // 绑定当前登录的社交用户 | ||
| 106 | + SocialUserBindDO socialUserBind = SocialUserBindDO.builder().userId(reqDTO.getUserId()).userType(reqDTO.getUserType()) | ||
| 107 | + .unionId(socialUser.getUnionId()).build(); | ||
| 108 | + socialUserBindMapper.insert(socialUserBind); | ||
| 174 | } | 109 | } |
| 175 | 110 | ||
| 176 | @Override | 111 | @Override |
| 177 | - public Long getBindUserId(Integer userType, Integer type, String code, String state) { | ||
| 178 | - AuthUser authUser = getAuthUser(type, code, state); | ||
| 179 | - if (authUser == null) { | 112 | + public void unbindSocialUser(Long userId, Integer userType, Integer type, String openid) { |
| 113 | + // 获得 openid 对应的 SocialUserDO 社交用户 | ||
| 114 | + SocialUserDO socialUser = socialUserMapper.selectByTypeAndOpenid(type, openid); | ||
| 115 | + if (socialUser == null) { | ||
| 180 | throw exception(SOCIAL_USER_NOT_FOUND); | 116 | throw exception(SOCIAL_USER_NOT_FOUND); |
| 181 | } | 117 | } |
| 182 | 118 | ||
| 183 | - // 如果未绑定 SocialUserDO 用户,则无法自动登录,进行报错 | ||
| 184 | - String unionId = getAuthUserUnionId(authUser); | ||
| 185 | - List<SocialUserDO> socialUsers = getAllSocialUserList(type, unionId, userType); | ||
| 186 | - if (CollUtil.isEmpty(socialUsers)) { | ||
| 187 | - throw exception(AUTH_THIRD_LOGIN_NOT_BIND); | ||
| 188 | - } | ||
| 189 | - return socialUsers.get(0).getUserId(); | 119 | + // 获得对应的社交绑定关系 |
| 120 | + socialUserBindMapper.deleteByUserTypeAndUserIdAndPlatformAndUnionId(userType, userId, | ||
| 121 | + SocialTypeEnum.valueOfType(socialUser.getType()).getPlatform(), socialUser.getUnionId()); | ||
| 190 | } | 122 | } |
| 191 | 123 | ||
| 192 | - @VisibleForTesting | ||
| 193 | - public void unbindOldSocialUser(Long userId, Integer userType, Integer type, String newUnionId) { | ||
| 194 | - List<Integer> types = SocialTypeEnum.getRelationTypes(type); | ||
| 195 | - List<SocialUserDO> oldSocialUsers = socialUserMapper.selectListByTypeAndUserId(userType, types, userId); | ||
| 196 | - // 如果新老的 unionId 是一致的,说明无需解绑 | ||
| 197 | - if (CollUtil.isEmpty(oldSocialUsers) || Objects.equals(newUnionId, oldSocialUsers.get(0).getUnionId())) { | ||
| 198 | - return; | 124 | + @Override |
| 125 | + public Long getBindUserId(Integer userType, Integer type, String code, String state) { | ||
| 126 | + // 获得社交用户 | ||
| 127 | + SocialUserDO socialUser = authSocialUser(type, code, state); | ||
| 128 | + Assert.notNull(socialUser, "社交用户不能为空"); | ||
| 129 | + | ||
| 130 | + // 如果未绑定的社交用户,则无法自动登录,进行报错 | ||
| 131 | + SocialUserBindDO socialUserBind = socialUserBindMapper.selectByUserTypeAndPlatformAndUnionId(userType, | ||
| 132 | + SocialTypeEnum.valueOfType(socialUser.getType()).getPlatform(), socialUser.getUnionId()); | ||
| 133 | + if (socialUserBind == null) { | ||
| 134 | + throw exception(AUTH_THIRD_LOGIN_NOT_BIND); | ||
| 199 | } | 135 | } |
| 200 | - | ||
| 201 | - // 解绑 | ||
| 202 | - socialUserMapper.deleteBatchIds(CollectionUtils.convertSet(oldSocialUsers, SocialUserDO::getId)); | 136 | + return socialUserBind.getUserId(); |
| 203 | } | 137 | } |
| 204 | 138 | ||
| 205 | /** | 139 | /** |
| @@ -209,7 +143,7 @@ public class SocialUserServiceImpl implements SocialUserService { | @@ -209,7 +143,7 @@ public class SocialUserServiceImpl implements SocialUserService { | ||
| 209 | * @param authCallback 授权回调 | 143 | * @param authCallback 授权回调 |
| 210 | * @return 授权的用户 | 144 | * @return 授权的用户 |
| 211 | */ | 145 | */ |
| 212 | - private AuthUser getAuthUser0(Integer type, AuthCallback authCallback) { | 146 | + private AuthUser getAuthUser(Integer type, AuthCallback authCallback) { |
| 213 | AuthRequest authRequest = authRequestFactory.get(SocialTypeEnum.valueOfType(type).getSource()); | 147 | AuthRequest authRequest = authRequestFactory.get(SocialTypeEnum.valueOfType(type).getSource()); |
| 214 | AuthResponse<?> authResponse = authRequest.login(authCallback); | 148 | AuthResponse<?> authResponse = authRequest.login(authCallback); |
| 215 | log.info("[getAuthUser0][请求社交平台 type({}) request({}) response({})]", type, toJsonString(authCallback), | 149 | log.info("[getAuthUser0][请求社交平台 type({}) request({}) response({})]", type, toJsonString(authCallback), |
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceTest.java
| 1 | package cn.iocoder.yudao.module.system.service.social; | 1 | package cn.iocoder.yudao.module.system.service.social; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; | ||
| 4 | import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper; | 3 | import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper; |
| 5 | -import cn.iocoder.yudao.module.system.dal.redis.social.SocialAuthUserRedisDAO; | ||
| 6 | -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; | ||
| 7 | -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||
| 8 | import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; | 4 | import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; |
| 5 | +import com.baomidou.mybatisplus.core.toolkit.PluginUtils; | ||
| 9 | import com.xkcoding.justauth.AuthRequestFactory; | 6 | import com.xkcoding.justauth.AuthRequestFactory; |
| 10 | -import me.zhyd.oauth.model.AuthUser; | 7 | +import me.zhyd.oauth.request.AuthRequest; |
| 8 | +import me.zhyd.oauth.utils.AuthStateUtils; | ||
| 11 | import org.junit.jupiter.api.Test; | 9 | import org.junit.jupiter.api.Test; |
| 10 | +import org.mockito.MockedStatic; | ||
| 12 | import org.springframework.boot.test.mock.mockito.MockBean; | 11 | import org.springframework.boot.test.mock.mockito.MockBean; |
| 13 | import org.springframework.context.annotation.Import; | 12 | import org.springframework.context.annotation.Import; |
| 14 | 13 | ||
| 15 | import javax.annotation.Resource; | 14 | import javax.annotation.Resource; |
| 16 | -import java.util.List; | ||
| 17 | 15 | ||
| 18 | import static cn.hutool.core.util.RandomUtil.randomEle; | 16 | import static cn.hutool.core.util.RandomUtil.randomEle; |
| 19 | import static cn.hutool.core.util.RandomUtil.randomString; | 17 | import static cn.hutool.core.util.RandomUtil.randomString; |
| 20 | -import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; | ||
| 21 | -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; | ||
| 22 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | 18 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
| 23 | import static org.junit.jupiter.api.Assertions.assertEquals; | 19 | import static org.junit.jupiter.api.Assertions.assertEquals; |
| 20 | +import static org.mockito.Mockito.*; | ||
| 24 | 21 | ||
| 25 | -@Import({SocialUserServiceImpl.class, SocialAuthUserRedisDAO.class}) | 22 | +@Import(SocialUserServiceImpl.class) |
| 26 | public class SocialUserServiceTest extends BaseDbAndRedisUnitTest { | 23 | public class SocialUserServiceTest extends BaseDbAndRedisUnitTest { |
| 27 | 24 | ||
| 28 | @Resource | 25 | @Resource |
| 29 | - private SocialUserServiceImpl socialService; | 26 | + private SocialUserServiceImpl socialUserService; |
| 30 | 27 | ||
| 31 | @Resource | 28 | @Resource |
| 32 | private SocialUserMapper socialUserMapper; | 29 | private SocialUserMapper socialUserMapper; |
| @@ -34,132 +31,152 @@ public class SocialUserServiceTest extends BaseDbAndRedisUnitTest { | @@ -34,132 +31,152 @@ public class SocialUserServiceTest extends BaseDbAndRedisUnitTest { | ||
| 34 | @MockBean | 31 | @MockBean |
| 35 | private AuthRequestFactory authRequestFactory; | 32 | private AuthRequestFactory authRequestFactory; |
| 36 | 33 | ||
| 37 | - /** | ||
| 38 | - * 情况一,创建 SocialUserDO 的情况 | ||
| 39 | - */ | ||
| 40 | @Test | 34 | @Test |
| 41 | - public void testBindSocialUser_create() { | ||
| 42 | - // mock 数据 | ||
| 43 | - // 准备参数 | ||
| 44 | - Long userId = randomLongId(); | ||
| 45 | - Integer type = randomEle(SocialTypeEnum.values()).getType(); | ||
| 46 | - AuthUser authUser = randomPojo(AuthUser.class); | ||
| 47 | - // mock 方法 | ||
| 48 | - | ||
| 49 | - // 调用 | ||
| 50 | - socialService.bindSocialUser(userId, UserTypeEnum.ADMIN.getValue(), type, authUser); | ||
| 51 | - // 断言 | ||
| 52 | - List<SocialUserDO> socialUsers = socialUserMapper.selectList("user_id", userId); | ||
| 53 | - assertEquals(1, socialUsers.size()); | ||
| 54 | - assertBindSocialUser(socialUsers.get(0), authUser, userId, type); | ||
| 55 | - } | ||
| 56 | - | ||
| 57 | - /** | ||
| 58 | - * 情况二,更新 SocialUserDO 的情况 | ||
| 59 | - */ | ||
| 60 | - @Test | ||
| 61 | - public void testBindSocialUser_update() { | ||
| 62 | - // mock 数据 | ||
| 63 | - SocialUserDO dbSocialUser = randomPojo(SocialUserDO.class, socialUserDO -> { | ||
| 64 | - socialUserDO.setUserType(UserTypeEnum.ADMIN.getValue()); | ||
| 65 | - socialUserDO.setType(randomEle(SocialTypeEnum.values()).getType()); | ||
| 66 | - }); | ||
| 67 | - socialUserMapper.insert(dbSocialUser); | ||
| 68 | - // 准备参数 | ||
| 69 | - Long userId = dbSocialUser.getUserId(); | ||
| 70 | - Integer type = dbSocialUser.getType(); | ||
| 71 | - AuthUser authUser = randomPojo(AuthUser.class); | ||
| 72 | - // mock 方法 | ||
| 73 | - | ||
| 74 | - // 调用 | ||
| 75 | - socialService.bindSocialUser(userId, UserTypeEnum.ADMIN.getValue(), type, authUser); | ||
| 76 | - // 断言 | ||
| 77 | - List<SocialUserDO> socialUsers = socialUserMapper.selectList("user_id", userId); | ||
| 78 | - assertEquals(1, socialUsers.size()); | ||
| 79 | - assertBindSocialUser(socialUsers.get(0), authUser, userId, type); | 35 | + public void testGetAuthorizeUrl() { |
| 36 | + try (MockedStatic<AuthStateUtils> authStateUtilsMock = mockStatic(AuthStateUtils.class)) { | ||
| 37 | + // 准备参数 | ||
| 38 | + Integer type = 31; | ||
| 39 | + String redirectUri = "sss"; | ||
| 40 | + // mock 获得对应的 AuthRequest 实现 | ||
| 41 | + AuthRequest authRequest = mock(AuthRequest.class); | ||
| 42 | + when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest); | ||
| 43 | + // mock 方法 | ||
| 44 | + authStateUtilsMock.when(AuthStateUtils::createState).thenReturn("aoteman"); | ||
| 45 | + when(authRequest.authorize(eq("aoteman"))).thenReturn("https://www.iocoder.cn?redirect_uri=yyy"); | ||
| 46 | + | ||
| 47 | + // 调用 | ||
| 48 | + String url = socialUserService.getAuthorizeUrl(type, redirectUri); | ||
| 49 | + // 断言 | ||
| 50 | + assertEquals("https://www.iocoder.cn/?redirect_uri=sss", url); | ||
| 51 | + } | ||
| 80 | } | 52 | } |
| 81 | 53 | ||
| 82 | - /** | ||
| 83 | - * 情况一和二都存在的,逻辑二的场景 | ||
| 84 | - */ | ||
| 85 | - @Test | ||
| 86 | - public void testBindSocialUser_userId() { | ||
| 87 | - // mock 数据 | ||
| 88 | - SocialUserDO dbSocialUser = randomPojo(SocialUserDO.class, socialUserDO -> { | ||
| 89 | - socialUserDO.setUserType(UserTypeEnum.ADMIN.getValue()); | ||
| 90 | - socialUserDO.setType(randomEle(SocialTypeEnum.values()).getType()); | ||
| 91 | - }); | ||
| 92 | - socialUserMapper.insert(dbSocialUser); | ||
| 93 | - // 准备参数 | ||
| 94 | - Long userId = randomLongId(); | ||
| 95 | - Integer type = dbSocialUser.getType(); | ||
| 96 | - AuthUser authUser = randomPojo(AuthUser.class); | ||
| 97 | - // mock 方法 | ||
| 98 | - | ||
| 99 | - // 调用 | ||
| 100 | - socialService.bindSocialUser(userId, UserTypeEnum.ADMIN.getValue(), type, authUser); | ||
| 101 | - // 断言 | ||
| 102 | - List<SocialUserDO> socialUsers = socialUserMapper.selectList("user_id", userId); | ||
| 103 | - assertEquals(1, socialUsers.size()); | ||
| 104 | - } | ||
| 105 | - | ||
| 106 | - private void assertBindSocialUser(SocialUserDO socialUser, AuthUser authUser, Long userId, | ||
| 107 | - Integer type) { | ||
| 108 | - assertEquals(authUser.getToken().getAccessToken(), socialUser.getToken()); | ||
| 109 | - assertEquals(toJsonString(authUser.getToken()), socialUser.getRawTokenInfo()); | ||
| 110 | - assertEquals(authUser.getNickname(), socialUser.getNickname()); | ||
| 111 | - assertEquals(authUser.getAvatar(), socialUser.getAvatar()); | ||
| 112 | - assertEquals(toJsonString(authUser.getRawUserInfo()), socialUser.getRawUserInfo()); | ||
| 113 | - assertEquals(userId, socialUser.getUserId()); | ||
| 114 | - assertEquals(UserTypeEnum.ADMIN.getValue(), socialUser.getUserType()); | ||
| 115 | - assertEquals(type, socialUser.getType()); | ||
| 116 | - assertEquals(authUser.getUuid(), socialUser.getOpenid()); | ||
| 117 | - assertEquals(socialService.getAuthUserUnionId(authUser), socialUser.getUnionId()); | ||
| 118 | - } | ||
| 119 | - | ||
| 120 | - /** | ||
| 121 | - * 情况一,如果新老的 unionId 是一致的,无需解绑 | ||
| 122 | - */ | ||
| 123 | - @Test | ||
| 124 | - public void testUnbindOldSocialUser_no() { | ||
| 125 | - // mock 数据 | ||
| 126 | - SocialUserDO oldSocialUser = randomPojo(SocialUserDO.class, socialUserDO -> { | ||
| 127 | - socialUserDO.setUserType(UserTypeEnum.ADMIN.getValue()); | ||
| 128 | - socialUserDO.setType(randomEle(SocialTypeEnum.values()).getType()); | ||
| 129 | - }); | ||
| 130 | - socialUserMapper.insert(oldSocialUser); | ||
| 131 | - // 准备参数 | ||
| 132 | - Long userId = oldSocialUser.getUserId(); | ||
| 133 | - Integer type = oldSocialUser.getType(); | ||
| 134 | - String newUnionId = oldSocialUser.getUnionId(); | ||
| 135 | - | ||
| 136 | - // 调用 | ||
| 137 | - socialService.unbindOldSocialUser(userId, UserTypeEnum.ADMIN.getValue(), type, newUnionId); | ||
| 138 | - // 断言 | ||
| 139 | - assertEquals(1L, socialUserMapper.selectCount(null).longValue()); | ||
| 140 | - } | ||
| 141 | - | ||
| 142 | - | ||
| 143 | - /** | ||
| 144 | - * 情况二,如果新老的 unionId 不一致的,需解绑 | ||
| 145 | - */ | ||
| 146 | - @Test | ||
| 147 | - public void testUnbindOldSocialUser_yes() { | ||
| 148 | - // mock 数据 | ||
| 149 | - SocialUserDO oldSocialUser = randomPojo(SocialUserDO.class, socialUserDO -> { | ||
| 150 | - socialUserDO.setUserType(UserTypeEnum.ADMIN.getValue()); | ||
| 151 | - socialUserDO.setType(randomEle(SocialTypeEnum.values()).getType()); | ||
| 152 | - }); | ||
| 153 | - socialUserMapper.insert(oldSocialUser); | ||
| 154 | - // 准备参数 | ||
| 155 | - Long userId = oldSocialUser.getUserId(); | ||
| 156 | - Integer type = oldSocialUser.getType(); | ||
| 157 | - String newUnionId = randomString(10); | ||
| 158 | - | ||
| 159 | - // 调用 | ||
| 160 | - socialService.unbindOldSocialUser(userId, UserTypeEnum.ADMIN.getValue(), type, newUnionId); | ||
| 161 | - // 断言 | ||
| 162 | - assertEquals(0L, socialUserMapper.selectCount(null).longValue()); | ||
| 163 | - } | 54 | +// /** |
| 55 | +// * 情况一,创建 SocialUserDO 的情况 | ||
| 56 | +// */ | ||
| 57 | +// @Test | ||
| 58 | +// public void testBindSocialUser_create() { | ||
| 59 | +// // mock 数据 | ||
| 60 | +// // 准备参数 | ||
| 61 | +// Long userId = randomLongId(); | ||
| 62 | +// Integer type = randomEle(SocialTypeEnum.values()).getType(); | ||
| 63 | +// AuthUser authUser = randomPojo(AuthUser.class); | ||
| 64 | +// // mock 方法 | ||
| 65 | +// | ||
| 66 | +// // 调用 | ||
| 67 | +// socialService.bindSocialUser(userId, UserTypeEnum.ADMIN.getValue(), type, authUser); | ||
| 68 | +// // 断言 | ||
| 69 | +// List<SocialUserDO> socialUsers = socialUserMapper.selectList("user_id", userId); | ||
| 70 | +// assertEquals(1, socialUsers.size()); | ||
| 71 | +// assertBindSocialUser(socialUsers.get(0), authUser, userId, type); | ||
| 72 | +// } | ||
| 73 | +// | ||
| 74 | +// /** | ||
| 75 | +// * 情况二,更新 SocialUserDO 的情况 | ||
| 76 | +// */ | ||
| 77 | +// @Test | ||
| 78 | +// public void testBindSocialUser_update() { | ||
| 79 | +// // mock 数据 | ||
| 80 | +// SocialUserDO dbSocialUser = randomPojo(SocialUserDO.class, socialUserDO -> { | ||
| 81 | +// socialUserDO.setUserType(UserTypeEnum.ADMIN.getValue()); | ||
| 82 | +// socialUserDO.setType(randomEle(SocialTypeEnum.values()).getType()); | ||
| 83 | +// }); | ||
| 84 | +// socialUserMapper.insert(dbSocialUser); | ||
| 85 | +// // 准备参数 | ||
| 86 | +// Long userId = dbSocialUser.getUserId(); | ||
| 87 | +// Integer type = dbSocialUser.getType(); | ||
| 88 | +// AuthUser authUser = randomPojo(AuthUser.class); | ||
| 89 | +// // mock 方法 | ||
| 90 | +// | ||
| 91 | +// // 调用 | ||
| 92 | +// socialService.bindSocialUser(userId, UserTypeEnum.ADMIN.getValue(), type, authUser); | ||
| 93 | +// // 断言 | ||
| 94 | +// List<SocialUserDO> socialUsers = socialUserMapper.selectList("user_id", userId); | ||
| 95 | +// assertEquals(1, socialUsers.size()); | ||
| 96 | +// assertBindSocialUser(socialUsers.get(0), authUser, userId, type); | ||
| 97 | +// } | ||
| 98 | +// | ||
| 99 | +// /** | ||
| 100 | +// * 情况一和二都存在的,逻辑二的场景 | ||
| 101 | +// */ | ||
| 102 | +// @Test | ||
| 103 | +// public void testBindSocialUser_userId() { | ||
| 104 | +// // mock 数据 | ||
| 105 | +// SocialUserDO dbSocialUser = randomPojo(SocialUserDO.class, socialUserDO -> { | ||
| 106 | +// socialUserDO.setUserType(UserTypeEnum.ADMIN.getValue()); | ||
| 107 | +// socialUserDO.setType(randomEle(SocialTypeEnum.values()).getType()); | ||
| 108 | +// }); | ||
| 109 | +// socialUserMapper.insert(dbSocialUser); | ||
| 110 | +// // 准备参数 | ||
| 111 | +// Long userId = randomLongId(); | ||
| 112 | +// Integer type = dbSocialUser.getType(); | ||
| 113 | +// AuthUser authUser = randomPojo(AuthUser.class); | ||
| 114 | +// // mock 方法 | ||
| 115 | +// | ||
| 116 | +// // 调用 | ||
| 117 | +// socialService.bindSocialUser(userId, UserTypeEnum.ADMIN.getValue(), type, authUser); | ||
| 118 | +// // 断言 | ||
| 119 | +// List<SocialUserDO> socialUsers = socialUserMapper.selectList("user_id", userId); | ||
| 120 | +// assertEquals(1, socialUsers.size()); | ||
| 121 | +// } | ||
| 122 | +// | ||
| 123 | +// private void assertBindSocialUser(SocialUserDO socialUser, AuthUser authUser, Long userId, | ||
| 124 | +// Integer type) { | ||
| 125 | +// assertEquals(authUser.getToken().getAccessToken(), socialUser.getToken()); | ||
| 126 | +// assertEquals(toJsonString(authUser.getToken()), socialUser.getRawTokenInfo()); | ||
| 127 | +// assertEquals(authUser.getNickname(), socialUser.getNickname()); | ||
| 128 | +// assertEquals(authUser.getAvatar(), socialUser.getAvatar()); | ||
| 129 | +// assertEquals(toJsonString(authUser.getRawUserInfo()), socialUser.getRawUserInfo()); | ||
| 130 | +// assertEquals(userId, socialUser.getUserId()); | ||
| 131 | +// assertEquals(UserTypeEnum.ADMIN.getValue(), socialUser.getUserType()); | ||
| 132 | +// assertEquals(type, socialUser.getType()); | ||
| 133 | +// assertEquals(authUser.getUuid(), socialUser.getOpenid()); | ||
| 134 | +// assertEquals(socialService.getAuthUserUnionId(authUser), socialUser.getUnionId()); | ||
| 135 | +// } | ||
| 136 | +// | ||
| 137 | +// /** | ||
| 138 | +// * 情况一,如果新老的 unionId 是一致的,无需解绑 | ||
| 139 | +// */ | ||
| 140 | +// @Test | ||
| 141 | +// public void testUnbindOldSocialUser_no() { | ||
| 142 | +// // mock 数据 | ||
| 143 | +// SocialUserDO oldSocialUser = randomPojo(SocialUserDO.class, socialUserDO -> { | ||
| 144 | +// socialUserDO.setUserType(UserTypeEnum.ADMIN.getValue()); | ||
| 145 | +// socialUserDO.setType(randomEle(SocialTypeEnum.values()).getType()); | ||
| 146 | +// }); | ||
| 147 | +// socialUserMapper.insert(oldSocialUser); | ||
| 148 | +// // 准备参数 | ||
| 149 | +// Long userId = oldSocialUser.getUserId(); | ||
| 150 | +// Integer type = oldSocialUser.getType(); | ||
| 151 | +// String newUnionId = oldSocialUser.getUnionId(); | ||
| 152 | +// | ||
| 153 | +// // 调用 | ||
| 154 | +// socialService.unbindOldSocialUser(userId, UserTypeEnum.ADMIN.getValue(), type, newUnionId); | ||
| 155 | +// // 断言 | ||
| 156 | +// assertEquals(1L, socialUserMapper.selectCount(null).longValue()); | ||
| 157 | +// } | ||
| 158 | +// | ||
| 159 | +// | ||
| 160 | +// /** | ||
| 161 | +// * 情况二,如果新老的 unionId 不一致的,需解绑 | ||
| 162 | +// */ | ||
| 163 | +// @Test | ||
| 164 | +// public void testUnbindOldSocialUser_yes() { | ||
| 165 | +// // mock 数据 | ||
| 166 | +// SocialUserDO oldSocialUser = randomPojo(SocialUserDO.class, socialUserDO -> { | ||
| 167 | +// socialUserDO.setUserType(UserTypeEnum.ADMIN.getValue()); | ||
| 168 | +// socialUserDO.setType(randomEle(SocialTypeEnum.values()).getType()); | ||
| 169 | +// }); | ||
| 170 | +// socialUserMapper.insert(oldSocialUser); | ||
| 171 | +// // 准备参数 | ||
| 172 | +// Long userId = oldSocialUser.getUserId(); | ||
| 173 | +// Integer type = oldSocialUser.getType(); | ||
| 174 | +// String newUnionId = randomString(10); | ||
| 175 | +// | ||
| 176 | +// // 调用 | ||
| 177 | +// socialService.unbindOldSocialUser(userId, UserTypeEnum.ADMIN.getValue(), type, newUnionId); | ||
| 178 | +// // 断言 | ||
| 179 | +// assertEquals(0L, socialUserMapper.selectCount(null).longValue()); | ||
| 180 | +// } | ||
| 164 | 181 | ||
| 165 | } | 182 | } |