Jelajahi Sumber

完整完成钉钉的三方接入

YunaiV 3 tahun lalu
induk
melakukan
9be00d6035
23 mengubah file dengan 401 tambahan dan 94 penghapusan
  1. 26 20
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/SysAuthController.java
  2. 48 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/vo/auth/SysAuthSocialLogin2ReqVO.java
  3. 4 4
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/vo/auth/SysAuthSocialLoginReqVO.java
  4. 5 5
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/convert/auth/SysAuthConvert.java
  5. 12 11
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/dataobject/user/SysSocialUserDO.java
  6. 2 2
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/mysql/social/SysSysUserSocialMapper.java
  7. 2 2
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/mysql/social/SysUserSocialMapper.java
  8. 18 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/mysql/user/SysSocialUserMapper.java
  9. 7 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/redis/SysRedisKeyConstants.java
  10. 38 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/redis/auth/SysAuthSocialUserRedisDAO.java
  11. 1 1
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/redis/auth/SysLoginUserRedisDAO.java
  12. 3 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/SysErrorCodeConstants.java
  13. 1 0
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/logger/SysLoginLogTypeEnum.java
  14. 1 4
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/user/SysUserSocialTypeEnum.java
  15. 14 3
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/SysAuthService.java
  16. 134 20
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java
  17. 2 2
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/SysUserServiceImpl.java
  18. 21 6
      yudao-admin-ui/src/api/login.js
  19. 1 1
      yudao-admin-ui/src/permission.js
  20. 2 2
      yudao-admin-ui/src/router/index.js
  21. 37 1
      yudao-admin-ui/src/store/modules/user.js
  22. 4 4
      yudao-admin-ui/src/views/login.vue
  23. 18 6
      yudao-admin-ui/src/views/socialLogin.vue

+ 26 - 20
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/SysAuthController.java

@@ -1,6 +1,5 @@
 package cn.iocoder.yudao.adminserver.modules.system.controller.auth;
 
-import cn.hutool.json.JSONUtil;
 import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.*;
 import cn.iocoder.yudao.adminserver.modules.system.convert.auth.SysAuthConvert;
 import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.permission.SysMenuDO;
@@ -23,9 +22,6 @@ import io.swagger.annotations.ApiImplicitParam;
 import io.swagger.annotations.ApiImplicitParams;
 import io.swagger.annotations.ApiOperation;
 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.validation.annotation.Validated;
@@ -100,16 +96,16 @@ public class SysAuthController {
         return success(SysAuthConvert.INSTANCE.buildMenuTree(menuList));
     }
 
-    // ========== 三方登陆相关 ==========
+    // ========== 社交登陆相关 ==========
 
-    @GetMapping("/third-login-redirect")
-    @ApiOperation("三方登陆的跳转")
+    @GetMapping("/social-login-redirect")
+    @ApiOperation("社交登陆的跳转")
     @ApiImplicitParams({
             @ApiImplicitParam(name = "type", value = "三方类型", required = true, dataTypeClass = Integer.class),
             @ApiImplicitParam(name = "redirectUri", value = "回调路径", dataTypeClass = String.class)
     })
