完整完成钉钉的三方接入
This commit is contained in:
parent
e05d90590e
commit
9be00d6035
|
@ -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;
|
||||
// }
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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")
|
|
@ -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);
|
||||
|
||||
/**
|
||||
* 将菜单列表,构建成菜单树
|
||||
|
|
|
@ -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 作为该字段的值
|
|
@ -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> {
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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 已经过期");
|
||||
|
|
|
@ -11,6 +11,7 @@ import lombok.Getter;
|
|||
public enum SysLoginLogTypeEnum {
|
||||
|
||||
LOGIN_USERNAME(100), // 使用账号登录
|
||||
LOGIN_SOCIAL(101), // 使用社交登陆
|
||||
|
||||
LOGOUT_SELF(200), // 自己主动登出
|
||||
LOGOUT_TIMEOUT(201), // 超时登出
|
||||
|
|
|
@ -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 芋道源码
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
// 查询用户信息
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
|
@ -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;
|
Loading…
Reference in New Issue