From fcf1c228c58a727497bcc005f4ef5f06d733647e Mon Sep 17 00:00:00 2001 From: timfruit Date: Tue, 26 Oct 2021 21:35:06 +0800 Subject: [PATCH] 初步完成微信公众号登录 --- yudao-admin-server/pom.xml | 6 ------ yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/SysAuthController.java | 5 +++-- yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/vo/auth/SysAuthSocialBindReqVO.java | 2 +- yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/vo/auth/SysAuthSocialLogin2ReqVO.java | 2 +- yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/vo/auth/SysAuthSocialLoginReqVO.java | 2 +- yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/dataobject/social/SysSocialUserDO.java | 82 ---------------------------------------------------------------------------------- yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/mysql/social/SysSocialUserMapper.java | 28 ---------------------------- yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/redis/SysRedisKeyConstants.java | 7 ------- yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/redis/social/SysSocialAuthUserRedisDAO.java | 38 -------------------------------------- yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/SysErrorCodeConstants.java | 4 +--- yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/social/SysSocialTypeEnum.java | 55 ------------------------------------------------------- yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java | 16 +++++++++------- yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/social/SysSocialService.java | 80 -------------------------------------------------------------------------------- yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/social/impl/SysSocialServiceImpl.java | 178 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- yudao-core-service/pom.xml | 6 ++++++ yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/dataobject/social/SysSocialUserDO.java | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/mysql/social/SysSocialUserMapper.java | 28 ++++++++++++++++++++++++++++ yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/redis/SysRedisKeyCoreConstants.java | 10 ++++++++++ yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/redis/social/SysSocialAuthUserRedisDAO.java | 39 +++++++++++++++++++++++++++++++++++++++ yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/enums/SysErrorCodeConstants.java | 5 +++++ yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/enums/social/SysSocialTypeEnum.java | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/social/SysSocialService.java | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/social/impl/SysSocialServiceImpl.java | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/enums/social/SysSocialTypeEnum.java | 55 ------------------------------------------------------- yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.java | 48 +++++++++++++++++++++++++++++++----------------- yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialBindReqVO.java | 2 +- yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialLogin2ReqVO.java | 2 +- yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialLoginReqVO.java | 2 +- yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialUnbindReqVO.java | 2 +- yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/SysErrorCodeConstants.java | 4 ++++ yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/SysAuthService.java | 32 ++++++++++++++++++++++++++++++-- yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/impl/SysAuthServiceImpl.java | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- yudao-user-server/src/main/resources/application-dev.yaml | 27 +++++++++++++++++++++++++++ yudao-user-server/src/main/resources/application-local.yaml | 26 ++++++++++++++++++++++++++ 34 files changed, 701 insertions(+), 570 deletions(-) delete mode 100644 yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/dataobject/social/SysSocialUserDO.java delete mode 100644 yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/mysql/social/SysSocialUserMapper.java delete mode 100644 yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/redis/social/SysSocialAuthUserRedisDAO.java delete mode 100644 yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/social/SysSocialTypeEnum.java delete mode 100644 yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/social/SysSocialService.java delete mode 100644 yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/social/impl/SysSocialServiceImpl.java create mode 100644 yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/dataobject/social/SysSocialUserDO.java create mode 100644 yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/mysql/social/SysSocialUserMapper.java create mode 100644 yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/redis/social/SysSocialAuthUserRedisDAO.java create mode 100644 yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/enums/social/SysSocialTypeEnum.java create mode 100644 yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/social/SysSocialService.java create mode 100644 yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/social/impl/SysSocialServiceImpl.java delete mode 100644 yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/enums/social/SysSocialTypeEnum.java diff --git a/yudao-admin-server/pom.xml b/yudao-admin-server/pom.xml index 120d3e9..bfccf80 100644 --- a/yudao-admin-server/pom.xml +++ b/yudao-admin-server/pom.xml @@ -122,12 +122,6 @@ screw-core - - - com.xkcoding.justauth - justauth-spring-boot-starter - 1.4.0 - diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/SysAuthController.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/SysAuthController.java index b87deac..16130e1 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/SysAuthController.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/SysAuthController.java @@ -8,10 +8,11 @@ import cn.iocoder.yudao.adminserver.modules.system.enums.permission.MenuTypeEnum import cn.iocoder.yudao.adminserver.modules.system.service.auth.SysAuthService; import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysPermissionService; import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysRoleService; -import cn.iocoder.yudao.adminserver.modules.system.service.social.SysSocialService; import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO; +import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialService; import cn.iocoder.yudao.coreservice.modules.system.service.user.SysUserCoreService; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.collection.SetUtils; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; @@ -132,7 +133,7 @@ public class SysAuthController { @DeleteMapping("/social-unbind") @ApiOperation("取消社交绑定") public CommonResult socialUnbind(@RequestBody SysAuthSocialUnbindReqVO reqVO) { - socialService.unbindSocialUser(getLoginUserId(), reqVO.getType(), reqVO.getUnionId()); + socialService.unbindSocialUser(getLoginUserId(), reqVO.getType(), reqVO.getUnionId(), UserTypeEnum.ADMIN); return CommonResult.success(true); } diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/vo/auth/SysAuthSocialBindReqVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/vo/auth/SysAuthSocialBindReqVO.java index 66d05ce..e92d3fc 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/vo/auth/SysAuthSocialBindReqVO.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/vo/auth/SysAuthSocialBindReqVO.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth; -import cn.iocoder.yudao.adminserver.modules.system.enums.social.SysSocialTypeEnum; +import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum; import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/vo/auth/SysAuthSocialLogin2ReqVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/vo/auth/SysAuthSocialLogin2ReqVO.java index bedf8ba..13aaa71 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/vo/auth/SysAuthSocialLogin2ReqVO.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/vo/auth/SysAuthSocialLogin2ReqVO.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth; -import cn.iocoder.yudao.adminserver.modules.system.enums.social.SysSocialTypeEnum; +import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum; import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/vo/auth/SysAuthSocialLoginReqVO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/vo/auth/SysAuthSocialLoginReqVO.java index b34022d..71d1968 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/vo/auth/SysAuthSocialLoginReqVO.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/vo/auth/SysAuthSocialLoginReqVO.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth; -import cn.iocoder.yudao.adminserver.modules.system.enums.social.SysSocialTypeEnum; +import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum; import cn.iocoder.yudao.framework.common.validation.InEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/dataobject/social/SysSocialUserDO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/dataobject/social/SysSocialUserDO.java deleted file mode 100644 index 7e998b8..0000000 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/dataobject/social/SysSocialUserDO.java +++ /dev/null @@ -1,82 +0,0 @@ -package cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.social; - -import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.*; - -/** - * 社交用户 - * 通过 {@link SysSocialUserDO#getUserId()} 关联到对应的 {@link SysUserDO} - * - * @author weir - */ -@TableName(value = "sys_social_user", autoResultMap = true) -@Data -@EqualsAndHashCode(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class SysSocialUserDO extends BaseDO { - - /** - * 自增主键 - */ - @TableId - private Long id; - /** - * 关联的用户编号 - */ - private Long userId; - /** - * 用户类型 - * - * 枚举 {@link UserTypeEnum} - */ - private Integer userType; - - /** - * 社交平台的类型 - * - * 枚举 {@link UserTypeEnum} - */ - private Integer type; - - /** - * 社交 openid - */ - private String openid; - /** - * 社交 token - */ - private String token; - /** - * 社交的全局编号 - * - * 例如说,微信平台的 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/union-id.html - * 如果没有 unionId 的平台,直接使用 openid 作为该字段的值 - */ - private String unionId; - /** - * 原始 Token 数据,一般是 JSON 格式 - */ - private String rawTokenInfo; - - /** - * 用户昵称 - */ - private String nickname; - /** - * 用户头像 - */ - private String avatar; - /** - * 原始用户数据,一般是 JSON 格式 - */ - private String rawUserInfo; - -} - - diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/mysql/social/SysSocialUserMapper.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/mysql/social/SysSocialUserMapper.java deleted file mode 100644 index b5762ec..0000000 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/mysql/social/SysSocialUserMapper.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.adminserver.modules.system.dal.mysql.social; - -import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.social.SysSocialUserDO; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import org.apache.ibatis.annotations.Mapper; - -import java.util.Collection; -import java.util.List; - -@Mapper -public interface SysSocialUserMapper extends BaseMapperX { - - default List selectListByTypeAndUnionId(Integer userType, Collection types, String unionId) { - return selectList(new QueryWrapper().eq("user_type", userType) - .in("type", types).eq("union_id", unionId)); - } - - default List selectListByTypeAndUserId(Integer userType, Collection types, Long userId) { - return selectList(new QueryWrapper().eq("user_type", userType) - .in("type", types).eq("user_id", userId)); - } - - default List selectListByUserId(Integer userType, Long userId) { - return selectList(new QueryWrapper().eq("user_type", userType).eq("user_id", userId)); - } - -} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/redis/SysRedisKeyConstants.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/redis/SysRedisKeyConstants.java index e7c484b..e9d3373 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/redis/SysRedisKeyConstants.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/redis/SysRedisKeyConstants.java @@ -19,12 +19,5 @@ public interface SysRedisKeyConstants { "captcha_code:%s", // 参数为 uuid STRING, String.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); - RedisKeyDefine SOCIAL_AUTH_USER = new RedisKeyDefine("社交的授权用户", - "social_auth_user:%d:%s", // 参数为 type,code - STRING, AuthUser.class, Duration.ofDays(1)); - - RedisKeyDefine SOCIAL_AUTH_STATE = new RedisKeyDefine("社交的 state", - "social_auth_state:%s", // 参数为 state - STRING, String.class, Duration.ofHours(24)); // 值为 state } diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/redis/social/SysSocialAuthUserRedisDAO.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/redis/social/SysSocialAuthUserRedisDAO.java deleted file mode 100644 index d5e84e6..0000000 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/redis/social/SysSocialAuthUserRedisDAO.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.yudao.adminserver.modules.system.dal.redis.social; - -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import me.zhyd.oauth.model.AuthCallback; -import me.zhyd.oauth.model.AuthUser; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.stereotype.Repository; - -import javax.annotation.Resource; - -import static cn.iocoder.yudao.adminserver.modules.system.dal.redis.SysRedisKeyConstants.SOCIAL_AUTH_USER; - -/** - * 社交 {@link me.zhyd.oauth.model.AuthUser} 的 RedisDAO - * - * @author 芋道源码 - */ -@Repository -public class SysSocialAuthUserRedisDAO { - - @Resource - private StringRedisTemplate stringRedisTemplate; - - public AuthUser get(Integer type, AuthCallback authCallback) { - String redisKey = formatKey(type, authCallback.getCode()); - return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), AuthUser.class); - } - - public void set(Integer type, AuthCallback authCallback, AuthUser authUser) { - String redisKey = formatKey(type, authCallback.getCode()); - stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(authUser), SOCIAL_AUTH_USER.getTimeout()); - } - - private static String formatKey(Integer type, String code) { - return String.format(SOCIAL_AUTH_USER.getKeyTemplate(), type, code); - } - -} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/SysErrorCodeConstants.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/SysErrorCodeConstants.java index 8a606e0..3b4051d 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/SysErrorCodeConstants.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/SysErrorCodeConstants.java @@ -90,8 +90,6 @@ public interface SysErrorCodeConstants { ErrorCode ERROR_CODE_NOT_EXISTS = new ErrorCode(1002013000, "错误码不存在"); ErrorCode ERROR_CODE_DUPLICATE = new ErrorCode(1002013001, "已经存在编码为【{}】的错误码"); - // ========== 社交模块 1002014000 ========== - ErrorCode SOCIAL_AUTH_FAILURE = new ErrorCode(1002014000, "社交授权失败,原因是:{}"); - ErrorCode SOCIAL_UNBIND_NOT_SELF = new ErrorCode(1002014001, "社交解绑失败,非当前用户绑定"); + } diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/social/SysSocialTypeEnum.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/social/SysSocialTypeEnum.java deleted file mode 100644 index 87d762d..0000000 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/social/SysSocialTypeEnum.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.adminserver.modules.system.enums.social; - -import cn.hutool.core.collection.ListUtil; -import cn.hutool.core.util.ArrayUtil; -import cn.iocoder.yudao.framework.common.core.IntArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; -import java.util.List; - -/** - * 社交平台的类型枚举 - * - * @author 芋道源码 - */ -@Getter -@AllArgsConstructor -public enum SysSocialTypeEnum implements IntArrayValuable { - - GITEE(10, "GITEE"), // https://gitee.com/api/v5/oauth_doc#/ - DINGTALK(20, "DINGTALK"), // https://developers.dingtalk.com/document/app/obtain-identity-credentials - WECHAT_ENTERPRISE(30, "WECHAT_ENTERPRISE"), // https://xkcoding.com/2019/08/06/use-justauth-integration-wechat-enterprise.html - ; - - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SysSocialTypeEnum::getType).toArray(); - - public static final List WECHAT_ALL = ListUtil.toList(WECHAT_ENTERPRISE.type); - - /** - * 类型 - */ - private final Integer type; - /** - * 类型的标识 - */ - private final String source; - - @Override - public int[] array() { - return ARRAYS; - } - - public static SysSocialTypeEnum valueOfType(Integer type) { - return ArrayUtil.firstMatch(o -> o.getType().equals(type), values()); - } - - public static List getRelationTypes(Integer type) { - if (WECHAT_ALL.contains(type)) { - return WECHAT_ALL; - } - return ListUtil.toList(type); - } - -} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java index d5fb91f..0bf08e8 100644 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java +++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java @@ -6,18 +6,18 @@ import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAu import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthSocialLogin2ReqVO; import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthSocialLoginReqVO; import cn.iocoder.yudao.adminserver.modules.system.convert.auth.SysAuthConvert; -import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.social.SysSocialUserDO; import cn.iocoder.yudao.adminserver.modules.system.enums.logger.SysLoginLogTypeEnum; import cn.iocoder.yudao.adminserver.modules.system.enums.logger.SysLoginResultEnum; import cn.iocoder.yudao.adminserver.modules.system.service.auth.SysAuthService; import cn.iocoder.yudao.adminserver.modules.system.service.common.SysCaptchaService; import cn.iocoder.yudao.adminserver.modules.system.service.permission.SysPermissionService; -import cn.iocoder.yudao.adminserver.modules.system.service.social.SysSocialService; import cn.iocoder.yudao.adminserver.modules.system.service.user.SysUserService; +import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social.SysSocialUserDO; import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO; import cn.iocoder.yudao.coreservice.modules.system.service.auth.SysUserSessionCoreService; import cn.iocoder.yudao.coreservice.modules.system.service.logger.SysLoginLogCoreService; import cn.iocoder.yudao.coreservice.modules.system.service.logger.dto.SysLoginLogCreateReqDTO; +import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialService; import cn.iocoder.yudao.coreservice.modules.system.service.user.SysUserCoreService; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; @@ -75,6 +75,8 @@ public class SysAuthServiceImpl implements SysAuthService { @Resource private SysSocialService socialService; + private static final UserTypeEnum userTypeEnum = UserTypeEnum.ADMIN; + @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 获取 username 对应的 SysUserDO @@ -198,7 +200,7 @@ public class SysAuthServiceImpl implements SysAuthService { // 如果未绑定 SysSocialUserDO 用户,则无法自动登录,进行报错 String unionId = socialService.getAuthUserUnionId(authUser); - List socialUsers = socialService.getAllSocialUserList(reqVO.getType(), unionId); + List socialUsers = socialService.getAllSocialUserList(reqVO.getType(), unionId, userTypeEnum); if (CollUtil.isEmpty(socialUsers)) { throw exception(AUTH_THIRD_LOGIN_NOT_BIND); } @@ -216,7 +218,7 @@ public class SysAuthServiceImpl implements SysAuthService { loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId())); // 获取用户角色列表 // 绑定社交用户(更新) - socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser); + socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, userTypeEnum); // 缓存登录用户到 Redis 中,返回 sessionId 编号 return userSessionCoreService.createUserSession(loginUser, userIp, userAgent); @@ -233,7 +235,7 @@ public class SysAuthServiceImpl implements SysAuthService { loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId())); // 获取用户角色列表 // 绑定社交用户(新增) - socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser); + socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, userTypeEnum); // 缓存登录用户到 Redis 中,返回 sessionId 编号 return userSessionCoreService.createUserSession(loginUser, userIp, userAgent); @@ -246,7 +248,7 @@ public class SysAuthServiceImpl implements SysAuthService { Assert.notNull(authUser, "授权用户不为空"); // 绑定社交用户(新增) - socialService.bindSocialUser(userId, reqVO.getType(), authUser); + socialService.bindSocialUser(userId, reqVO.getType(), authUser, userTypeEnum); } @Override @@ -267,7 +269,7 @@ public class SysAuthServiceImpl implements SysAuthService { reqDTO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType()); reqDTO.setTraceId(TracerUtils.getTraceId()); reqDTO.setUserId(userId); - reqDTO.setUserType(UserTypeEnum.ADMIN.getValue()); + reqDTO.setUserType(userTypeEnum.getValue()); reqDTO.setUsername(username); reqDTO.setUserAgent(ServletUtils.getUserAgent()); reqDTO.setUserIp(ServletUtils.getClientIP()); diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/social/SysSocialService.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/social/SysSocialService.java deleted file mode 100644 index 44dfad6..0000000 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/social/SysSocialService.java +++ /dev/null @@ -1,80 +0,0 @@ -package cn.iocoder.yudao.adminserver.modules.system.service.social; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.social.SysSocialUserDO; -import cn.iocoder.yudao.adminserver.modules.system.enums.social.SysSocialTypeEnum; -import cn.iocoder.yudao.framework.common.exception.ServiceException; -import me.zhyd.oauth.model.AuthUser; - -import javax.validation.constraints.NotNull; -import java.util.List; - -/** - * 社交 Service 接口,例如说社交平台的授权登录 - * - * @author 芋道源码 - */ -public interface SysSocialService { - - /** - * 获得社交平台的授权 URL - * - * @param type 社交平台的类型 {@link SysSocialTypeEnum} - * @param redirectUri 重定向 URL - * @return 社交平台的授权 URL - */ - String getAuthorizeUrl(Integer type, String redirectUri); - - /** - * 获得授权的用户 - * 如果授权失败,则会抛出 {@link ServiceException} 异常 - * - * @param type 社交平台的类型 {@link SysSocialTypeEnum} - * @param code 授权码 - * @param state state - * @return 授权用户 - */ - @NotNull - AuthUser getAuthUser(Integer type, String code, String state); - - default String getAuthUserUnionId(AuthUser authUser) { - return StrUtil.blankToDefault(authUser.getToken().getUnionId(), authUser.getUuid()); - } - - /** - * 获得 unionId 对应的某个社交平台的“所有”社交用户 - * 注意,这里的“所有”,指的是类似【微信】平台,包括了小程序、公众号、PC 网站,他们的 unionId 是一致的 - * - * @param type 社交平台的类型 {@link SysSocialTypeEnum} - * @param unionId 社交平台的 unionId - * @return 社交用户列表 - */ - List getAllSocialUserList(Integer type, String unionId); - - /** - * 获得指定用户的社交用户列表 - * - * @param userId 用户编号 - * @return 社交用户列表 - */ - List getSocialUserList(Long userId); - - /** - * 绑定社交用户 - * - * @param userId 用户编号 - * @param type 社交平台的类型 {@link SysSocialTypeEnum} - * @param authUser 授权用户 - */ - void bindSocialUser(Long userId, Integer type, AuthUser authUser); - - /** - * 取消绑定社交用户 - * - * @param userId 用户编号 - * @param type 社交平台的类型 {@link SysSocialTypeEnum} - * @param unionId 社交平台的 unionId - */ - void unbindSocialUser(Long userId, Integer type, String unionId); - -} diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/social/impl/SysSocialServiceImpl.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/social/impl/SysSocialServiceImpl.java deleted file mode 100644 index 291015a..0000000 --- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/social/impl/SysSocialServiceImpl.java +++ /dev/null @@ -1,178 +0,0 @@ -package cn.iocoder.yudao.adminserver.modules.system.service.social.impl; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.social.SysSocialUserDO; -import cn.iocoder.yudao.adminserver.modules.system.dal.mysql.social.SysSocialUserMapper; -import cn.iocoder.yudao.adminserver.modules.system.dal.redis.social.SysSocialAuthUserRedisDAO; -import cn.iocoder.yudao.adminserver.modules.system.enums.social.SysSocialTypeEnum; -import cn.iocoder.yudao.adminserver.modules.system.service.social.SysSocialService; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.common.util.http.HttpUtils; -import com.google.common.annotations.VisibleForTesting; -import com.xkcoding.justauth.AuthRequestFactory; -import lombok.extern.slf4j.Slf4j; -import me.zhyd.oauth.model.AuthCallback; -import me.zhyd.oauth.model.AuthResponse; -import me.zhyd.oauth.model.AuthUser; -import me.zhyd.oauth.request.AuthRequest; -import me.zhyd.oauth.utils.AuthStateUtils; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import javax.annotation.Resource; -import javax.validation.Valid; -import java.util.List; -import java.util.Objects; - -import static cn.iocoder.yudao.adminserver.modules.system.enums.SysErrorCodeConstants.SOCIAL_AUTH_FAILURE; -import static cn.iocoder.yudao.adminserver.modules.system.enums.SysErrorCodeConstants.SOCIAL_UNBIND_NOT_SELF; -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; - -/** - * 社交 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Valid -@Slf4j -public class SysSocialServiceImpl implements SysSocialService { - - @Resource - private AuthRequestFactory authRequestFactory; - - @Resource - private SysSocialAuthUserRedisDAO authSocialUserRedisDAO; - - @Resource - private SysSocialUserMapper socialUserMapper; - - @Override - public String getAuthorizeUrl(Integer type, String redirectUri) { - // 获得对应的 AuthRequest 实现 - AuthRequest authRequest = authRequestFactory.get(SysSocialTypeEnum.valueOfType(type).getSource()); - // 生成跳转地址 - String authorizeUri = authRequest.authorize(AuthStateUtils.createState()); - return HttpUtils.replaceUrlQuery(authorizeUri, "redirect_uri", redirectUri); - } - - @Override - public AuthUser getAuthUser(Integer type, String code, String state) { - AuthCallback authCallback = buildAuthCallback(code, state); - // 从缓存中获取 - AuthUser authUser = authSocialUserRedisDAO.get(type, authCallback); - if (authUser != null) { - return authUser; - } - - // 请求获取 - authUser = this.getAuthUser0(type, authCallback); - // 缓存。原因是 code 有且可以使用一次。在社交登录时,当未绑定 User 时,需要绑定登录,此时需要 code 使用两次 - authSocialUserRedisDAO.set(type, authCallback, authUser); - return authUser; - } - - @Override - public List getAllSocialUserList(Integer type, String unionId) { - List types = SysSocialTypeEnum.getRelationTypes(type); - return socialUserMapper.selectListByTypeAndUnionId(UserTypeEnum.ADMIN.getValue(), types, unionId); - } - - @Override - public List getSocialUserList(Long userId) { - return socialUserMapper.selectListByUserId(UserTypeEnum.ADMIN.getValue(), userId); - } - - @Override - @Transactional - public void bindSocialUser(Long userId, Integer type, AuthUser authUser) { - // 获得 unionId 对应的 SysSocialUserDO 列表 - String unionId = getAuthUserUnionId(authUser); - List socialUsers = this.getAllSocialUserList(type, unionId); - - // 逻辑一:如果 userId 之前绑定过该 type 的其它账号,需要进行解绑 - this.unbindOldSocialUser(userId, type, unionId); - - // 逻辑二:如果 socialUsers 指定的 userId 改变,需要进行更新 - // 例如说,一个微信 unionId 对应了多个社交账号,结果其中有个关联了新的 userId,则其它也要跟着修改 - // 考虑到 socialUsers 一般比较少,直接 for 循环更新即可 - socialUsers.forEach(socialUser -> { - if (Objects.equals(socialUser.getUserId(), userId)) { - return; - } - socialUserMapper.updateById(new SysSocialUserDO().setId(socialUser.getId()).setUserId(userId)); - }); - - // 逻辑三:如果 authUser 不存在于 socialUsers 中,则进行新增;否则,进行更新 - SysSocialUserDO socialUser = CollUtil.findOneByField(socialUsers, "openid", authUser.getUuid()); - SysSocialUserDO saveSocialUser = SysSocialUserDO.builder() // 新增和更新的通用属性 - .token(authUser.getToken().getAccessToken()).rawTokenInfo(toJsonString(authUser.getToken())) - .nickname(authUser.getNickname()).avatar(authUser.getAvatar()).rawUserInfo(toJsonString(authUser.getRawUserInfo())) - .build(); - if (socialUser == null) { - saveSocialUser.setUserId(userId).setUserType(UserTypeEnum.ADMIN.getValue()) - .setType(type).setOpenid(authUser.getUuid()).setUnionId(unionId); - socialUserMapper.insert(saveSocialUser); - } else { - saveSocialUser.setId(socialUser.getId()); - socialUserMapper.updateById(saveSocialUser); - } - } - - @Override - public void unbindSocialUser(Long userId, Integer type, String unionId) { - // 获得 unionId 对应的所有 SysSocialUserDO 社交用户 - List socialUsers = this.getAllSocialUserList(type, unionId); - if (CollUtil.isEmpty(socialUsers)) { - return; - } - // 校验,是否解绑的是非自己的 - socialUsers.forEach(socialUser -> { - if (!Objects.equals(socialUser.getUserId(), userId)) { - throw exception(SOCIAL_UNBIND_NOT_SELF); - } - }); - - // 解绑 - socialUserMapper.deleteBatchIds(CollectionUtils.convertSet(socialUsers, SysSocialUserDO::getId)); - } - - @VisibleForTesting - public void unbindOldSocialUser(Long userId, Integer type, String newUnionId) { - List types = SysSocialTypeEnum.getRelationTypes(type); - List oldSocialUsers = socialUserMapper.selectListByTypeAndUserId( - UserTypeEnum.ADMIN.getValue(), types, userId); - // 如果新老的 unionId 是一致的,说明无需解绑 - if (CollUtil.isEmpty(oldSocialUsers) || Objects.equals(newUnionId, oldSocialUsers.get(0).getUnionId())) { - return; - } - - // 解绑 - socialUserMapper.deleteBatchIds(CollectionUtils.convertSet(oldSocialUsers, SysSocialUserDO::getId)); - } - - /** - * 请求社交平台,获得授权的用户 - * - * @param type 社交平台的类型 - * @param authCallback 授权回调 - * @return 授权的用户 - */ - private AuthUser getAuthUser0(Integer type, AuthCallback authCallback) { - AuthRequest authRequest = authRequestFactory.get(SysSocialTypeEnum.valueOfType(type).getSource()); - AuthResponse authResponse = authRequest.login(authCallback); - log.info("[getAuthUser0][请求社交平台 type({}) request({}) response({})]", type, toJsonString(authCallback), - toJsonString(authResponse)); - if (!authResponse.ok()) { - throw exception(SOCIAL_AUTH_FAILURE, authResponse.getMsg()); - } - return (AuthUser) authResponse.getData(); - } - - private static AuthCallback buildAuthCallback(String code, String state) { - return AuthCallback.builder().code(code).state(state).build(); - } - -} diff --git a/yudao-core-service/pom.xml b/yudao-core-service/pom.xml index 43cdae1..42a7d96 100644 --- a/yudao-core-service/pom.xml +++ b/yudao-core-service/pom.xml @@ -85,6 +85,12 @@ com.google.guava guava + + + com.xkcoding.justauth + justauth-spring-boot-starter + 1.4.0 + diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/dataobject/social/SysSocialUserDO.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/dataobject/social/SysSocialUserDO.java new file mode 100644 index 0000000..d1772a1 --- /dev/null +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/dataobject/social/SysSocialUserDO.java @@ -0,0 +1,82 @@ +package cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social; + +import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 社交用户 + * 通过 {@link SysSocialUserDO#getUserId()} 关联到对应的 {@link SysUserDO} + * + * @author weir + */ +@TableName(value = "sys_social_user", autoResultMap = true) +@Data +@EqualsAndHashCode(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SysSocialUserDO extends BaseDO { + + /** + * 自增主键 + */ + @TableId + private Long id; + /** + * 关联的用户编号 + */ + private Long userId; + /** + * 用户类型 + * + * 枚举 {@link UserTypeEnum} + */ + private Integer userType; + + /** + * 社交平台的类型 + * + * 枚举 {@link UserTypeEnum} + */ + private Integer type; + + /** + * 社交 openid + */ + private String openid; + /** + * 社交 token + */ + private String token; + /** + * 社交的全局编号 + * + * 例如说,微信平台的 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/union-id.html + * 如果没有 unionId 的平台,直接使用 openid 作为该字段的值 + */ + private String unionId; + /** + * 原始 Token 数据,一般是 JSON 格式 + */ + private String rawTokenInfo; + + /** + * 用户昵称 + */ + private String nickname; + /** + * 用户头像 + */ + private String avatar; + /** + * 原始用户数据,一般是 JSON 格式 + */ + private String rawUserInfo; + +} + + diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/mysql/social/SysSocialUserMapper.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/mysql/social/SysSocialUserMapper.java new file mode 100644 index 0000000..43fc4a7 --- /dev/null +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/mysql/social/SysSocialUserMapper.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.coreservice.modules.system.dal.mysql.social; + +import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social.SysSocialUserDO; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +@Mapper +public interface SysSocialUserMapper extends BaseMapperX { + + default List selectListByTypeAndUnionId(Integer userType, Collection types, String unionId) { + return selectList(new QueryWrapper().eq("user_type", userType) + .in("type", types).eq("union_id", unionId)); + } + + default List selectListByTypeAndUserId(Integer userType, Collection types, Long userId) { + return selectList(new QueryWrapper().eq("user_type", userType) + .in("type", types).eq("user_id", userId)); + } + + default List selectListByUserId(Integer userType, Long userId) { + return selectList(new QueryWrapper().eq("user_type", userType).eq("user_id", userId)); + } + +} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/redis/SysRedisKeyCoreConstants.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/redis/SysRedisKeyCoreConstants.java index fb41188..8076980 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/redis/SysRedisKeyCoreConstants.java +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/redis/SysRedisKeyCoreConstants.java @@ -2,6 +2,9 @@ package cn.iocoder.yudao.coreservice.modules.system.dal.redis; import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; import cn.iocoder.yudao.framework.security.core.LoginUser; +import me.zhyd.oauth.model.AuthUser; + +import java.time.Duration; import static cn.iocoder.yudao.framework.redis.core.RedisKeyDefine.KeyTypeEnum.STRING; @@ -16,4 +19,11 @@ public interface SysRedisKeyCoreConstants { "login_user:%s", // 参数为 sessionId STRING, LoginUser.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); + RedisKeyDefine SOCIAL_AUTH_USER = new RedisKeyDefine("社交的授权用户", + "social_auth_user:%d:%s", // 参数为 type,code + STRING, AuthUser.class, Duration.ofDays(1)); + + RedisKeyDefine SOCIAL_AUTH_STATE = new RedisKeyDefine("社交的 state", + "social_auth_state:%s", // 参数为 state + STRING, String.class, Duration.ofHours(24)); // 值为 state } diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/redis/social/SysSocialAuthUserRedisDAO.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/redis/social/SysSocialAuthUserRedisDAO.java new file mode 100644 index 0000000..e82c813 --- /dev/null +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/dal/redis/social/SysSocialAuthUserRedisDAO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.coreservice.modules.system.dal.redis.social; + +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import me.zhyd.oauth.model.AuthCallback; +import me.zhyd.oauth.model.AuthUser; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Repository; + +import javax.annotation.Resource; + +import static cn.iocoder.yudao.coreservice.modules.system.dal.redis.SysRedisKeyCoreConstants.SOCIAL_AUTH_USER; + + +/** + * 社交 {@link AuthUser} 的 RedisDAO + * + * @author 芋道源码 + */ +@Repository +public class SysSocialAuthUserRedisDAO { + + @Resource + private StringRedisTemplate stringRedisTemplate; + + public AuthUser get(Integer type, AuthCallback authCallback) { + String redisKey = formatKey(type, authCallback.getCode()); + return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), AuthUser.class); + } + + public void set(Integer type, AuthCallback authCallback, AuthUser authUser) { + String redisKey = formatKey(type, authCallback.getCode()); + stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(authUser), SOCIAL_AUTH_USER.getTimeout()); + } + + private static String formatKey(Integer type, String code) { + return String.format(SOCIAL_AUTH_USER.getKeyTemplate(), type, code); + } + +} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/enums/SysErrorCodeConstants.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/enums/SysErrorCodeConstants.java index 3c1fc87..a7231f4 100644 --- a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/enums/SysErrorCodeConstants.java +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/enums/SysErrorCodeConstants.java @@ -14,4 +14,9 @@ public interface SysErrorCodeConstants { ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1006000001, "模板参数({})缺失"); ErrorCode SMS_SEND_TEMPLATE_NOT_EXISTS = new ErrorCode(1006000000, "短信模板不存在"); + + // ========== 社交模块 1006001000 ========== + ErrorCode SOCIAL_AUTH_FAILURE = new ErrorCode(1006001000, "社交授权失败,原因是:{}"); + ErrorCode SOCIAL_UNBIND_NOT_SELF = new ErrorCode(1006001001, "社交解绑失败,非当前用户绑定"); + } diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/enums/social/SysSocialTypeEnum.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/enums/social/SysSocialTypeEnum.java new file mode 100644 index 0000000..592c274 --- /dev/null +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/enums/social/SysSocialTypeEnum.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.coreservice.modules.system.enums.social; + +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.List; + +/** + * 社交平台的类型枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum SysSocialTypeEnum implements IntArrayValuable { + + GITEE(10, "GITEE"), // https://gitee.com/api/v5/oauth_doc#/ + DINGTALK(20, "DINGTALK"), // https://developers.dingtalk.com/document/app/obtain-identity-credentials + WECHAT_ENTERPRISE(30, "WECHAT_ENTERPRISE"), // https://xkcoding.com/2019/08/06/use-justauth-integration-wechat-enterprise.html + /** + * 微信公众平台 - H5 + */ + WECHAT_MP(12, "WECHAT_MP"), // https://www.cnblogs.com/juewuzhe/p/11905461.html + /** + * 微信开放平台 - 小程序 + */ + WECHAT_OPEN(11, "WECHAT_OPEN"), // https://justauth.wiki/guide/oauth/wechat_open/#_2-%E7%94%B3%E8%AF%B7%E5%BC%80%E5%8F%91%E8%80%85%E8%B5%84%E8%B4%A8%E8%AE%A4%E8%AF%81 + ; + + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SysSocialTypeEnum::getType).toArray(); + + public static final List WECHAT_ALL = ListUtil.toList(WECHAT_ENTERPRISE.type, WECHAT_MP.type, WECHAT_OPEN.type); + + /** + * 类型 + */ + private final Integer type; + /** + * 类型的标识 + */ + private final String source; + + @Override + public int[] array() { + return ARRAYS; + } + + public static SysSocialTypeEnum valueOfType(Integer type) { + return ArrayUtil.firstMatch(o -> o.getType().equals(type), values()); + } + + public static List getRelationTypes(Integer type) { + if (WECHAT_ALL.contains(type)) { + return WECHAT_ALL; + } + return ListUtil.toList(type); + } + +} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/social/SysSocialService.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/social/SysSocialService.java new file mode 100644 index 0000000..81ee31e --- /dev/null +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/social/SysSocialService.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.coreservice.modules.system.service.social; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social.SysSocialUserDO; +import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import me.zhyd.oauth.model.AuthUser; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * 社交 Service 接口,例如说社交平台的授权登录 + * + * @author 芋道源码 + */ +public interface SysSocialService { + + /** + * 获得社交平台的授权 URL + * + * @param type 社交平台的类型 {@link SysSocialTypeEnum} + * @param redirectUri 重定向 URL + * @return 社交平台的授权 URL + */ + String getAuthorizeUrl(Integer type, String redirectUri); + + /** + * 获得授权的用户 + * 如果授权失败,则会抛出 {@link ServiceException} 异常 + * + * @param type 社交平台的类型 {@link SysSocialTypeEnum} + * @param code 授权码 + * @param state state + * @return 授权用户 + */ + @NotNull + AuthUser getAuthUser(Integer type, String code, String state); + + default String getAuthUserUnionId(AuthUser authUser) { + return StrUtil.blankToDefault(authUser.getToken().getUnionId(), authUser.getUuid()); + } + + /** + * 获得 unionId 对应的某个社交平台的“所有”社交用户 + * 注意,这里的“所有”,指的是类似【微信】平台,包括了小程序、公众号、PC 网站,他们的 unionId 是一致的 + * + * @param type 社交平台的类型 {@link SysSocialTypeEnum} + * @param unionId 社交平台的 unionId + * @return 社交用户列表 + */ + List getAllSocialUserList(Integer type, String unionId, UserTypeEnum userTypeEnum); + + /** + * 获得指定用户的社交用户列表 + * + * @param userId 用户编号 + * @return 社交用户列表 + */ + List getSocialUserList(Long userId,UserTypeEnum userTypeEnum); + + /** + * 绑定社交用户 + * + * @param userId 用户编号 + * @param type 社交平台的类型 {@link SysSocialTypeEnum} + * @param authUser 授权用户 + */ + void bindSocialUser(Long userId, Integer type, AuthUser authUser, UserTypeEnum userTypeEnum); + + /** + * 取消绑定社交用户 + * + * @param userId 用户编号 + * @param type 社交平台的类型 {@link SysSocialTypeEnum} + * @param unionId 社交平台的 unionId + */ + void unbindSocialUser(Long userId, Integer type, String unionId,UserTypeEnum userTypeEnum); + +} diff --git a/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/social/impl/SysSocialServiceImpl.java b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/social/impl/SysSocialServiceImpl.java new file mode 100644 index 0000000..2bc8a73 --- /dev/null +++ b/yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/system/service/social/impl/SysSocialServiceImpl.java @@ -0,0 +1,178 @@ +package cn.iocoder.yudao.coreservice.modules.system.service.social.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social.SysSocialUserDO; +import cn.iocoder.yudao.coreservice.modules.system.dal.mysql.social.SysSocialUserMapper; +import cn.iocoder.yudao.coreservice.modules.system.dal.redis.social.SysSocialAuthUserRedisDAO; +import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum; +import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialService; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; +import com.google.common.annotations.VisibleForTesting; +import com.xkcoding.justauth.AuthRequestFactory; +import lombok.extern.slf4j.Slf4j; +import me.zhyd.oauth.model.AuthCallback; +import me.zhyd.oauth.model.AuthResponse; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.request.AuthRequest; +import me.zhyd.oauth.utils.AuthStateUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; +import java.util.Objects; + +import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.SOCIAL_AUTH_FAILURE; +import static cn.iocoder.yudao.coreservice.modules.system.enums.SysErrorCodeConstants.SOCIAL_UNBIND_NOT_SELF; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; + +/** + * 社交 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Valid +@Slf4j +public class SysSocialServiceImpl implements SysSocialService { + + @Resource + private AuthRequestFactory authRequestFactory; + + @Resource + private SysSocialAuthUserRedisDAO authSocialUserRedisDAO; + + @Resource + private SysSocialUserMapper socialUserMapper; + + @Override + public String getAuthorizeUrl(Integer type, String redirectUri) { + // 获得对应的 AuthRequest 实现 + AuthRequest authRequest = authRequestFactory.get(SysSocialTypeEnum.valueOfType(type).getSource()); + // 生成跳转地址 + String authorizeUri = authRequest.authorize(AuthStateUtils.createState()); + return HttpUtils.replaceUrlQuery(authorizeUri, "redirect_uri", redirectUri); + } + + @Override + public AuthUser getAuthUser(Integer type, String code, String state) { + AuthCallback authCallback = buildAuthCallback(code, state); + // 从缓存中获取 + AuthUser authUser = authSocialUserRedisDAO.get(type, authCallback); + if (authUser != null) { + return authUser; + } + + // 请求获取 + authUser = this.getAuthUser0(type, authCallback); + // 缓存。原因是 code 有且可以使用一次。在社交登录时,当未绑定 User 时,需要绑定登录,此时需要 code 使用两次 + authSocialUserRedisDAO.set(type, authCallback, authUser); + return authUser; + } + + @Override + public List getAllSocialUserList(Integer type, String unionId,UserTypeEnum userTypeEnum) { + List types = SysSocialTypeEnum.getRelationTypes(type); + return socialUserMapper.selectListByTypeAndUnionId(userTypeEnum.getValue(), types, unionId); + } + + @Override + public List getSocialUserList(Long userId,UserTypeEnum userTypeEnum) { + return socialUserMapper.selectListByUserId(userTypeEnum.getValue(), userId); + } + + @Override + @Transactional + public void bindSocialUser(Long userId, Integer type, AuthUser authUser,UserTypeEnum userTypeEnum) { + // 获得 unionId 对应的 SysSocialUserDO 列表 + String unionId = getAuthUserUnionId(authUser); + List socialUsers = this.getAllSocialUserList(type, unionId, userTypeEnum); + + // 逻辑一:如果 userId 之前绑定过该 type 的其它账号,需要进行解绑 + this.unbindOldSocialUser(userId, type, unionId, userTypeEnum); + + // 逻辑二:如果 socialUsers 指定的 userId 改变,需要进行更新 + // 例如说,一个微信 unionId 对应了多个社交账号,结果其中有个关联了新的 userId,则其它也要跟着修改 + // 考虑到 socialUsers 一般比较少,直接 for 循环更新即可 + socialUsers.forEach(socialUser -> { + if (Objects.equals(socialUser.getUserId(), userId)) { + return; + } + socialUserMapper.updateById(new SysSocialUserDO().setId(socialUser.getId()).setUserId(userId)); + }); + + // 逻辑三:如果 authUser 不存在于 socialUsers 中,则进行新增;否则,进行更新 + SysSocialUserDO socialUser = CollUtil.findOneByField(socialUsers, "openid", authUser.getUuid()); + SysSocialUserDO saveSocialUser = SysSocialUserDO.builder() // 新增和更新的通用属性 + .token(authUser.getToken().getAccessToken()).rawTokenInfo(toJsonString(authUser.getToken())) + .nickname(authUser.getNickname()).avatar(authUser.getAvatar()).rawUserInfo(toJsonString(authUser.getRawUserInfo())) + .build(); + if (socialUser == null) { + saveSocialUser.setUserId(userId).setUserType(userTypeEnum.getValue()) + .setType(type).setOpenid(authUser.getUuid()).setUnionId(unionId); + socialUserMapper.insert(saveSocialUser); + } else { + saveSocialUser.setId(socialUser.getId()); + socialUserMapper.updateById(saveSocialUser); + } + } + + @Override + public void unbindSocialUser(Long userId, Integer type, String unionId, UserTypeEnum userTypeEnum) { + // 获得 unionId 对应的所有 SysSocialUserDO 社交用户 + List socialUsers = this.getAllSocialUserList(type, unionId, userTypeEnum); + if (CollUtil.isEmpty(socialUsers)) { + return; + } + // 校验,是否解绑的是非自己的 + socialUsers.forEach(socialUser -> { + if (!Objects.equals(socialUser.getUserId(), userId)) { + throw exception(SOCIAL_UNBIND_NOT_SELF); + } + }); + + // 解绑 + socialUserMapper.deleteBatchIds(CollectionUtils.convertSet(socialUsers, SysSocialUserDO::getId)); + } + + @VisibleForTesting + public void unbindOldSocialUser(Long userId, Integer type, String newUnionId, UserTypeEnum userTypeEnum) { + List types = SysSocialTypeEnum.getRelationTypes(type); + List oldSocialUsers = socialUserMapper.selectListByTypeAndUserId( + userTypeEnum.getValue(), types, userId); + // 如果新老的 unionId 是一致的,说明无需解绑 + if (CollUtil.isEmpty(oldSocialUsers) || Objects.equals(newUnionId, oldSocialUsers.get(0).getUnionId())) { + return; + } + + // 解绑 + socialUserMapper.deleteBatchIds(CollectionUtils.convertSet(oldSocialUsers, SysSocialUserDO::getId)); + } + + /** + * 请求社交平台,获得授权的用户 + * + * @param type 社交平台的类型 + * @param authCallback 授权回调 + * @return 授权的用户 + */ + private AuthUser getAuthUser0(Integer type, AuthCallback authCallback) { + AuthRequest authRequest = authRequestFactory.get(SysSocialTypeEnum.valueOfType(type).getSource()); + AuthResponse authResponse = authRequest.login(authCallback); + log.info("[getAuthUser0][请求社交平台 type({}) request({}) response({})]", type, toJsonString(authCallback), + toJsonString(authResponse)); + if (!authResponse.ok()) { + throw exception(SOCIAL_AUTH_FAILURE, authResponse.getMsg()); + } + return (AuthUser) authResponse.getData(); + } + + private static AuthCallback buildAuthCallback(String code, String state) { + return AuthCallback.builder().code(code).state(state).build(); + } + +} diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/enums/social/SysSocialTypeEnum.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/enums/social/SysSocialTypeEnum.java deleted file mode 100644 index fe93f39..0000000 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/enums/social/SysSocialTypeEnum.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.userserver.modules.member.enums.social; - -import cn.hutool.core.collection.ListUtil; -import cn.hutool.core.util.ArrayUtil; -import cn.iocoder.yudao.framework.common.core.IntArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; -import java.util.List; - -/** - * 社交平台的类型枚举 - * - * @author 芋道源码 - */ -@Getter -@AllArgsConstructor -public enum SysSocialTypeEnum implements IntArrayValuable { - - GITEE(10, "GITEE"), // https://gitee.com/api/v5/oauth_doc#/ - DINGTALK(20, "DINGTALK"), // https://developers.dingtalk.com/document/app/obtain-identity-credentials - WECHAT_ENTERPRISE(30, "WECHAT_ENTERPRISE"), // https://xkcoding.com/2019/08/06/use-justauth-integration-wechat-enterprise.html - ; - - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SysSocialTypeEnum::getType).toArray(); - - public static final List WECHAT_ALL = ListUtil.toList(WECHAT_ENTERPRISE.type); - - /** - * 类型 - */ - private final Integer type; - /** - * 类型的标识 - */ - private final String source; - - @Override - public int[] array() { - return ARRAYS; - } - - public static SysSocialTypeEnum valueOfType(Integer type) { - return ArrayUtil.firstMatch(o -> o.getType().equals(type), values()); - } - - public static List getRelationTypes(Integer type) { - if (WECHAT_ALL.contains(type)) { - return WECHAT_ALL; - } - return ListUtil.toList(type); - } - -} diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.java index 2b1fd61..377dd99 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.java +++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.java @@ -1,9 +1,13 @@ package cn.iocoder.yudao.userserver.modules.system.controller.auth; +import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialService; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.*; import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService; import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; +import com.alibaba.fastjson.JSON; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; @@ -13,11 +17,13 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getUserAgent; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @Api(tags = "认证") @RestController @@ -30,6 +36,9 @@ public class SysAuthController { private SysAuthService authService; @Resource private SysSmsCodeService smsCodeService; + @Resource + private SysSocialService socialService; + @PostMapping("/login") @ApiOperation("使用手机 + 密码登录") @@ -70,42 +79,47 @@ public class SysAuthController { }) public CommonResult socialAuthRedirect(@RequestParam("type") Integer type, @RequestParam("redirectUri") String redirectUri) { -// return CommonResult.success(socialService.getAuthorizeUrl(type, redirectUri)); - return null; + return CommonResult.success(socialService.getAuthorizeUrl(type, redirectUri)); + } + + @GetMapping("/social-login-get") + @ApiOperation("社交登录,使用 code 授权码") + @ResponseBody + @Deprecated + public CommonResult socialLoginGet(HttpServletRequest request,String code,String state) { + // 返回结果 + MbrAuthSocialLoginReqVO reqVO = MbrAuthSocialLoginReqVO.builder().state(state).code(code).build(); + reqVO.setType(12); + System.out.println(JSON.toJSON(reqVO)); + return success(reqVO); } @PostMapping("/social-login") @ApiOperation("社交登录,使用 code 授权码") - public CommonResult socialLogin(@RequestBody @Valid MbrAuthSocialLoginReqVO reqVO) { -// String token = authService.socialLogin(reqVO, getClientIP(), getUserAgent()); -// // 返回结果 -// return success(MbrAuthLoginRespVO.builder().token(token).build()); - return null; + public CommonResult socialLogin(@RequestBody @Valid MbrAuthSocialLoginReqVO reqVO) { + String token = authService.socialLogin(reqVO, getClientIP(), getUserAgent()); + return success(SysAuthLoginRespVO.builder().token(token).build()); } @PostMapping("/social-login2") @ApiOperation("社交登录,使用 code 授权码 + 账号密码") public CommonResult socialLogin2(@RequestBody @Valid MbrAuthSocialLogin2ReqVO reqVO) { -// String token = authService.socialLogin2(reqVO, getClientIP(), getUserAgent()); -// // 返回结果 -// return success(MbrAuthLoginRespVO.builder().token(token).build()); - return null; + String token = authService.socialLogin2(reqVO, getClientIP(), getUserAgent()); + return success(SysAuthLoginRespVO.builder().token(token).build()); } @PostMapping("/social-bind") @ApiOperation("社交绑定,使用 code 授权码") public CommonResult socialBind(@RequestBody @Valid MbrAuthSocialBindReqVO reqVO) { -// authService.socialBind(getLoginUserId(), reqVO); -// return CommonResult.success(true); - return null; + authService.socialBind(getLoginUserId(), reqVO); + return CommonResult.success(true); } @DeleteMapping("/social-unbind") @ApiOperation("取消社交绑定") public CommonResult socialUnbind(@RequestBody MbrAuthSocialUnbindReqVO reqVO) { -// socialService.unbindSocialUser(getLoginUserId(), reqVO.getType(), reqVO.getUnionId()); -// return CommonResult.success(true); - return null; + socialService.unbindSocialUser(getLoginUserId(), reqVO.getType(), reqVO.getUnionId(), UserTypeEnum.MEMBER); + return CommonResult.success(true); } } diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialBindReqVO.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialBindReqVO.java index d866411..767446b 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialBindReqVO.java +++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialBindReqVO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo; +import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum; import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.userserver.modules.member.enums.social.SysSocialTypeEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialLogin2ReqVO.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialLogin2ReqVO.java index 371e7e9..584df7e 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialLogin2ReqVO.java +++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialLogin2ReqVO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo; +import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum; import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.userserver.modules.member.enums.social.SysSocialTypeEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialLoginReqVO.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialLoginReqVO.java index 5fdd424..cb43023 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialLoginReqVO.java +++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialLoginReqVO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo; +import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum; import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.userserver.modules.member.enums.social.SysSocialTypeEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialUnbindReqVO.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialUnbindReqVO.java index ea9050a..75e3170 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialUnbindReqVO.java +++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSocialUnbindReqVO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.userserver.modules.system.controller.auth.vo; +import cn.iocoder.yudao.coreservice.modules.system.enums.social.SysSocialTypeEnum; import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.userserver.modules.member.enums.social.SysSocialTypeEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/SysErrorCodeConstants.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/SysErrorCodeConstants.java index e5f4ac6..f24be9c 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/SysErrorCodeConstants.java +++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/SysErrorCodeConstants.java @@ -14,6 +14,7 @@ public interface SysErrorCodeConstants { ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1005000001, "登录失败,账号被禁用"); ErrorCode AUTH_LOGIN_FAIL_UNKNOWN = new ErrorCode(1005000002, "登录失败"); // 登录失败的兜底,未知原因 ErrorCode AUTH_TOKEN_EXPIRED = new ErrorCode(1005000003, "Token 已经过期"); + ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1005000004, "未绑定账号,需要进行绑定"); // ========== SMS CODE 模块 1005001000 ========== ErrorCode USER_SMS_CODE_NOT_FOUND = new ErrorCode(1005001000, "验证码不存在"); @@ -22,4 +23,7 @@ public interface SysErrorCodeConstants { ErrorCode USER_SMS_CODE_NOT_CORRECT = new ErrorCode(1005001003, "验证码不正确"); ErrorCode USER_SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1005001004, "超过每日短信发送数量"); ErrorCode USER_SMS_CODE_SEND_TOO_FAST = new ErrorCode(1005001005, "短信发送过于频率"); + + // ========== 用户模块 1005002000 ========== + ErrorCode USER_NOT_EXISTS = new ErrorCode(1005002001, "用户不存在"); } diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/SysAuthService.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/SysAuthService.java index b35d1c2..628f95c 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/SysAuthService.java +++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/SysAuthService.java @@ -1,8 +1,7 @@ package cn.iocoder.yudao.userserver.modules.system.service.auth; import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService; -import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthLoginReqVO; -import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSmsLoginReqVO; +import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.*; import javax.validation.Valid; @@ -35,4 +34,33 @@ public interface SysAuthService extends SecurityAuthFrameworkService { */ String smsLogin(@Valid SysAuthSmsLoginReqVO reqVO, String userIp, String userAgent); + + /** + * 社交登录,使用 code 授权码 + * + * @param reqVO 登录信息 + * @param userIp 用户 IP + * @param userAgent 用户 UA + * @return 身份令牌,使用 JWT 方式 + */ + String socialLogin(@Valid MbrAuthSocialLoginReqVO reqVO, String userIp, String userAgent); + + /** + * 社交登录,使用 code 授权码 + 账号密码 + * + * @param reqVO 登录信息 + * @param userIp 用户 IP + * @param userAgent 用户 UA + * @return 身份令牌,使用 JWT 方式 + */ + String socialLogin2(@Valid MbrAuthSocialLogin2ReqVO reqVO, String userIp, String userAgent); + + /** + * 社交绑定,使用 code 授权码 + * + * @param userId 用户编号 + * @param reqVO 绑定信息 + */ + void socialBind(Long userId, @Valid MbrAuthSocialBindReqVO reqVO); + } diff --git a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/impl/SysAuthServiceImpl.java b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/impl/SysAuthServiceImpl.java index 0199c07..52acc46 100644 --- a/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/impl/SysAuthServiceImpl.java +++ b/yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/impl/SysAuthServiceImpl.java @@ -1,25 +1,29 @@ package cn.iocoder.yudao.userserver.modules.system.service.auth.impl; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.iocoder.yudao.coreservice.modules.member.dal.dataobject.user.MbrUserDO; +import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.social.SysSocialUserDO; +import cn.iocoder.yudao.coreservice.modules.system.dal.dataobject.user.SysUserDO; import cn.iocoder.yudao.coreservice.modules.system.enums.logger.SysLoginLogTypeEnum; import cn.iocoder.yudao.coreservice.modules.system.enums.logger.SysLoginResultEnum; import cn.iocoder.yudao.coreservice.modules.system.service.auth.SysUserSessionCoreService; import cn.iocoder.yudao.coreservice.modules.system.service.logger.SysLoginLogCoreService; import cn.iocoder.yudao.coreservice.modules.system.service.logger.dto.SysLoginLogCreateReqDTO; +import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialService; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.security.core.LoginUser; import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService; -import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthLoginReqVO; -import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSmsLoginReqVO; +import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.*; import cn.iocoder.yudao.userserver.modules.system.convert.auth.SysAuthConvert; import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum; import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService; import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService; import lombok.extern.slf4j.Slf4j; +import me.zhyd.oauth.model.AuthUser; import org.springframework.context.annotation.Lazy; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; @@ -33,6 +37,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -59,6 +65,9 @@ public class SysAuthServiceImpl implements SysAuthService { private SysLoginLogCoreService loginLogCoreService; @Resource private SysUserSessionCoreService userSessionCoreService; + @Resource + private SysSocialService socialService; + private static final UserTypeEnum userTypeEnum = UserTypeEnum.MEMBER; @Override public UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException { @@ -99,6 +108,65 @@ public class SysAuthServiceImpl implements SysAuthService { return userSessionCoreService.createUserSession(loginUser, userIp, userAgent); } + @Override + public String socialLogin(MbrAuthSocialLoginReqVO reqVO, String userIp, String userAgent) { + // 使用 code 授权码,进行登录 + AuthUser authUser = socialService.getAuthUser(reqVO.getType(), reqVO.getCode(), reqVO.getState()); + org.springframework.util.Assert.notNull(authUser, "授权用户不为空"); + + // 如果未绑定 SysSocialUserDO 用户,则无法自动登录,进行报错 + String unionId = socialService.getAuthUserUnionId(authUser); + List socialUsers = socialService.getAllSocialUserList(reqVO.getType(), unionId, userTypeEnum); + if (CollUtil.isEmpty(socialUsers)) { + throw exception(AUTH_THIRD_LOGIN_NOT_BIND); + } + + // 自动登录 + MbrUserDO user = userService.getUser(socialUsers.get(0).getUserId()); + if (user == null) { + throw exception(USER_NOT_EXISTS); + } + this.createLoginLog(user.getMobile(), SysLoginLogTypeEnum.LOGIN_SOCIAL, SysLoginResultEnum.SUCCESS); + + // 创建 LoginUser 对象 + LoginUser loginUser = SysAuthConvert.INSTANCE.convert(user); + // TODO 芋艿:需要改造下,增加各种登录方式 +// loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId())); // 获取用户角色列表 + + // 绑定社交用户(更新) + socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, userTypeEnum); + + // 缓存登录用户到 Redis 中,返回 sessionId 编号 + return userSessionCoreService.createUserSession(loginUser, userIp, userAgent); + } + + @Override + public String socialLogin2(MbrAuthSocialLogin2ReqVO reqVO, String userIp, String userAgent) { + // 使用 code 授权码,进行登录 + AuthUser authUser = socialService.getAuthUser(reqVO.getType(), reqVO.getCode(), reqVO.getState()); + org.springframework.util.Assert.notNull(authUser, "授权用户不为空"); + + // 使用账号密码,进行登录。 + LoginUser loginUser = this.login0(reqVO.getUsername(), reqVO.getPassword()); +// loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId())); // 获取用户角色列表 + + // 绑定社交用户(新增) + socialService.bindSocialUser(loginUser.getId(), reqVO.getType(), authUser, userTypeEnum); + + // 缓存登录用户到 Redis 中,返回 sessionId 编号 + return userSessionCoreService.createUserSession(loginUser, userIp, userAgent); + } + + @Override + public void socialBind(Long userId, MbrAuthSocialBindReqVO reqVO) { + // 使用 code 授权码,进行登录 + AuthUser authUser = socialService.getAuthUser(reqVO.getType(), reqVO.getCode(), reqVO.getState()); + org.springframework.util.Assert.notNull(authUser, "授权用户不为空"); + + // 绑定社交用户(新增) + socialService.bindSocialUser(userId, reqVO.getType(), authUser, userTypeEnum); + } + private LoginUser login0(String username, String password) { final SysLoginLogTypeEnum logTypeEnum = SysLoginLogTypeEnum.LOGIN_USERNAME; // 用户验证 @@ -207,7 +275,7 @@ public class SysAuthServiceImpl implements SysAuthService { reqDTO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType()); reqDTO.setTraceId(TracerUtils.getTraceId()); reqDTO.setUserId(userId); - reqDTO.setUserType(UserTypeEnum.MEMBER.getValue()); + reqDTO.setUserType(userTypeEnum.getValue()); reqDTO.setUsername(username); reqDTO.setUserAgent(ServletUtils.getUserAgent()); reqDTO.setUserIp(ServletUtils.getClientIP()); diff --git a/yudao-user-server/src/main/resources/application-dev.yaml b/yudao-user-server/src/main/resources/application-dev.yaml index b6bfbd1..bbf7398 100644 --- a/yudao-user-server/src/main/resources/application-dev.yaml +++ b/yudao-user-server/src/main/resources/application-dev.yaml @@ -138,3 +138,30 @@ yudao: - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 demo: true # 开启演示模式 + + +justauth: + enabled: true + type: + GITEE: # Gitee + client-id: ee61f0374a4c6c404a8717094caa7a410d76950e45ff60348015830c519ba5c1 + client-secret: 7c044a5671be3b051414db0cf2cec6ad702dd298d2416ba24ceaf608e6fa26f9 + ignore-check-redirect-uri: true + DINGTALK: # 钉钉 + client-id: dingvrnreaje3yqvzhxg + client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI + ignore-check-redirect-uri: true + WECHAT_ENTERPRISE: # 企业微信 + client-id: wwd411c69a39ad2e54 + client-secret: 1wTb7hYxnpT2TUbIeHGXGo7T0odav1ic10mLdyyATOw + agent-id: 1000004 + ignore-check-redirect-uri: true + WECHAT_MP: # 微信公众平台 - H5 https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index + client-id: wxa5a05b85ac361f96 + client-secret: 247073c7cebb67f27f0e769195c2a57e + ignore-check-redirect-uri: true + cache: + type: REDIS + prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE:: + timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 + diff --git a/yudao-user-server/src/main/resources/application-local.yaml b/yudao-user-server/src/main/resources/application-local.yaml index 506ee19..74f8abb 100644 --- a/yudao-user-server/src/main/resources/application-local.yaml +++ b/yudao-user-server/src/main/resources/application-local.yaml @@ -140,3 +140,29 @@ yudao: - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 demo: false # 关闭演示模式 + + +justauth: + enabled: true + type: + GITEE: # Gitee + client-id: ee61f0374a4c6c404a8717094caa7a410d76950e45ff60348015830c519ba5c1 + client-secret: 7c044a5671be3b051414db0cf2cec6ad702dd298d2416ba24ceaf608e6fa26f9 + ignore-check-redirect-uri: true + DINGTALK: # 钉钉 + client-id: dingvrnreaje3yqvzhxg + client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI + ignore-check-redirect-uri: true + WECHAT_ENTERPRISE: # 企业微信 + client-id: wwd411c69a39ad2e54 + client-secret: 1wTb7hYxnpT2TUbIeHGXGo7T0odav1ic10mLdyyATOw + agent-id: 1000004 + ignore-check-redirect-uri: true + WECHAT_MP: # 微信公众平台 - H5 https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index + client-id: wxa5a05b85ac361f96 + client-secret: 247073c7cebb67f27f0e769195c2a57e + ignore-check-redirect-uri: true + cache: + type: REDIS + prefix: 'social_auth_state:' # 缓存前缀,目前只对 Redis 缓存生效,默认 JUSTAUTH::STATE:: + timeout: 24h # 超时时长,目前只对 Redis 缓存生效,默认 3 分钟 \ No newline at end of file -- libgit2 0.21.4