-    public CommonResult<String> thirdLoginRedirect(@RequestParam("type") Integer type,
-                                                   @RequestParam("redirectUri") String redirectUri) {
+    public CommonResult<String> socialLoginRedirect(@RequestParam("type") Integer type,
+                                                    @RequestParam("redirectUri") String redirectUri) {
         // 获得对应的 AuthRequest 实现
         AuthRequest authRequest = authRequestFactory.get(SysUserSocialTypeEnum.valueOfType(type).getSource());
         // 生成跳转地址
@@ -118,20 +114,30 @@ public class SysAuthController {
         return CommonResult.success(authorizeUri);
     }
 
-    @PostMapping("/third-login")
-    @ApiOperation("三方登陆,使用 code 授权码")
+    @PostMapping("/social-login")
+    @ApiOperation("社交登陆,使用 code 授权码")
     @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
-    public CommonResult<SysAuthLoginRespVO> thirdLogin(@RequestBody @Valid SysAuthThirdLoginReqVO reqVO) {
-        String token = authService.thirdLogin(reqVO, getClientIP(), getUserAgent());
-        return null;
+    public CommonResult<SysAuthLoginRespVO> socialLogin(@RequestBody @Valid SysAuthSocialLoginReqVO reqVO) {
+        String token = authService.socialLogin(reqVO, getClientIP(), getUserAgent());
+        // 返回结果
+        return success(SysAuthLoginRespVO.builder().token(token).build());
     }
 
-    @RequestMapping("/{type}/callback")
-    public AuthResponse login(@PathVariable String type, AuthCallback callback) {
-        AuthRequest authRequest = authRequestFactory.get(type);
-        AuthResponse<AuthUser> response = authRequest.login(callback);
-        log.info("【response】= {}", JSONUtil.toJsonStr(response));
-        return response;
+    @PostMapping("/social-login2")
+    @ApiOperation("社交登陆,使用 code 授权码 + 账号密码")
+    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
+    public CommonResult<SysAuthLoginRespVO> socialLogin2(@RequestBody @Valid SysAuthSocialLogin2ReqVO reqVO) {
+        String token = authService.socialLogin2(reqVO, getClientIP(), getUserAgent());
+        // 返回结果
+        return success(SysAuthLoginRespVO.builder().token(token).build());
     }
 
+//    @RequestMapping("/{type}/callback")
+//    public AuthResponse login(@PathVariable String type, AuthCallback callback) {
+//        AuthRequest authRequest = authRequestFactory.get(type);
+//        AuthResponse<AuthUser> response = authRequest.login(callback);
+//        log.info("【response】= {}", JSONUtil.toJsonStr(response));
+//        return response;
+//    }
+
 }

+ 48 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/vo/auth/SysAuthSocialLogin2ReqVO.java

@@ -0,0 +1,48 @@
+package cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth;
+
+import cn.iocoder.yudao.adminserver.modules.system.enums.user.SysUserSocialTypeEnum;
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+
+@ApiModel("社交登陆 Request VO,使用 code 授权码 + 账号密码")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class SysAuthSocialLogin2ReqVO {
+
+    @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值")
+    @InEnum(SysUserSocialTypeEnum.class)
+    @NotNull(message = "社交平台的类型不能为空")
+    private Integer type;
+
+    @ApiModelProperty(value = "授权码", required = true, example = "1024")
+    @NotEmpty(message = "授权码不能为空")
+    private String code;
+
+    @ApiModelProperty(value = "state", required = true, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62")
+    @NotEmpty(message = "state 不能为空")
+    private String state;
+
+    @ApiModelProperty(value = "账号", required = true, example = "yudaoyuanma")
+    @NotEmpty(message = "登陆账号不能为空")
+    @Length(min = 4, max = 16, message = "账号长度为 4-16 位")
+    @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
+    private String username;
+
+    @ApiModelProperty(value = "密码", required = true, example = "buzhidao")
+    @NotEmpty(message = "密码不能为空")
+    @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
+    private String password;
+
+}

+ 4 - 4
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/vo/auth/SysAuthThirdLoginReqVO.java → yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/controller/auth/vo/auth/SysAuthSocialLoginReqVO.java

@@ -14,16 +14,16 @@ import javax.validation.constraints.NotEmpty;
 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Pattern;
 
-@ApiModel("三方登陆 Request VO,使用 code 授权码")
+@ApiModel("社交登陆 Request VO,使用 code 授权码")
 @Data
 @NoArgsConstructor
 @AllArgsConstructor
 @Builder
-public class SysAuthThirdLoginReqVO {
+public class SysAuthSocialLoginReqVO {
 
-    @ApiModelProperty(value = "三方平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值")
+    @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值")
     @InEnum(SysUserSocialTypeEnum.class)
-    @NotNull(message = "三方平台的类型不能为空")
+    @NotNull(message = "社交平台的类型不能为空")
     private Integer type;
 
     @ApiModelProperty(value = "授权码", required = true, example = "1024")

+ 5 - 5
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/convert/auth/SysAuthConvert.java

@@ -2,10 +2,10 @@ package cn.iocoder.yudao.adminserver.modules.system.convert.auth;
 
 import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthMenuRespVO;
 import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthPermissionInfoRespVO;
-import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthThirdLoginReqVO;
+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.controller.user.vo.profile.SysUserProfileUpdatePasswordReqVO;
 import cn.iocoder.yudao.adminserver.modules.system.controller.user.vo.profile.SysUserProfileUpdateReqVO;
-import cn.iocoder.yudao.adminserver.modules.system.controller.user.vo.user.SysUserCreateReqVO;
 import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.permission.SysMenuDO;
 import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.permission.SysRoleDO;
 import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.user.SysUserDO;
@@ -13,10 +13,8 @@ import cn.iocoder.yudao.adminserver.modules.system.enums.permission.MenuIdEnum;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.security.core.LoginUser;
 import me.zhyd.oauth.model.AuthCallback;
-import me.zhyd.oauth.model.AuthUser;
 import org.mapstruct.Mapper;
 import org.mapstruct.Mapping;
-import org.mapstruct.Mappings;
 import org.mapstruct.factory.Mappers;
 import org.slf4j.LoggerFactory;
 
@@ -45,7 +43,9 @@ public interface SysAuthConvert {
 
     LoginUser convert(SysUserProfileUpdatePasswordReqVO reqVO);
 
-    AuthCallback convert(SysAuthThirdLoginReqVO bean);
+    AuthCallback convert(SysAuthSocialLoginReqVO bean);
+
+    AuthCallback convert(SysAuthSocialLogin2ReqVO bean);
 
     /**
      * 将菜单列表,构建成菜单树

+ 12 - 11
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/dataobject/user/SysUserSocialDO.java → yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/dataobject/user/SysSocialUserDO.java

@@ -1,6 +1,5 @@
 package cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.user;
 
-import cn.iocoder.yudao.adminserver.modules.system.enums.user.SysUserSocialTypeEnum;
 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 import com.baomidou.mybatisplus.annotation.TableId;
@@ -8,18 +7,18 @@ import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.*;
 
 /**
- * 三方登陆信息
- * 通过 {@link SysUserSocialDO#getUserId()} 关联到对应的 {@link SysUserDO}
+ * 社交用户
+ * 通过 {@link SysSocialUserDO#getUserId()} 关联到对应的 {@link SysUserDO}
  *
  * @author weir
  */
-@TableName(value = "sys_user_social", autoResultMap = true)
+@TableName(value = "sys_social_user", autoResultMap = true)
 @Data
 @EqualsAndHashCode(callSuper = true)
 @Builder
 @NoArgsConstructor
 @AllArgsConstructor
-public class SysUserSocialDO extends BaseDO {
+public class SysSocialUserDO extends BaseDO {
 
     /**
      * 自增主键
@@ -27,7 +26,7 @@ public class SysUserSocialDO extends BaseDO {
     @TableId
     private Long id;
     /**
-     * 用户编号
+     * 关联的用户编号
      */
     private Long userId;
     /**
@@ -38,20 +37,22 @@ public class SysUserSocialDO extends BaseDO {
     private Integer userType;
 
     /**
-     * 三方平台的类型
+     * 社交平台的类型
+     *
+     * 枚举 {@link UserTypeEnum}
      */
-    private SysUserSocialTypeEnum type;
+    private Integer type;
 
     /**
-     * 三方 openid
+     * 社交 openid
      */
     private String openid;
     /**
-     * 三方 token
+     * 社交 token
      */
     private String token;
     /**
-     * 三方的全局编号
+     * 社交的全局编号
      *
      * 例如说,微信平台的 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/union-id.html
      * 如果没有 unionId 的平台,直接使用 openid 作为该字段的值

+ 2 - 2
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/mysql/social/SysSysUserSocialMapper.java

@@ -1,11 +1,11 @@
 package cn.iocoder.yudao.adminserver.modules.system.dal.mysql.social;
 
-import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.user.SysUserSocialDO;
+import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.user.SysSocialUserDO;
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import org.apache.ibatis.annotations.Mapper;
 
 @Mapper
-public interface SysSysUserSocialMapper extends BaseMapperX<SysUserSocialDO> {
+public interface SysSysUserSocialMapper extends BaseMapperX<SysSocialUserDO> {
 
 
 }

+ 2 - 2
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/mysql/social/SysUserSocialMapper.java

@@ -1,11 +1,11 @@
 package cn.iocoder.yudao.adminserver.modules.system.dal.mysql.social;
 
-import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.user.SysUserSocialDO;
+import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.user.SysSocialUserDO;
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 import org.apache.ibatis.annotations.Mapper;
 
 @Mapper
-public interface SysUserSocialMapper extends BaseMapperX<SysUserSocialDO> {
+public interface SysUserSocialMapper extends BaseMapperX<SysSocialUserDO> {
 
 
 }

+ 18 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/mysql/user/SysSocialUserMapper.java

@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.adminserver.modules.system.dal.mysql.user;
+
+import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.user.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.List;
+
+@Mapper
+public interface SysSocialUserMapper extends BaseMapperX<SysSocialUserDO> {
+
+    default List<SysSocialUserDO> selectListByTypeAndUnionId(Integer userType, Integer type, String unionId) {
+        return selectList(new QueryWrapper<SysSocialUserDO>().eq("user_type", userType)
+                .eq("type", type).eq("union_id", unionId));
+    }
+
+}

+ 7 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/redis/SysRedisKeyConstants.java

@@ -2,6 +2,9 @@ package cn.iocoder.yudao.adminserver.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;
 
@@ -20,4 +23,8 @@ public interface SysRedisKeyConstants {
             "captcha_code:%s", // 参数为 uuid
             STRING, String.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC);
 
+    RedisKeyDefine AUTH_SOCIAL_USER = new RedisKeyDefine("认证的三方用户",
+            "auth_social_user:%d:%s", // 参数为 type,code
+            STRING, AuthUser.class, Duration.ofDays(1));
+
 }

+ 38 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/redis/auth/SysAuthSocialUserRedisDAO.java

@@ -0,0 +1,38 @@
+package cn.iocoder.yudao.adminserver.modules.system.dal.redis.auth;
+
+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.AUTH_SOCIAL_USER;
+
+/**
+ * 社交 {@link me.zhyd.oauth.model.AuthUser} 的 RedisDAO
+ *
+ * @author 芋道源码
+ */
+@Repository
+public class SysAuthSocialUserRedisDAO {
+
+    @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), AUTH_SOCIAL_USER.getTimeout());
+    }
+
+    private static String formatKey(Integer type, String code) {
+        return String.format(AUTH_SOCIAL_USER.getKeyTemplate(), type, code);
+    }
+
+}

+ 1 - 1
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/dal/redis/auth/SysLoginUserRedisDAO.java

@@ -22,7 +22,7 @@ public class SysLoginUserRedisDAO {
     @Resource
     private StringRedisTemplate stringRedisTemplate;
     @Resource
-    private SysUserSessionService sysUserSessionService;
+    private SysUserSessionService sysUserSessionService; // TODO 芋艿:得看看怎么拿出去
 
     public LoginUser get(String sessionId) {
         String redisKey = formatKey(sessionId);

+ 3 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/SysErrorCodeConstants.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.adminserver.modules.system.enums;
 
+import cn.iocoder.yudao.adminserver.modules.tool.framework.errorcode.config.ErrorCodeConfiguration;
 import cn.iocoder.yudao.framework.common.exception.ErrorCode;
 
 /**
@@ -15,6 +16,8 @@ public interface SysErrorCodeConstants {
     ErrorCode AUTH_LOGIN_FAIL_UNKNOWN = new ErrorCode(1002000002, "登录失败"); // 登陆失败的兜底,位置原因
     ErrorCode AUTH_LOGIN_CAPTCHA_NOT_FOUND = new ErrorCode(1002000003, "验证码不存在");
     ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1002000004, "验证码不正确");
+    ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1002000005, "未绑定账号,需要进行绑定");
+    ErrorCode AUTH_THIRD_OAUTH_FAILURE = new ErrorCode(1002000006, "三方授权失败,原因是:{}");
 
     // ========== TOKEN 模块 1002001000 ==========
     ErrorCode TOKEN_EXPIRED = new ErrorCode(1002001000, "Token 已经过期");

+ 1 - 0
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/logger/SysLoginLogTypeEnum.java

@@ -11,6 +11,7 @@ import lombok.Getter;
 public enum SysLoginLogTypeEnum {
 
     LOGIN_USERNAME(100), // 使用账号登录
+    LOGIN_SOCIAL(101), // 使用社交登陆
 
     LOGOUT_SELF(200),  // 自己主动登出
     LOGOUT_TIMEOUT(201), // 超时登出

+ 1 - 4
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/user/SysUserSocialTypeEnum.java

@@ -1,17 +1,14 @@
 package cn.iocoder.yudao.adminserver.modules.system.enums.user;
 
 import cn.hutool.core.util.ArrayUtil;
-import cn.iocoder.yudao.adminserver.modules.system.enums.errorcode.SysErrorCodeTypeEnum;
 import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
-import com.fasterxml.jackson.annotation.JsonCreator;
-import jodd.util.ArraysUtil;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 
 import java.util.Arrays;
 
 /**
- * 用户的三方平台的类型枚举
+ * 用户的社交平台的类型枚举
  *
  * @author 芋道源码
  */

+ 14 - 3
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/SysAuthService.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.adminserver.modules.system.service.auth;
 
-import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthThirdLoginReqVO;
+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.framework.security.core.service.SecurityAuthFrameworkService;
 import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthLoginReqVO;
 
@@ -24,13 +25,23 @@ public interface SysAuthService extends SecurityAuthFrameworkService {
     String login(SysAuthLoginReqVO reqVO, String userIp, String userAgent);
 
     /**
-     * 三方登陆用户,使用 code 授权码
+     * 社交登陆用户,使用 code 授权码
      *
      * @param reqVO 登陆信息
      * @param userIp 用户 IP
      * @param userAgent 用户 UA
      * @return 身份令牌,使用 JWT 方式
      */
-    String thirdLogin(SysAuthThirdLoginReqVO reqVO, String userIp, String userAgent);
+    String socialLogin(SysAuthSocialLoginReqVO reqVO, String userIp, String userAgent);
+
+    /**
+     * 社交登陆用户,使用 code 授权码 + 账号密码
+     *
+     * @param reqVO 登陆信息
+     * @param userIp 用户 IP
+     * @param userAgent 用户 UA
+     * @return 身份令牌,使用 JWT 方式
+     */
+    String socialLogin2(SysAuthSocialLogin2ReqVO reqVO, String userIp, String userAgent);
 
 }

+ 134 - 20
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java

@@ -1,9 +1,15 @@
 package cn.iocoder.yudao.adminserver.modules.system.service.auth.impl;
 
-import cn.hutool.json.JSONUtil;
-import cn.iocoder.yudao.adminserver.modules.system.controller.auth.vo.auth.SysAuthThirdLoginReqVO;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+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.dal.dataobject.user.SysSocialUserDO;
+import cn.iocoder.yudao.adminserver.modules.system.dal.mysql.user.SysSocialUserMapper;
+import cn.iocoder.yudao.adminserver.modules.system.dal.redis.auth.SysAuthSocialUserRedisDAO;
 import cn.iocoder.yudao.adminserver.modules.system.enums.user.SysUserSocialTypeEnum;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.framework.security.core.LoginUser;
@@ -40,6 +46,8 @@ import org.springframework.stereotype.Service;
 import org.springframework.util.Assert;
 
 import javax.annotation.Resource;
+import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -58,6 +66,7 @@ public class SysAuthServiceImpl implements SysAuthService {
     @Resource
     @Lazy // 延迟加载,因为存在相互依赖的问题
     private AuthenticationManager authenticationManager;
+
     @Resource
     private SysUserService userService;
     @Resource
@@ -69,6 +78,11 @@ public class SysAuthServiceImpl implements SysAuthService {
     @Resource
     private SysUserSessionService userSessionService;
 
+    @Resource
+    private SysAuthSocialUserRedisDAO authSocialUserRedisDAO;
+    @Resource
+    private SysSocialUserMapper socialUserMapper;
+
     @Resource
     private AuthRequestFactory authRequestFactory;
 
@@ -109,24 +123,6 @@ public class SysAuthServiceImpl implements SysAuthService {
         return userSessionService.createUserSession(loginUser, userIp, userAgent);
     }
 
-    @Override
-    public String thirdLogin(SysAuthThirdLoginReqVO reqVO, String userIp, String userAgent) {
-        // 使用 code 授权码,进行登陆
-        AuthRequest authRequest = authRequestFactory.get(SysUserSocialTypeEnum.valueOfType(reqVO.getType()).getSource());
-        AuthCallback authCallback = SysAuthConvert.INSTANCE.convert(reqVO);
-        AuthResponse<?> authResponse = authRequest.login(authCallback);
-        log.info("[thirdLogin][请求三方平台 type({}) request({}) response({})]", reqVO.getType(), JsonUtils.toJsonString(authCallback),
-                JsonUtils.toJsonString(authResponse));
-        if (!authResponse.ok()) {
-            throw new RuntimeException(""); // TODO 芋艿:补全
-        }
-        AuthUser authUser = (AuthUser) authResponse.getData();
-
-        // 查找到对应的
-
-        return null;
-    }
-
     private void verifyCaptcha(String username, String captchaUUID, String captchaCode) {
         String code = captchaService.getCaptchaCode(captchaUUID);
         // 验证码不存在
@@ -190,6 +186,124 @@ public class SysAuthServiceImpl implements SysAuthService {
         return permissionService.getUserRoleIds(userId, singleton(CommonStatusEnum.ENABLE.getStatus()));
     }
 
+    @Override
+    public String socialLogin(SysAuthSocialLoginReqVO reqVO, String userIp, String userAgent) {
+        // 使用 code 授权码,进行登陆
+        AuthCallback authCallback = SysAuthConvert.INSTANCE.convert(reqVO);
+        AuthUser authUser = this.obtainAuthUserFromCache(reqVO.getType(), authCallback);
+
+        // 如果未绑定 SysSocialUserDO 用户,则无法自动登陆,进行报错
+        String unionId = getAuthUserUnionId(authUser);
+        List<SysSocialUserDO> socialUsers = socialUserMapper.selectListByTypeAndUnionId(UserTypeEnum.ADMIN.getValue(),
+                reqVO.getType(), unionId);
+        if (CollUtil.isEmpty(socialUsers)) {
+            throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
+        }
+
+        // 使用账号密码,进行登陆。
+        SysUserDO user = userService.getUser(socialUsers.get(0).getUserId());
+        if (user == null) {
+            throw exception(USER_NOT_EXISTS);
+        }
+        LoginUser loginUser = SysAuthConvert.INSTANCE.convert(user);
+        // TODO 芋艿:需要改造下,增加各种登陆方式
+        loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId())); // 获取用户角色列表
+
+        // 保存社交用户
+        this.saveSocialUser(reqVO.getType(), socialUsers, loginUser.getId(), authUser);
+
+        // 缓存登陆用户到 Redis 中,返回 sessionId 编号
+        return userSessionService.createUserSession(loginUser, userIp, userAgent);
+    }
+
+    @Override
+    public String socialLogin2(SysAuthSocialLogin2ReqVO reqVO, String userIp, String userAgent) {
+        // 使用 code 授权码,进行登陆
+        AuthCallback authCallback = SysAuthConvert.INSTANCE.convert(reqVO);
+        AuthUser authUser = this.obtainAuthUserFromCache(reqVO.getType(), authCallback);
+
+        // 查询社交对应的 SysSocialUserDO 用户
+        String unionId = getAuthUserUnionId(authUser);
+        List<SysSocialUserDO> socialUsers = socialUserMapper.selectListByTypeAndUnionId(UserTypeEnum.ADMIN.getValue(),
+                reqVO.getType(), unionId);
+
+        // 使用账号密码,进行登陆。
+        LoginUser loginUser = this.login0(reqVO.getUsername(), reqVO.getPassword()); // TODO 芋艿:需要改造下,增加各种登陆方式
+        loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId())); // 获取用户角色列表
+
+        // 保存社交用户
+        this.saveSocialUser(reqVO.getType(), socialUsers, loginUser.getId(), authUser);
+
+        // 缓存登陆用户到 Redis 中,返回 sessionId 编号
+        return userSessionService.createUserSession(loginUser, userIp, userAgent);
+    }
+
+    private static String getAuthUserUnionId(AuthUser authUser) {
+        return StrUtil.blankToDefault(authUser.getToken().getUnionId(), authUser.getUuid());
+    }
+
+    private AuthUser obtainAuthUserFromCache(Integer type, AuthCallback authCallback) {
+        // 从缓存中获取
+        AuthUser authUser = authSocialUserRedisDAO.get(type, authCallback);
+        if (authUser != null) {
+            return authUser;
+        }
+
+        // 请求获取
+        authUser = this.obtainAuthUser(type, authCallback);
+        authSocialUserRedisDAO.set(type, authCallback, authUser);
+        return authUser;
+    }
+
+    private AuthUser obtainAuthUser(Integer type, AuthCallback authCallback) {
+        AuthRequest authRequest = authRequestFactory.get(SysUserSocialTypeEnum.valueOfType(type).getSource());
+        AuthResponse<?> authResponse = authRequest.login(authCallback);
+        log.info("[obtainAuthUser][请求三方平台 type({}) request({}) response({})]", type, JsonUtils.toJsonString(authCallback),
+                JsonUtils.toJsonString(authResponse));
+        if (!authResponse.ok()) {
+            throw exception(AUTH_THIRD_OAUTH_FAILURE, authResponse.getMsg());
+        }
+        return (AuthUser) authResponse.getData();
+    }
+
+    /**
+     * 保存社交用户
+     *
+     * @param socialUsers 已存在的社交用户列表
+     * @param userId 绑定的用户编号
+     * @param authUser 需要保存的社交用户信息
+     */
+    private void saveSocialUser(Integer type, List<SysSocialUserDO> socialUsers, Long userId, AuthUser authUser) {
+        // 逻辑一:如果 socialUsers 指定的 userId 改变,需要进行更新
+        // 例如说,一个微信 unionId 对应了多个社交账号,结果其中有个关联了新的 userId,则其它也要跟着修改
+        // 考虑到 socialUsers 一般比较少,直接 for 循环更新即可
+        socialUsers.forEach(socialUser -> {
+            if (Objects.equals(socialUser.getUserId(), userId)) {
+                return;
+            }
+            socialUserMapper.updateById(new SysSocialUserDO().setUserId(socialUser.getUserId()).setUserId(userId));
+        });
+
+        // 逻辑二:如果 authUser 不存在于 socialUsers 中,则进行新增;否则,进行更新
+        SysSocialUserDO saveSocialUser = CollUtil.findOneByField(socialUsers, "openid", authUser.getUuid());
+        if (saveSocialUser == null) {
+            saveSocialUser = new SysSocialUserDO();
+            saveSocialUser.setUserId(userId).setUserType(UserTypeEnum.ADMIN.getValue());
+            saveSocialUser.setType(type).setOpenid(authUser.getUuid()).setToken(authUser.getToken().getAccessToken())
+                    .setUnionId(getAuthUserUnionId(authUser)).setRawTokenInfo(JsonUtils.toJsonString(authUser.getToken()));
+            saveSocialUser.setNickname(authUser.getNickname()).setAvatar(authUser.getAvatar())
+                            .setRawUserInfo(JsonUtils.toJsonString(authUser.getRawUserInfo()));
+            socialUserMapper.insert(saveSocialUser);
+        } else {
+            saveSocialUser = new SysSocialUserDO().setId(saveSocialUser.getId());
+            saveSocialUser.setToken(authUser.getToken().getAccessToken()).setUnionId(getAuthUserUnionId(authUser))
+                    .setRawTokenInfo(JsonUtils.toJsonString(authUser.getToken()));
+            saveSocialUser.setNickname(authUser.getNickname()).setAvatar(authUser.getAvatar())
+                    .setRawUserInfo(JsonUtils.toJsonString(authUser.getRawUserInfo()));
+            socialUserMapper.updateById(saveSocialUser);
+        }
+    }
+
     @Override
     public void logout(String token) {
         // 查询用户信息

+ 2 - 2
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/user/SysUserServiceImpl.java

@@ -4,7 +4,7 @@ import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.io.IoUtil;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.user.SysUserSocialDO;
+import cn.iocoder.yudao.adminserver.modules.system.dal.dataobject.user.SysSocialUserDO;
 import cn.iocoder.yudao.adminserver.modules.system.dal.mysql.social.SysUserSocialMapper;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.exception.ServiceException;
@@ -80,7 +80,7 @@ public class SysUserServiceImpl implements SysUserService {
 
     @Override
     public Long bindSocialUSer(Long sysUserId, String socialUSerId) {
-        SysUserSocialDO userSocialDO = new SysUserSocialDO();
+        SysSocialUserDO userSocialDO = new SysSocialUserDO();
         userSocialDO.setUserId(sysUserId);
 //        userSocialDO.setSocialUserId(socialUSerId);
         userSocialMapper.insert(userSocialDO);

+ 21 - 6
yudao-admin-ui/src/api/login.js

@@ -39,18 +39,18 @@ export function getCodeImg() {
   })
 }
 
-// 三方登陆的跳转
-export function thirdLoginRedirect(type, redirectUri) {
+// 社交登陆的跳转
+export function socialLoginRedirect(type, redirectUri) {
   return request({
-    url: '/third-login-redirect?type=' + type + '&redirectUri=' + redirectUri,
+    url: '/social-login-redirect?type=' + type + '&redirectUri=' + redirectUri,
     method: 'get'
   })
 }
 
-// 三方登陆,使用 code 授权码
-export function thirdLogin(type, code, state) {
+// 社交登陆,使用 code 授权码
+export function socialLogin(type, code, state) {
   return request({
-    url: '/third-login',
+    url: '/social-login',
     method: 'post',
     data: {
       type,
@@ -59,3 +59,18 @@ export function thirdLogin(type, code, state) {
     }
   })
 }
+
+// 社交登陆,使用 code 授权码 + + 账号密码
+export function socialLogin2(type, code, state, username, password) {
+  return request({
+    url: '/social-login2',
+    method: 'post',
+    data: {
+      type,
+      code,
+      state,
+      username,
+      password
+    }
+  })
+}

+ 1 - 1
yudao-admin-ui/src/permission.js

@@ -7,7 +7,7 @@ import { getToken } from '@/utils/auth'
 
 NProgress.configure({ showSpinner: false })
 
-const whiteList = ['/login', '/third-login',  '/auth-redirect', '/bind', '/register', '/oauthLogin/gitee']
+const whiteList = ['/login', '/social-login',  '/auth-redirect', '/bind', '/register', '/oauthLogin/gitee']
 
 router.beforeEach((to, from, next) => {
   NProgress.start()

+ 2 - 2
yudao-admin-ui/src/router/index.js

@@ -44,8 +44,8 @@ export const constantRoutes = [
     hidden: true
   },
   {
-    path: '/third-login',
-    component: (resolve) => require(['@/views/thirdLogin'], resolve),
+    path: '/social-login',
+    component: (resolve) => require(['@/views/socialLogin'], resolve),
     hidden: true
   },
   {

+ 37 - 1
yudao-admin-ui/src/store/modules/user.js

@@ -1,4 +1,4 @@
-import { login, logout, getInfo } from '@/api/login'
+import {login, logout, getInfo, socialLogin, socialLogin2} from '@/api/login'
 import { getToken, setToken, removeToken } from '@/utils/auth'
 
 const user = {
@@ -47,6 +47,42 @@ const user = {
       })
     },
 
+    // 社交登陆
+    SocialLogin({ commit }, userInfo) {
+      const code = userInfo.code
+      const state = userInfo.state
+      const type = userInfo.type
+      return new Promise((resolve, reject) => {
+        socialLogin(type, code, state).then(res => {
+          res = res.data;
+          setToken(res.token)
+          commit('SET_TOKEN', res.token)
+          resolve()
+        }).catch(error => {
+          reject(error)
+        })
+      })
+    },
+
+    // 社交登陆
+    SocialLogin2({ commit }, userInfo) {
+      const code = userInfo.code
+      const state = userInfo.state
+      const type = userInfo.type
+      const username = userInfo.username.trim()
+      const password = userInfo.password
+      return new Promise((resolve, reject) => {
+        socialLogin2(type, code, state, username, password).then(res => {
+          res = res.data;
+          setToken(res.token)
+          commit('SET_TOKEN', res.token)
+          resolve()
+        }).catch(error => {
+          reject(error)
+        })
+      })
+    },
+
     // 获取用户信息
     GetInfo({ commit, state }) {
       return new Promise((resolve, reject) => {

+ 4 - 4
yudao-admin-ui/src/views/login.vue

@@ -45,7 +45,7 @@
 </template>
 
 <script>
-import { getCodeImg,thirdLoginRedirect } from "@/api/login";
+import { getCodeImg,socialLoginRedirect } from "@/api/login";
 import Cookies from "js-cookie";
 import { encrypt, decrypt } from '@/utils/jsencrypt'
 
@@ -143,11 +143,11 @@ export default {
       // 设置登陆中
       this.loading = true;
       // 计算 redirectUri
-      // const redirectUri = location.origin + '/third-login';
+      const redirectUri = location.origin + '/social-login';
       // const redirectUri = 'http://127.0.0.1:48080/api/gitee/callback';
-      const redirectUri = 'http://127.0.0.1:48080/api/dingtalk/callback';
+      // const redirectUri = 'http://127.0.0.1:48080/api/dingtalk/callback';
       // 进行跳转
-      thirdLoginRedirect(provider.type, redirectUri).then((res) => {
+      socialLoginRedirect(provider.type, redirectUri).then((res) => {
         // console.log(res.url);
         window.location.href = res.data;
       });

+ 18 - 6
yudao-admin-ui/src/views/thirdLogin.vue → yudao-admin-ui/src/views/socialLogin.vue

@@ -28,7 +28,7 @@
 </template>
 
 <script>
-import { thirdLogin } from "@/api/login";
+import { socialLogin } from "@/api/login";
 import Cookies from "js-cookie";
 import { encrypt, decrypt } from '@/utils/jsencrypt'
 
@@ -39,7 +39,7 @@ export default {
       loginForm: {
         username: "admin",
         password: "admin123",
-        rememberMe: false,
+        rememberMe: false, // TODO 芋艿:后面看情况,去掉这块
       },
       loginRules: {
         username: [
@@ -68,11 +68,17 @@ export default {
   created() {
     this.getCookie();
     // 三方登陆相关
-    this.type = 10;
+    this.type = 20;
     this.code = this.$route.query.code;
     this.state = this.$route.query.state;
-    thirdLogin(this.type, this.code, this.state).then(res => {
-      debugger
+    this.$store.dispatch("SocialLogin", {
+      code: this.code,
+      state: this.state,
+      type: this.type
+    }).then(() => {
+      this.$router.push({ path: this.redirect || "/" }).catch(()=>{});
+    }).catch(() => {
+      this.loading = false;
     });
   },
   methods: {
@@ -97,7 +103,13 @@ export default {
             Cookies.remove("username");
             Cookies.remove("password");
           }
-          this.$store.dispatch("Login", this.loginForm).then(() => {
+          this.$store.dispatch("SocialLogin2", {
+            code: this.code,
+            state: this.state,
+            type: this.type,
+            username: this.loginForm.username,
+            password: this.loginForm.password
+          }).then(() => {
             this.$router.push({ path: this.redirect || "/" }).catch(()=>{});
           }).catch(() => {
             this.loading = false;