commit
0bf5b20fd7
|
@ -1,6 +1,9 @@
|
||||||
package cn.iocoder.yudao.framework.social.config;
|
package cn.iocoder.yudao.framework.social.config;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ReflectUtil;
|
||||||
import cn.iocoder.yudao.framework.social.core.YudaoAuthRequestFactory;
|
import cn.iocoder.yudao.framework.social.core.YudaoAuthRequestFactory;
|
||||||
|
import com.xkcoding.http.HttpUtil;
|
||||||
|
import com.xkcoding.http.support.hutool.HutoolImpl;
|
||||||
import com.xkcoding.justauth.autoconfigure.JustAuthProperties;
|
import com.xkcoding.justauth.autoconfigure.JustAuthProperties;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import me.zhyd.oauth.cache.AuthStateCache;
|
import me.zhyd.oauth.cache.AuthStateCache;
|
||||||
|
@ -23,6 +26,9 @@ public class YudaoSocialAutoConfiguration {
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty(prefix = "justauth", value = "enabled", havingValue = "true", matchIfMissing = true)
|
@ConditionalOnProperty(prefix = "justauth", value = "enabled", havingValue = "true", matchIfMissing = true)
|
||||||
public YudaoAuthRequestFactory yudaoAuthRequestFactory(JustAuthProperties properties, AuthStateCache authStateCache) {
|
public YudaoAuthRequestFactory yudaoAuthRequestFactory(JustAuthProperties properties, AuthStateCache authStateCache) {
|
||||||
|
// 需要修改 HttpUtil 使用的实现,避免类报错
|
||||||
|
HttpUtil.setHttp(new HutoolImpl());
|
||||||
|
// 创建 YudaoAuthRequestFactory
|
||||||
return new YudaoAuthRequestFactory(properties, authStateCache);
|
return new YudaoAuthRequestFactory(properties, authStateCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti
|
||||||
|
|
||||||
@Api(tags = "用户 APP - 认证")
|
@Api(tags = "用户 APP - 认证")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/member/")
|
@RequestMapping("/member/auth")
|
||||||
@Validated
|
@Validated
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class AppAuthController {
|
public class AppAuthController {
|
||||||
|
@ -33,7 +33,6 @@ public class AppAuthController {
|
||||||
|
|
||||||
@PostMapping("/login")
|
@PostMapping("/login")
|
||||||
@ApiOperation("使用手机 + 密码登录")
|
@ApiOperation("使用手机 + 密码登录")
|
||||||
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志
|
|
||||||
public CommonResult<AppAuthLoginRespVO> login(@RequestBody @Valid AppAuthLoginReqVO reqVO) {
|
public CommonResult<AppAuthLoginRespVO> login(@RequestBody @Valid AppAuthLoginReqVO reqVO) {
|
||||||
String token = authService.login(reqVO, getClientIP(), getUserAgent());
|
String token = authService.login(reqVO, getClientIP(), getUserAgent());
|
||||||
// 返回结果
|
// 返回结果
|
||||||
|
@ -42,7 +41,6 @@ public class AppAuthController {
|
||||||
|
|
||||||
@PostMapping("/sms-login")
|
@PostMapping("/sms-login")
|
||||||
@ApiOperation("使用手机 + 验证码登录")
|
@ApiOperation("使用手机 + 验证码登录")
|
||||||
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志
|
|
||||||
public CommonResult<AppAuthLoginRespVO> smsLogin(@RequestBody @Valid AppAuthSmsLoginReqVO reqVO) {
|
public CommonResult<AppAuthLoginRespVO> smsLogin(@RequestBody @Valid AppAuthSmsLoginReqVO reqVO) {
|
||||||
String token = authService.smsLogin(reqVO, getClientIP(), getUserAgent());
|
String token = authService.smsLogin(reqVO, getClientIP(), getUserAgent());
|
||||||
// 返回结果
|
// 返回结果
|
||||||
|
@ -51,7 +49,6 @@ public class AppAuthController {
|
||||||
|
|
||||||
@PostMapping("/send-sms-code")
|
@PostMapping("/send-sms-code")
|
||||||
@ApiOperation(value = "发送手机验证码")
|
@ApiOperation(value = "发送手机验证码")
|
||||||
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志
|
|
||||||
public CommonResult<Boolean> sendSmsCode(@RequestBody @Valid AppAuthSendSmsReqVO reqVO) {
|
public CommonResult<Boolean> sendSmsCode(@RequestBody @Valid AppAuthSendSmsReqVO reqVO) {
|
||||||
authService.sendSmsCode(getLoginUserId(), reqVO);
|
authService.sendSmsCode(getLoginUserId(), reqVO);
|
||||||
return success(true);
|
return success(true);
|
||||||
|
@ -60,7 +57,6 @@ public class AppAuthController {
|
||||||
@PostMapping("/reset-password")
|
@PostMapping("/reset-password")
|
||||||
@ApiOperation(value = "重置密码", notes = "用户忘记密码时使用")
|
@ApiOperation(value = "重置密码", notes = "用户忘记密码时使用")
|
||||||
@PreAuthenticated
|
@PreAuthenticated
|
||||||
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志
|
|
||||||
public CommonResult<Boolean> resetPassword(@RequestBody @Valid AppAuthResetPasswordReqVO reqVO) {
|
public CommonResult<Boolean> resetPassword(@RequestBody @Valid AppAuthResetPasswordReqVO reqVO) {
|
||||||
authService.resetPassword(reqVO);
|
authService.resetPassword(reqVO);
|
||||||
return success(true);
|
return success(true);
|
||||||
|
@ -87,35 +83,18 @@ public class AppAuthController {
|
||||||
return CommonResult.success(authService.getSocialAuthorizeUrl(type, redirectUri));
|
return CommonResult.success(authService.getSocialAuthorizeUrl(type, redirectUri));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/social-login")
|
@PostMapping("/social-quick-login")
|
||||||
@ApiOperation(value = "社交登录,使用 code 授权码", notes = "适合未登录的用户,但是社交账号已绑定用户")
|
@ApiOperation(value = "社交快捷登录,使用 code 授权码", notes = "适合未登录的用户,但是社交账号已绑定用户")
|
||||||
public CommonResult<AppAuthLoginRespVO> socialLogin(@RequestBody @Valid AppAuthSocialLoginReqVO reqVO) {
|
public CommonResult<AppAuthLoginRespVO> socialLogin(@RequestBody @Valid AppAuthSocialQuickLoginReqVO reqVO) {
|
||||||
String token = authService.socialLogin(reqVO, getClientIP(), getUserAgent());
|
String token = authService.socialQuickLogin(reqVO, getClientIP(), getUserAgent());
|
||||||
return success(AppAuthLoginRespVO.builder().token(token).build());
|
return success(AppAuthLoginRespVO.builder().token(token).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/social-login2")
|
@PostMapping("/social-bind-login")
|
||||||
@ApiOperation(value = "社交登录,使用 手机号 + 手机验证码", notes = "适合未登录的用户,进行登录 + 绑定")
|
@ApiOperation(value = "社交绑定登录,使用 手机号 + 手机验证码", notes = "适合未登录的用户,进行登录 + 绑定")
|
||||||
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志
|
public CommonResult<AppAuthLoginRespVO> socialLogin2(@RequestBody @Valid AppAuthSocialBindLoginReqVO reqVO) {
|
||||||
public CommonResult<AppAuthLoginRespVO> socialLogin2(@RequestBody @Valid AppAuthSocialLogin2ReqVO reqVO) {
|
String token = authService.socialBindLogin(reqVO, getClientIP(), getUserAgent());
|
||||||
String token = authService.socialLogin2(reqVO, getClientIP(), getUserAgent());
|
|
||||||
return success(AppAuthLoginRespVO.builder().token(token).build());
|
return success(AppAuthLoginRespVO.builder().token(token).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/social-bind")
|
|
||||||
@ApiOperation(value = "社交绑定,使用 code 授权码", notes = "使用在用户已经登录的情况下")
|
|
||||||
@PreAuthenticated
|
|
||||||
public CommonResult<Boolean> socialBind(@RequestBody @Valid AppAuthSocialBindReqVO reqVO) {
|
|
||||||
authService.socialBind(getLoginUserId(), reqVO);
|
|
||||||
return CommonResult.success(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@DeleteMapping("/social-unbind")
|
|
||||||
@ApiOperation("取消社交绑定")
|
|
||||||
@PreAuthenticated
|
|
||||||
public CommonResult<Boolean> socialUnbind(@RequestBody AppAuthSocialUnbindReqVO reqVO) {
|
|
||||||
authService.unbindSocialUser(getLoginUserId(), reqVO);
|
|
||||||
return CommonResult.success(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,12 @@ import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import javax.validation.constraints.Pattern;
|
import javax.validation.constraints.Pattern;
|
||||||
|
|
||||||
@ApiModel("用户 APP - 社交登录 Request VO,使用 code 授权码 + 账号密码")
|
@ApiModel("用户 APP - 社交绑定登录 Request VO,使用 code 授权码 + 账号密码")
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Builder
|
@Builder
|
||||||
public class AppAuthSocialLogin2ReqVO {
|
public class AppAuthSocialBindLoginReqVO {
|
||||||
|
|
||||||
@ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值")
|
@ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值")
|
||||||
@InEnum(SocialTypeEnum.class)
|
@InEnum(SocialTypeEnum.class)
|
||||||
|
@ -45,5 +45,4 @@ public class AppAuthSocialLogin2ReqVO {
|
||||||
@Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
|
@Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
|
||||||
private String smsCode;
|
private String smsCode;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -12,12 +12,12 @@ import lombok.NoArgsConstructor;
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
@ApiModel("用户 APP - 社交登录 Request VO,使用 code 授权码")
|
@ApiModel("用户 APP - 社交快捷登录 Request VO,使用 code 授权码")
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Builder
|
@Builder
|
||||||
public class AppAuthSocialLoginReqVO {
|
public class AppAuthSocialQuickLoginReqVO {
|
||||||
|
|
||||||
@ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值")
|
@ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值")
|
||||||
@InEnum(SocialTypeEnum.class)
|
@InEnum(SocialTypeEnum.class)
|
|
@ -0,0 +1,42 @@
|
||||||
|
package cn.iocoder.yudao.module.member.controller.app.social;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserBindReqVO;
|
||||||
|
import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO;
|
||||||
|
import cn.iocoder.yudao.module.member.convert.social.SocialUserConvert;
|
||||||
|
import cn.iocoder.yudao.module.system.api.social.SocialUserApi;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||||
|
|
||||||
|
@Api(tags = "用户 App - 社交用户")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/system/social-user")
|
||||||
|
@Validated
|
||||||
|
public class AppSocialUserController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SocialUserApi socialUserApi;
|
||||||
|
|
||||||
|
@PostMapping("/bind")
|
||||||
|
@ApiOperation("社交绑定,使用 code 授权码")
|
||||||
|
public CommonResult<Boolean> socialBind(@RequestBody @Valid AppSocialUserBindReqVO reqVO) {
|
||||||
|
socialUserApi.bindSocialUser(SocialUserConvert.INSTANCE.convert(getLoginUserId(), UserTypeEnum.MEMBER.getValue(), reqVO));
|
||||||
|
return CommonResult.success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/unbind")
|
||||||
|
@ApiOperation("取消社交绑定")
|
||||||
|
public CommonResult<Boolean> socialUnbind(@RequestBody AppSocialUserUnbindReqVO reqVO) {
|
||||||
|
socialUserApi.unbindSocialUser(SocialUserConvert.INSTANCE.convert(getLoginUserId(), UserTypeEnum.MEMBER.getValue(), reqVO));
|
||||||
|
return CommonResult.success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package cn.iocoder.yudao.module.member.controller.app.auth.vo;
|
package cn.iocoder.yudao.module.member.controller.app.social.vo;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||||
|
@ -17,7 +17,7 @@ import javax.validation.constraints.NotNull;
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Builder
|
@Builder
|
||||||
public class AppAuthSocialBindReqVO {
|
public class AppSocialUserBindReqVO {
|
||||||
|
|
||||||
@ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值")
|
@ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值")
|
||||||
@InEnum(SocialTypeEnum.class)
|
@InEnum(SocialTypeEnum.class)
|
|
@ -1,4 +1,4 @@
|
||||||
package cn.iocoder.yudao.module.member.controller.app.auth.vo;
|
package cn.iocoder.yudao.module.member.controller.app.social.vo;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||||
|
@ -12,20 +12,20 @@ import lombok.NoArgsConstructor;
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
@ApiModel("用户 APP - 取消社交绑定 Request VO,使用 code 授权码")
|
@ApiModel("用户 APP - 取消社交绑定 Request VO")
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Builder
|
@Builder
|
||||||
public class AppAuthSocialUnbindReqVO {
|
public class AppSocialUserUnbindReqVO {
|
||||||
|
|
||||||
@ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值")
|
@ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值")
|
||||||
@InEnum(SocialTypeEnum.class)
|
@InEnum(SocialTypeEnum.class)
|
||||||
@NotNull(message = "社交平台的类型不能为空")
|
@NotNull(message = "社交平台的类型不能为空")
|
||||||
private Integer type;
|
private Integer type;
|
||||||
|
|
||||||
@ApiModelProperty(value = "社交的全局编号", required = true, example = "IPRmJ0wvBptiPIlGEZiPewGwiEiE")
|
@ApiModelProperty(value = "社交用户的 openid", required = true, example = "IPRmJ0wvBptiPIlGEZiPewGwiEiE")
|
||||||
@NotEmpty(message = "社交的全局编号不能为空")
|
@NotEmpty(message = "社交用户的 openid 不能为空")
|
||||||
private String unionId;
|
private String openid;
|
||||||
|
|
||||||
}
|
}
|
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.member.convert.auth;
|
||||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||||
import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
|
import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
|
||||||
|
import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO;
|
||||||
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
|
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
|
||||||
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
|
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
|
||||||
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
|
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
|
||||||
|
@ -26,10 +27,9 @@ public interface AuthConvert {
|
||||||
return convert0(bean).setUserType(UserTypeEnum.MEMBER.getValue());
|
return convert0(bean).setUserType(UserTypeEnum.MEMBER.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialBindReqVO reqVO);
|
SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialBindLoginReqVO reqVO);
|
||||||
SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialLogin2ReqVO reqVO);
|
SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialQuickLoginReqVO reqVO);
|
||||||
SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialLoginReqVO reqVO);
|
SocialUserUnbindReqDTO convert(Long userId, Integer userType, AppSocialUserUnbindReqVO reqVO);
|
||||||
SocialUserUnbindReqDTO convert(Long userId, Integer userType, AppAuthSocialUnbindReqVO reqVO);
|
|
||||||
|
|
||||||
SmsCodeSendReqDTO convert(AppAuthSendSmsReqVO reqVO);
|
SmsCodeSendReqDTO convert(AppAuthSendSmsReqVO reqVO);
|
||||||
SmsCodeUseReqDTO convert(AppAuthResetPasswordReqVO reqVO, SmsSceneEnum scene, String usedIp);
|
SmsCodeUseReqDTO convert(AppAuthResetPasswordReqVO reqVO, SmsSceneEnum scene, String usedIp);
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package cn.iocoder.yudao.module.member.convert.social;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserBindReqVO;
|
||||||
|
import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO;
|
||||||
|
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
|
||||||
|
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface SocialUserConvert {
|
||||||
|
|
||||||
|
SocialUserConvert INSTANCE = Mappers.getMapper(SocialUserConvert.class);
|
||||||
|
|
||||||
|
SocialUserBindReqDTO convert(Long userId, Integer userType, AppSocialUserBindReqVO reqVO);
|
||||||
|
|
||||||
|
SocialUserUnbindReqDTO convert(Long userId, Integer userType, AppSocialUserUnbindReqVO reqVO);
|
||||||
|
|
||||||
|
}
|
|
@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.member.service.auth;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService;
|
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService;
|
||||||
import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
|
import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
|
||||||
|
import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserBindReqVO;
|
||||||
|
import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO;
|
||||||
|
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
|
||||||
|
@ -43,7 +45,7 @@ public interface MemberAuthService extends SecurityAuthFrameworkService {
|
||||||
* @param userAgent 用户 UA
|
* @param userAgent 用户 UA
|
||||||
* @return 身份令牌,使用 JWT 方式
|
* @return 身份令牌,使用 JWT 方式
|
||||||
*/
|
*/
|
||||||
String socialLogin(@Valid AppAuthSocialLoginReqVO reqVO, String userIp, String userAgent);
|
String socialQuickLogin(@Valid AppAuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 社交登录,使用 手机号 + 手机验证码
|
* 社交登录,使用 手机号 + 手机验证码
|
||||||
|
@ -53,23 +55,7 @@ public interface MemberAuthService extends SecurityAuthFrameworkService {
|
||||||
* @param userAgent 用户 UA
|
* @param userAgent 用户 UA
|
||||||
* @return 身份令牌,使用 JWT 方式
|
* @return 身份令牌,使用 JWT 方式
|
||||||
*/
|
*/
|
||||||
String socialLogin2(@Valid AppAuthSocialLogin2ReqVO reqVO, String userIp, String userAgent);
|
String socialBindLogin(@Valid AppAuthSocialBindLoginReqVO reqVO, String userIp, String userAgent);
|
||||||
|
|
||||||
/**
|
|
||||||
* 社交绑定,使用 code 授权码
|
|
||||||
*
|
|
||||||
* @param userId 用户编号
|
|
||||||
* @param reqVO 绑定信息
|
|
||||||
*/
|
|
||||||
void socialBind(Long userId, @Valid AppAuthSocialBindReqVO reqVO);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 取消社交绑定
|
|
||||||
*
|
|
||||||
* @param userId 用户编号
|
|
||||||
* @param reqVO 解绑信息
|
|
||||||
*/
|
|
||||||
void unbindSocialUser(Long userId, @Valid AppAuthSocialUnbindReqVO reqVO);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得社交认证 URL
|
* 获得社交认证 URL
|
||||||
|
|
|
@ -8,6 +8,8 @@ import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||||
import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken;
|
import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken;
|
||||||
import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
|
import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
|
||||||
|
import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserBindReqVO;
|
||||||
|
import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO;
|
||||||
import cn.iocoder.yudao.module.member.convert.auth.AuthConvert;
|
import cn.iocoder.yudao.module.member.convert.auth.AuthConvert;
|
||||||
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
|
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
|
||||||
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
|
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
|
||||||
|
@ -108,7 +110,7 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String socialLogin(AppAuthSocialLoginReqVO reqVO, String userIp, String userAgent) {
|
public String socialQuickLogin(AppAuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent) {
|
||||||
// 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
|
// 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
|
||||||
Long userId = socialUserApi.getBindUserId(UserTypeEnum.MEMBER.getValue(), reqVO.getType(),
|
Long userId = socialUserApi.getBindUserId(UserTypeEnum.MEMBER.getValue(), reqVO.getType(),
|
||||||
reqVO.getCode(), reqVO.getState());
|
reqVO.getCode(), reqVO.getState());
|
||||||
|
@ -125,25 +127,19 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
||||||
// 创建 LoginUser 对象
|
// 创建 LoginUser 对象
|
||||||
LoginUser loginUser = AuthConvert.INSTANCE.convert(user);
|
LoginUser loginUser = AuthConvert.INSTANCE.convert(user);
|
||||||
|
|
||||||
// 绑定社交用户(更新)
|
|
||||||
socialUserApi.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO));
|
|
||||||
|
|
||||||
// 缓存登录用户到 Redis 中,返回 sessionId 编号
|
// 缓存登录用户到 Redis 中,返回 sessionId 编号
|
||||||
return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent);
|
return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String socialLogin2(AppAuthSocialLogin2ReqVO reqVO, String userIp, String userAgent) {
|
public String socialBindLogin(AppAuthSocialBindLoginReqVO reqVO, String userIp, String userAgent) {
|
||||||
// 校验社交平台的认证信息是否正确
|
|
||||||
socialUserApi.checkSocialUser(reqVO.getType(), reqVO.getCode(), reqVO.getState());
|
|
||||||
|
|
||||||
// 使用手机号、手机验证码登录
|
// 使用手机号、手机验证码登录
|
||||||
AppAuthSmsLoginReqVO loginReqVO = AppAuthSmsLoginReqVO.builder()
|
AppAuthSmsLoginReqVO loginReqVO = AppAuthSmsLoginReqVO.builder()
|
||||||
.mobile(reqVO.getMobile()).code(reqVO.getSmsCode()).build();
|
.mobile(reqVO.getMobile()).code(reqVO.getSmsCode()).build();
|
||||||
String sessionId = this.smsLogin(loginReqVO, userIp, userAgent);
|
String sessionId = this.smsLogin(loginReqVO, userIp, userAgent);
|
||||||
LoginUser loginUser = userSessionApi.getLoginUser(sessionId);
|
LoginUser loginUser = userSessionApi.getLoginUser(sessionId);
|
||||||
|
|
||||||
// 绑定社交用户(新增)
|
// 绑定社交用户
|
||||||
socialUserApi.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO));
|
socialUserApi.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO));
|
||||||
return sessionId;
|
return sessionId;
|
||||||
}
|
}
|
||||||
|
@ -155,17 +151,6 @@ public class MemberAuthServiceImpl implements MemberAuthService {
|
||||||
return userSessionApi.createUserSession(loginUser, userIp, userAgent);
|
return userSessionApi.createUserSession(loginUser, userIp, userAgent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void socialBind(Long userId, AppAuthSocialBindReqVO reqVO) {
|
|
||||||
// 绑定社交用户(新增)
|
|
||||||
socialUserApi.bindSocialUser(AuthConvert.INSTANCE.convert(userId, getUserType().getValue(), reqVO));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unbindSocialUser(Long userId, AppAuthSocialUnbindReqVO reqVO) {
|
|
||||||
socialUserApi.unbindSocialUser(AuthConvert.INSTANCE.convert(userId, getUserType().getValue(), reqVO));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getSocialAuthorizeUrl(Integer type, String redirectUri) {
|
public String getSocialAuthorizeUrl(Integer type, String redirectUri) {
|
||||||
return socialUserApi.getAuthorizeUrl(type, redirectUri);
|
return socialUserApi.getAuthorizeUrl(type, redirectUri);
|
||||||
|
|
|
@ -37,21 +37,10 @@ public interface SocialUserApi {
|
||||||
*/
|
*/
|
||||||
void unbindSocialUser(@Valid SocialUserUnbindReqDTO reqDTO);
|
void unbindSocialUser(@Valid SocialUserUnbindReqDTO reqDTO);
|
||||||
|
|
||||||
/**
|
|
||||||
* 校验社交用户的认证信息是否正确
|
|
||||||
* 如果校验不通过,则抛出 {@link ServiceException} 业务异常
|
|
||||||
*
|
|
||||||
* @param type 社交平台的类型
|
|
||||||
* @param code 授权码
|
|
||||||
* @param state state
|
|
||||||
*/
|
|
||||||
void checkSocialUser(Integer type, String code, String state);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得社交用户的绑定用户编号
|
* 获得社交用户的绑定用户编号
|
||||||
* 注意,返回的是 MemberUser 或者 AdminUser 的 id 编号!
|
* 注意,返回的是 MemberUser 或者 AdminUser 的 id 编号!
|
||||||
* 该方法会执行和 {@link #checkSocialUser(Integer, String, String)} 一样的逻辑。
|
* 在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常
|
||||||
* 所以在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常
|
|
||||||
*
|
*
|
||||||
* @param userType 用户类型
|
* @param userType 用户类型
|
||||||
* @param type 社交平台的类型
|
* @param type 社交平台的类型
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
package cn.iocoder.yudao.module.system.enums.social;
|
package cn.iocoder.yudao.module.system.enums.social;
|
||||||
|
|
||||||
import cn.hutool.core.collection.ListUtil;
|
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.Collection;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 社交平台的类型枚举
|
* 社交平台的类型枚举
|
||||||
|
@ -53,9 +54,6 @@ public enum SocialTypeEnum implements IntArrayValuable {
|
||||||
|
|
||||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SocialTypeEnum::getType).toArray();
|
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SocialTypeEnum::getType).toArray();
|
||||||
|
|
||||||
public static final List<Integer> WECHAT_ALL = ListUtil.toList(WECHAT_ENTERPRISE.type, WECHAT_MP.type, WECHAT_OPEN.type,
|
|
||||||
WECHAT_MINI_PROGRAM.type);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 类型
|
* 类型
|
||||||
*/
|
*/
|
||||||
|
@ -74,11 +72,4 @@ public enum SocialTypeEnum implements IntArrayValuable {
|
||||||
return ArrayUtil.firstMatch(o -> o.getType().equals(type), values());
|
return ArrayUtil.firstMatch(o -> o.getType().equals(type), values());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Integer> getRelationTypes(Integer type) {
|
|
||||||
if (WECHAT_ALL.contains(type)) {
|
|
||||||
return WECHAT_ALL;
|
|
||||||
}
|
|
||||||
return ListUtil.toList(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,11 +36,6 @@ public class SocialUserApiImpl implements SocialUserApi {
|
||||||
reqDTO.getType(), reqDTO.getUnionId());
|
reqDTO.getType(), reqDTO.getUnionId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void checkSocialUser(Integer type, String code, String state) {
|
|
||||||
socialUserService.checkSocialUser(type, code, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long getBindUserId(Integer userType, Integer type, String code, String state) {
|
public Long getBindUserId(Integer userType, Integer type, String code, String state) {
|
||||||
return socialUserService.getBindUserId(userType, type, code, state);
|
return socialUserService.getBindUserId(userType, type, code, state);
|
||||||
|
|
|
@ -36,7 +36,7 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti
|
||||||
|
|
||||||
@Api(tags = "管理后台 - 认证")
|
@Api(tags = "管理后台 - 认证")
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/system") // 暂时不跟 /auth 结尾
|
@RequestMapping("/system/auth") // 暂时不跟 /auth 结尾
|
||||||
@Validated
|
@Validated
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class AuthController {
|
public class AuthController {
|
||||||
|
@ -80,7 +80,7 @@ public class AuthController {
|
||||||
return success(AuthConvert.INSTANCE.convert(user, roleList, menuList));
|
return success(AuthConvert.INSTANCE.convert(user, roleList, menuList));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("list-menus")
|
@GetMapping("/list-menus")
|
||||||
@ApiOperation("获得登录用户的菜单列表")
|
@ApiOperation("获得登录用户的菜单列表")
|
||||||
public CommonResult<List<AuthMenuRespVO>> getMenus() {
|
public CommonResult<List<AuthMenuRespVO>> getMenus() {
|
||||||
// 获得用户拥有的菜单列表
|
// 获得用户拥有的菜单列表
|
||||||
|
@ -105,36 +105,22 @@ public class AuthController {
|
||||||
return CommonResult.success(socialUserService.getAuthorizeUrl(type, redirectUri));
|
return CommonResult.success(socialUserService.getAuthorizeUrl(type, redirectUri));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/social-login")
|
@PostMapping("/social-quick-login")
|
||||||
@ApiOperation("社交登录,使用 code 授权码")
|
@ApiOperation("社交快捷登录,使用 code 授权码")
|
||||||
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志
|
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志
|
||||||
public CommonResult<AuthLoginRespVO> socialLogin(@RequestBody @Valid AuthSocialLoginReqVO reqVO) {
|
public CommonResult<AuthLoginRespVO> socialQuickLogin(@RequestBody @Valid AuthSocialQuickLoginReqVO reqVO) {
|
||||||
String token = authService.socialLogin(reqVO, getClientIP(), getUserAgent());
|
String token = authService.socialLogin(reqVO, getClientIP(), getUserAgent());
|
||||||
// 返回结果
|
// 返回结果
|
||||||
return success(AuthLoginRespVO.builder().token(token).build());
|
return success(AuthLoginRespVO.builder().token(token).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/social-login2")
|
@PostMapping("/social-bind-login")
|
||||||
@ApiOperation("社交登录,使用 code 授权码 + 账号密码")
|
@ApiOperation("社交绑定登录,使用 code 授权码 + 账号密码")
|
||||||
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志
|
@OperateLog(enable = false) // 避免 Post 请求被记录操作日志
|
||||||
public CommonResult<AuthLoginRespVO> socialLogin2(@RequestBody @Valid AuthSocialLogin2ReqVO reqVO) {
|
public CommonResult<AuthLoginRespVO> socialBindLogin(@RequestBody @Valid AuthSocialBindLoginReqVO reqVO) {
|
||||||
String token = authService.socialLogin2(reqVO, getClientIP(), getUserAgent());
|
String token = authService.socialBindLogin(reqVO, getClientIP(), getUserAgent());
|
||||||
// 返回结果
|
// 返回结果
|
||||||
return success(AuthLoginRespVO.builder().token(token).build());
|
return success(AuthLoginRespVO.builder().token(token).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/social-bind")
|
|
||||||
@ApiOperation("社交绑定,使用 code 授权码")
|
|
||||||
public CommonResult<Boolean> socialBind(@RequestBody @Valid AuthSocialBindReqVO reqVO) {
|
|
||||||
authService.socialBind(getLoginUserId(), reqVO);
|
|
||||||
return CommonResult.success(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@DeleteMapping("/social-unbind")
|
|
||||||
@ApiOperation("取消社交绑定")
|
|
||||||
public CommonResult<Boolean> socialUnbind(@RequestBody AuthSocialUnbindReqVO reqVO) {
|
|
||||||
socialUserService.unbindSocialUser(getLoginUserId(), UserTypeEnum.ADMIN.getValue(), reqVO.getType(), reqVO.getUnionId());
|
|
||||||
return CommonResult.success(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,12 +14,12 @@ import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import javax.validation.constraints.Pattern;
|
import javax.validation.constraints.Pattern;
|
||||||
|
|
||||||
@ApiModel("管理后台 - 社交登录 Request VO,使用 code 授权码 + 账号密码")
|
@ApiModel("管理后台 - 社交绑定登录 Request VO,使用 code 授权码 + 账号密码")
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Builder
|
@Builder
|
||||||
public class AuthSocialLogin2ReqVO {
|
public class AuthSocialBindLoginReqVO {
|
||||||
|
|
||||||
@ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 UserSocialTypeEnum 枚举值")
|
@ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 UserSocialTypeEnum 枚举值")
|
||||||
@InEnum(SocialTypeEnum.class)
|
@InEnum(SocialTypeEnum.class)
|
|
@ -12,12 +12,12 @@ import lombok.NoArgsConstructor;
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
@ApiModel("管理后台 - 社交登录 Request VO,使用 code 授权码")
|
@ApiModel("管理后台 - 社交快捷登录 Request VO,使用 code 授权码")
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Builder
|
@Builder
|
||||||
public class AuthSocialLoginReqVO {
|
public class AuthSocialQuickLoginReqVO {
|
||||||
|
|
||||||
@ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 UserSocialTypeEnum 枚举值")
|
@ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 UserSocialTypeEnum 枚举值")
|
||||||
@InEnum(SocialTypeEnum.class)
|
@InEnum(SocialTypeEnum.class)
|
|
@ -0,0 +1,42 @@
|
||||||
|
package cn.iocoder.yudao.module.system.controller.admin.socail;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.SocialUserBindReqVO;
|
||||||
|
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.SocialUserUnbindReqVO;
|
||||||
|
import cn.iocoder.yudao.module.system.convert.social.SocialUserConvert;
|
||||||
|
import cn.iocoder.yudao.module.system.service.social.SocialUserService;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||||
|
|
||||||
|
@Api(tags = "管理后台 - 社交用户")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/system/social-user")
|
||||||
|
@Validated
|
||||||
|
public class SocialUserController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SocialUserService socialUserService;
|
||||||
|
|
||||||
|
@PostMapping("/bind")
|
||||||
|
@ApiOperation("社交绑定,使用 code 授权码")
|
||||||
|
public CommonResult<Boolean> socialBind(@RequestBody @Valid SocialUserBindReqVO reqVO) {
|
||||||
|
socialUserService.bindSocialUser(SocialUserConvert.INSTANCE.convert(getLoginUserId(), UserTypeEnum.ADMIN.getValue(), reqVO));
|
||||||
|
return CommonResult.success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/unbind")
|
||||||
|
@ApiOperation("取消社交绑定")
|
||||||
|
public CommonResult<Boolean> socialUnbind(@RequestBody SocialUserUnbindReqVO reqVO) {
|
||||||
|
socialUserService.unbindSocialUser(getLoginUserId(), UserTypeEnum.ADMIN.getValue(), reqVO.getType(), reqVO.getOpenid());
|
||||||
|
return CommonResult.success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth;
|
package cn.iocoder.yudao.module.system.controller.admin.socail.vo;
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||||
|
@ -17,7 +17,7 @@ import javax.validation.constraints.NotNull;
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Builder
|
@Builder
|
||||||
public class AuthSocialBindReqVO {
|
public class SocialUserBindReqVO {
|
||||||
|
|
||||||
@ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 UserSocialTypeEnum 枚举值")
|
@ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 UserSocialTypeEnum 枚举值")
|
||||||
@InEnum(SocialTypeEnum.class)
|
@InEnum(SocialTypeEnum.class)
|
|
@ -1,4 +1,4 @@
|
||||||
package cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth;
|
package cn.iocoder.yudao.module.system.controller.admin.socail.vo;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||||
|
@ -12,20 +12,20 @@ import lombok.NoArgsConstructor;
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
@ApiModel("管理后台 - 取消社交绑定 Request VO,使用 code 授权码")
|
@ApiModel("管理后台 - 取消社交绑定 Request VO")
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Builder
|
@Builder
|
||||||
public class AuthSocialUnbindReqVO {
|
public class SocialUserUnbindReqVO {
|
||||||
|
|
||||||
@ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 UserSocialTypeEnum 枚举值")
|
@ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 UserSocialTypeEnum 枚举值")
|
||||||
@InEnum(SocialTypeEnum.class)
|
@InEnum(SocialTypeEnum.class)
|
||||||
@NotNull(message = "社交平台的类型不能为空")
|
@NotNull(message = "社交平台的类型不能为空")
|
||||||
private Integer type;
|
private Integer type;
|
||||||
|
|
||||||
@ApiModelProperty(value = "社交的全局编号", required = true, example = "IPRmJ0wvBptiPIlGEZiPewGwiEiE")
|
@ApiModelProperty(value = "社交用户的 openid", required = true, example = "IPRmJ0wvBptiPIlGEZiPewGwiEiE")
|
||||||
@NotEmpty(message = "社交的全局编号不能为空")
|
@NotEmpty(message = "社交用户的 openid 不能为空")
|
||||||
private String unionId;
|
private String openid;
|
||||||
|
|
||||||
}
|
}
|
|
@ -96,8 +96,8 @@ public class UserProfileRespVO extends UserBaseVO {
|
||||||
@ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SocialTypeEnum 枚举类")
|
@ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SocialTypeEnum 枚举类")
|
||||||
private Integer type;
|
private Integer type;
|
||||||
|
|
||||||
@ApiModelProperty(value = "社交的全局编号", required = true, example = "IPRmJ0wvBptiPIlGEZiPewGwiEiE")
|
@ApiModelProperty(value = "社交用户的 openid", required = true, example = "IPRmJ0wvBptiPIlGEZiPewGwiEiE")
|
||||||
private String unionId;
|
private String openid;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,9 +72,8 @@ public interface AuthConvert {
|
||||||
return CollectionUtils.filterList(treeNodeMap.values(), node -> MenuIdEnum.ROOT.getId().equals(node.getParentId()));
|
return CollectionUtils.filterList(treeNodeMap.values(), node -> MenuIdEnum.ROOT.getId().equals(node.getParentId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
SocialUserBindReqDTO convert(Long userId, Integer userType, AuthSocialBindReqVO reqVO);
|
SocialUserBindReqDTO convert(Long userId, Integer userType, AuthSocialBindLoginReqVO reqVO);
|
||||||
SocialUserBindReqDTO convert(Long userId, Integer userType, AuthSocialLogin2ReqVO reqVO);
|
|
||||||
SocialUserBindReqDTO convert(Long userId, Integer userType, AuthSocialLoginReqVO reqVO);
|
SocialUserBindReqDTO convert(Long userId, Integer userType, AuthSocialQuickLoginReqVO reqVO);
|
||||||
SocialUserUnbindReqDTO convert(Long userId, Integer userType, AuthSocialUnbindReqVO reqVO);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package cn.iocoder.yudao.module.system.convert.social;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
|
||||||
|
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO;
|
||||||
|
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.SocialUserBindReqVO;
|
||||||
|
import cn.iocoder.yudao.module.system.controller.admin.socail.vo.SocialUserUnbindReqVO;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface SocialUserConvert {
|
||||||
|
|
||||||
|
SocialUserConvert INSTANCE = Mappers.getMapper(SocialUserConvert.class);
|
||||||
|
|
||||||
|
SocialUserBindReqDTO convert(Long userId, Integer userType, SocialUserBindReqVO reqVO);
|
||||||
|
|
||||||
|
SocialUserUnbindReqDTO convert(Long userId, Integer userType, SocialUserUnbindReqVO reqVO);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package cn.iocoder.yudao.module.system.dal.dataobject.social;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 社交用户的绑定
|
||||||
|
* 即 {@link SocialUserDO} 与 UserDO 的关联表
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@TableName(value = "system_social_user_bind", autoResultMap = true)
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class SocialUserBindDO extends BaseDO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联的用户编号
|
||||||
|
*
|
||||||
|
* 关联 UserDO 的编号
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
/**
|
||||||
|
* 用户类型
|
||||||
|
*
|
||||||
|
* 枚举 {@link UserTypeEnum}
|
||||||
|
*/
|
||||||
|
private Integer userType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 社交平台的用户编号
|
||||||
|
*
|
||||||
|
* 关联 {@link SocialUserDO#getId()}
|
||||||
|
*/
|
||||||
|
private Long socialUserId;
|
||||||
|
/**
|
||||||
|
* 社交平台的类型
|
||||||
|
*
|
||||||
|
* 冗余 {@link SocialUserDO#getType()}
|
||||||
|
*/
|
||||||
|
private Integer socialType;
|
||||||
|
|
||||||
|
}
|
|
@ -1,15 +1,13 @@
|
||||||
package cn.iocoder.yudao.module.system.dal.dataobject.social;
|
package cn.iocoder.yudao.module.system.dal.dataobject.social;
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
|
||||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||||
|
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 社交用户
|
* 社交用户
|
||||||
* 通过 {@link SocialUserDO#getUserId()} 关联到对应的 {@link AdminUserDO}
|
|
||||||
*
|
*
|
||||||
* @author weir
|
* @author weir
|
||||||
*/
|
*/
|
||||||
|
@ -26,21 +24,10 @@ public class SocialUserDO extends BaseDO {
|
||||||
*/
|
*/
|
||||||
@TableId
|
@TableId
|
||||||
private Long id;
|
private Long id;
|
||||||
/**
|
|
||||||
* 关联的用户编号
|
|
||||||
*/
|
|
||||||
private Long userId;
|
|
||||||
/**
|
|
||||||
* 用户类型
|
|
||||||
*
|
|
||||||
* 枚举 {@link UserTypeEnum}
|
|
||||||
*/
|
|
||||||
private Integer userType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 社交平台的类型
|
* 社交平台的类型
|
||||||
*
|
*
|
||||||
* 枚举 {@link UserTypeEnum}
|
* 枚举 {@link SocialTypeEnum}
|
||||||
*/
|
*/
|
||||||
private Integer type;
|
private Integer type;
|
||||||
|
|
||||||
|
@ -52,13 +39,6 @@ public class SocialUserDO extends BaseDO {
|
||||||
* 社交 token
|
* 社交 token
|
||||||
*/
|
*/
|
||||||
private String token;
|
private String token;
|
||||||
/**
|
|
||||||
* 社交的全局编号
|
|
||||||
*
|
|
||||||
* 例如说,微信平台的 https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/union-id.html
|
|
||||||
* 如果没有 unionId 的平台,直接使用 openid 作为该字段的值
|
|
||||||
*/
|
|
||||||
private String unionId;
|
|
||||||
/**
|
/**
|
||||||
* 原始 Token 数据,一般是 JSON 格式
|
* 原始 Token 数据,一般是 JSON 格式
|
||||||
*/
|
*/
|
||||||
|
@ -77,6 +57,15 @@ public class SocialUserDO extends BaseDO {
|
||||||
*/
|
*/
|
||||||
private String rawUserInfo;
|
private String rawUserInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最后一次的认证 code
|
||||||
|
*/
|
||||||
|
private String code;
|
||||||
|
/**
|
||||||
|
* 最后一次的认证 state
|
||||||
|
*/
|
||||||
|
private String state;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package cn.iocoder.yudao.module.system.dal.mysql.social;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
|
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
|
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface SocialUserBindMapper extends BaseMapperX<SocialUserBindDO> {
|
||||||
|
|
||||||
|
default void deleteByUserTypeAndUserIdAndSocialType(Integer userType, Long userId, Integer socialType) {
|
||||||
|
delete(new LambdaQueryWrapperX<SocialUserBindDO>()
|
||||||
|
.eq(SocialUserBindDO::getUserType, userType)
|
||||||
|
.eq(SocialUserBindDO::getUserId, userId)
|
||||||
|
.eq(SocialUserBindDO::getSocialType, socialType));
|
||||||
|
}
|
||||||
|
|
||||||
|
default void deleteByUserTypeAndSocialUserId(Integer userType, Long socialUserId) {
|
||||||
|
delete(new LambdaQueryWrapperX<SocialUserBindDO>()
|
||||||
|
.eq(SocialUserBindDO::getUserType, userType)
|
||||||
|
.eq(SocialUserBindDO::getSocialUserId, socialUserId));
|
||||||
|
}
|
||||||
|
|
||||||
|
default SocialUserBindDO selectByUserTypeAndSocialUserId(Integer userType, Long socialUserId) {
|
||||||
|
return selectOne(new LambdaQueryWrapperX<SocialUserBindDO>()
|
||||||
|
.eq(SocialUserBindDO::getUserType, userType)
|
||||||
|
.eq(SocialUserBindDO::getSocialUserId, socialUserId));
|
||||||
|
}
|
||||||
|
|
||||||
|
default List<SocialUserBindDO> selectListByUserIdAndUserType(Long userId, Integer userType) {
|
||||||
|
return selectList(new LambdaQueryWrapperX<SocialUserBindDO>()
|
||||||
|
.eq(SocialUserBindDO::getUserId, userId)
|
||||||
|
.eq(SocialUserBindDO::getUserType, userType));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.dal.mysql.social;
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
|
||||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@ -11,18 +12,17 @@ import java.util.List;
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface SocialUserMapper extends BaseMapperX<SocialUserDO> {
|
public interface SocialUserMapper extends BaseMapperX<SocialUserDO> {
|
||||||
|
|
||||||
default List<SocialUserDO> selectListByTypeAndUnionId(Integer userType, Collection<Integer> types, String unionId) {
|
default SocialUserDO selectByTypeAndCodeAnState(Integer type, String code, String state) {
|
||||||
return selectList(new QueryWrapper<SocialUserDO>().eq("user_type", userType)
|
return selectOne(new LambdaQueryWrapper<SocialUserDO>()
|
||||||
.in("type", types).eq("union_id", unionId));
|
.eq(SocialUserDO::getType, type)
|
||||||
|
.eq(SocialUserDO::getCode, code)
|
||||||
|
.eq(SocialUserDO::getState, state));
|
||||||
}
|
}
|
||||||
|
|
||||||
default List<SocialUserDO> selectListByTypeAndUserId(Integer userType, Collection<Integer> types, Long userId) {
|
default SocialUserDO selectByTypeAndOpenid(Integer type, String openid) {
|
||||||
return selectList(new QueryWrapper<SocialUserDO>().eq("user_type", userType)
|
return selectOne(new LambdaQueryWrapper<SocialUserDO>()
|
||||||
.in("type", types).eq("user_id", userId));
|
.eq(SocialUserDO::getType, type)
|
||||||
}
|
.eq(SocialUserDO::getOpenid, openid));
|
||||||
|
|
||||||
default List<SocialUserDO> selectListByUserId(Integer userType, Long userId) {
|
|
||||||
return selectList(new QueryWrapper<SocialUserDO>().eq("user_type", userType).eq("user_id", userId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,10 +23,6 @@ public interface RedisKeyConstants {
|
||||||
"login_user:%s", // 参数为 sessionId
|
"login_user:%s", // 参数为 sessionId
|
||||||
STRING, LoginUser.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC);
|
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", // 注意,它是被 JustAuth 的 justauth.type.prefix 使用到
|
RedisKeyDefine SOCIAL_AUTH_STATE = new RedisKeyDefine("社交登陆的 state", // 注意,它是被 JustAuth 的 justauth.type.prefix 使用到
|
||||||
"social_auth_state:%s", // 参数为 state
|
"social_auth_state:%s", // 参数为 state
|
||||||
STRING, String.class, Duration.ofHours(24)); // 值为 state
|
STRING, String.class, Duration.ofHours(24)); // 值为 state
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
package cn.iocoder.yudao.module.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.module.system.dal.redis.RedisKeyConstants.SOCIAL_AUTH_USER;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 社交 {@link me.zhyd.oauth.model.AuthUser} 的 RedisDAO
|
|
||||||
*
|
|
||||||
* @author 芋道源码
|
|
||||||
*/
|
|
||||||
@Repository
|
|
||||||
public class SocialAuthUserRedisDAO {
|
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -18,14 +18,18 @@ public class SecurityConfiguration {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void customize(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry) {
|
public void customize(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry) {
|
||||||
// 登录的接口,可匿名访问
|
// 登录的接口
|
||||||
registry.antMatchers(buildAdminApi("/system/login")).anonymous();
|
registry.antMatchers(buildAdminApi("/system/auth/login")).permitAll();
|
||||||
|
// 社交登陆的接口
|
||||||
|
registry.antMatchers(buildAdminApi("/system/auth/social-auth-redirect")).permitAll();
|
||||||
|
registry.antMatchers(buildAdminApi("/system/auth/social-quick-login")).permitAll();
|
||||||
|
registry.antMatchers(buildAdminApi("/system/auth/social-bind-login")).permitAll();
|
||||||
// 验证码的接口
|
// 验证码的接口
|
||||||
registry.antMatchers(buildAdminApi("/system/captcha/**")).anonymous();
|
registry.antMatchers(buildAdminApi("/system/captcha/**")).permitAll();
|
||||||
// 获得租户编号的接口
|
// 获得租户编号的接口
|
||||||
registry.antMatchers(buildAdminApi("/system/tenant/get-id-by-name")).anonymous();
|
registry.antMatchers(buildAdminApi("/system/tenant/get-id-by-name")).permitAll();
|
||||||
// 短信回调 API
|
// 短信回调 API
|
||||||
registry.antMatchers(buildAdminApi("/system/sms/callback/**")).anonymous();
|
registry.antMatchers(buildAdminApi("/system/sms/callback/**")).permitAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,31 +25,23 @@ public interface AdminAuthService extends SecurityAuthFrameworkService {
|
||||||
String login(@Valid AuthLoginReqVO reqVO, String userIp, String userAgent);
|
String login(@Valid AuthLoginReqVO reqVO, String userIp, String userAgent);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 社交登录,使用 code 授权码
|
* 社交快捷登录,使用 code 授权码
|
||||||
*
|
*
|
||||||
* @param reqVO 登录信息
|
* @param reqVO 登录信息
|
||||||
* @param userIp 用户 IP
|
* @param userIp 用户 IP
|
||||||
* @param userAgent 用户 UA
|
* @param userAgent 用户 UA
|
||||||
* @return 身份令牌,使用 JWT 方式
|
* @return 身份令牌,使用 JWT 方式
|
||||||
*/
|
*/
|
||||||
String socialLogin(@Valid AuthSocialLoginReqVO reqVO, String userIp, String userAgent);
|
String socialLogin(@Valid AuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 社交登录,使用 code 授权码 + 账号密码
|
* 社交绑定登录,使用 code 授权码 + 账号密码
|
||||||
*
|
*
|
||||||
* @param reqVO 登录信息
|
* @param reqVO 登录信息
|
||||||
* @param userIp 用户 IP
|
* @param userIp 用户 IP
|
||||||
* @param userAgent 用户 UA
|
* @param userAgent 用户 UA
|
||||||
* @return 身份令牌,使用 JWT 方式
|
* @return 身份令牌,使用 JWT 方式
|
||||||
*/
|
*/
|
||||||
String socialLogin2(@Valid AuthSocialLogin2ReqVO reqVO, String userIp, String userAgent);
|
String socialBindLogin(@Valid AuthSocialBindLoginReqVO reqVO, String userIp, String userAgent);
|
||||||
|
|
||||||
/**
|
|
||||||
* 社交绑定,使用 code 授权码
|
|
||||||
*
|
|
||||||
* @param userId 用户编号
|
|
||||||
* @param reqVO 绑定信息
|
|
||||||
*/
|
|
||||||
void socialBind(Long userId, @Valid AuthSocialBindReqVO reqVO);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,8 @@ import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||||
import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken;
|
import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken;
|
||||||
import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
|
import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.AuthLoginReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.AuthLoginReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.AuthSocialBindReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.AuthSocialBindLoginReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.AuthSocialLogin2ReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.AuthSocialQuickLoginReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.AuthSocialLoginReqVO;
|
|
||||||
import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
|
import cn.iocoder.yudao.module.system.convert.auth.AuthConvert;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||||
import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
|
import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum;
|
||||||
|
@ -22,7 +21,6 @@ import cn.iocoder.yudao.module.system.service.permission.PermissionService;
|
||||||
import cn.iocoder.yudao.module.system.service.social.SocialUserService;
|
import cn.iocoder.yudao.module.system.service.social.SocialUserService;
|
||||||
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import me.zhyd.oauth.model.AuthUser;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
@ -82,7 +80,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
||||||
throw new UsernameNotFoundException(username);
|
throw new UsernameNotFoundException(username);
|
||||||
}
|
}
|
||||||
// 创建 LoginUser 对象
|
// 创建 LoginUser 对象
|
||||||
return this.buildLoginUser(user);
|
return buildLoginUser(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -92,19 +90,19 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new UsernameNotFoundException(String.valueOf(userId));
|
throw new UsernameNotFoundException(String.valueOf(userId));
|
||||||
}
|
}
|
||||||
this.createLoginLog(user.getUsername(), LoginLogTypeEnum.LOGIN_MOCK, LoginResultEnum.SUCCESS);
|
createLoginLog(user.getUsername(), LoginLogTypeEnum.LOGIN_MOCK, LoginResultEnum.SUCCESS);
|
||||||
|
|
||||||
// 创建 LoginUser 对象
|
// 创建 LoginUser 对象
|
||||||
return this.buildLoginUser(user);
|
return buildLoginUser(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String login(AuthLoginReqVO reqVO, String userIp, String userAgent) {
|
public String login(AuthLoginReqVO reqVO, String userIp, String userAgent) {
|
||||||
// 判断验证码是否正确
|
// 判断验证码是否正确
|
||||||
this.verifyCaptcha(reqVO);
|
verifyCaptcha(reqVO);
|
||||||
|
|
||||||
// 使用账号密码,进行登录
|
// 使用账号密码,进行登录
|
||||||
LoginUser loginUser = this.login0(reqVO.getUsername(), reqVO.getPassword());
|
LoginUser loginUser = login0(reqVO.getUsername(), reqVO.getPassword());
|
||||||
|
|
||||||
// 缓存登陆用户到 Redis 中,返回 sessionId 编号
|
// 缓存登陆用户到 Redis 中,返回 sessionId 编号
|
||||||
return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent);
|
return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent);
|
||||||
|
@ -192,7 +190,7 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String socialLogin(AuthSocialLoginReqVO reqVO, String userIp, String userAgent) {
|
public String socialLogin(AuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent) {
|
||||||
// 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
|
// 使用 code 授权码,进行登录。然后,获得到绑定的用户编号
|
||||||
Long userId = socialUserService.getBindUserId(UserTypeEnum.ADMIN.getValue(), reqVO.getType(),
|
Long userId = socialUserService.getBindUserId(UserTypeEnum.ADMIN.getValue(), reqVO.getType(),
|
||||||
reqVO.getCode(), reqVO.getState());
|
reqVO.getCode(), reqVO.getState());
|
||||||
|
@ -207,25 +205,18 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建 LoginUser 对象
|
// 创建 LoginUser 对象
|
||||||
LoginUser loginUser = this.buildLoginUser(user);
|
LoginUser loginUser = buildLoginUser(user);
|
||||||
|
|
||||||
// 绑定社交用户(更新)
|
|
||||||
socialUserService.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO));
|
|
||||||
|
|
||||||
// 缓存登录用户到 Redis 中,返回 sessionId 编号
|
// 缓存登录用户到 Redis 中,返回 sessionId 编号
|
||||||
return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent);
|
return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String socialLogin2(AuthSocialLogin2ReqVO reqVO, String userIp, String userAgent) {
|
public String socialBindLogin(AuthSocialBindLoginReqVO reqVO, String userIp, String userAgent) {
|
||||||
// 使用 code 授权码,进行登录
|
|
||||||
AuthUser authUser = socialUserService.getAuthUser(reqVO.getType(), reqVO.getCode(), reqVO.getState());
|
|
||||||
Assert.notNull(authUser, "授权用户不为空");
|
|
||||||
|
|
||||||
// 使用账号密码,进行登录。
|
// 使用账号密码,进行登录。
|
||||||
LoginUser loginUser = this.login0(reqVO.getUsername(), reqVO.getPassword());
|
LoginUser loginUser = login0(reqVO.getUsername(), reqVO.getPassword());
|
||||||
|
|
||||||
// 绑定社交用户(新增)
|
// 绑定社交用户
|
||||||
socialUserService.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO));
|
socialUserService.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO));
|
||||||
|
|
||||||
// 缓存登录用户到 Redis 中,返回 sessionId 编号
|
// 缓存登录用户到 Redis 中,返回 sessionId 编号
|
||||||
|
@ -239,12 +230,6 @@ public class AdminAuthServiceImpl implements AdminAuthService {
|
||||||
return userSessionService.createUserSession(loginUser, userIp, userAgent);
|
return userSessionService.createUserSession(loginUser, userIp, userAgent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void socialBind(Long userId, AuthSocialBindReqVO reqVO) {
|
|
||||||
// 绑定社交用户(新增)
|
|
||||||
socialUserService.bindSocialUser(AuthConvert.INSTANCE.convert(userId, getUserType().getValue(), reqVO));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void logout(String token) {
|
public void logout(String token) {
|
||||||
// 查询用户信息
|
// 查询用户信息
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
package cn.iocoder.yudao.module.system.service.social;
|
package cn.iocoder.yudao.module.system.service.social;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
import cn.iocoder.yudao.framework.common.exception.ServiceException;
|
||||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
|
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
|
||||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||||
import me.zhyd.oauth.model.AuthUser;
|
|
||||||
|
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
@ -28,7 +26,7 @@ public interface SocialUserService {
|
||||||
String getAuthorizeUrl(Integer type, String redirectUri);
|
String getAuthorizeUrl(Integer type, String redirectUri);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得授权的用户
|
* 授权获得对应的社交用户
|
||||||
* 如果授权失败,则会抛出 {@link ServiceException} 异常
|
* 如果授权失败,则会抛出 {@link ServiceException} 异常
|
||||||
*
|
*
|
||||||
* @param type 社交平台的类型 {@link SocialTypeEnum}
|
* @param type 社交平台的类型 {@link SocialTypeEnum}
|
||||||
|
@ -37,17 +35,7 @@ public interface SocialUserService {
|
||||||
* @return 授权用户
|
* @return 授权用户
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
AuthUser getAuthUser(Integer type, String code, String state);
|
SocialUserDO authSocialUser(Integer type, String code, String state);
|
||||||
|
|
||||||
/**
|
|
||||||
* 获得社交用户的 unionId 编号
|
|
||||||
*
|
|
||||||
* @param authUser 社交用户
|
|
||||||
* @return unionId 编号
|
|
||||||
*/
|
|
||||||
default String getAuthUserUnionId(AuthUser authUser) {
|
|
||||||
return StrUtil.blankToDefault(authUser.getToken().getUnionId(), authUser.getUuid());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得指定用户的社交用户列表
|
* 获得指定用户的社交用户列表
|
||||||
|
@ -71,25 +59,14 @@ public interface SocialUserService {
|
||||||
* @param userId 用户编号
|
* @param userId 用户编号
|
||||||
* @param userType 全局用户类型
|
* @param userType 全局用户类型
|
||||||
* @param type 社交平台的类型 {@link SocialTypeEnum}
|
* @param type 社交平台的类型 {@link SocialTypeEnum}
|
||||||
* @param unionId 社交平台的 unionId
|
* @param openid 社交平台的 openid
|
||||||
*/
|
*/
|
||||||
void unbindSocialUser(Long userId, Integer userType, Integer type, String unionId);
|
void unbindSocialUser(Long userId, Integer userType, Integer type, String openid);
|
||||||
|
|
||||||
/**
|
|
||||||
* 校验社交用户的认证信息是否正确
|
|
||||||
* 如果校验不通过,则抛出 {@link ServiceException} 业务异常
|
|
||||||
*
|
|
||||||
* @param type 社交平台的类型
|
|
||||||
* @param code 授权码
|
|
||||||
* @param state state
|
|
||||||
*/
|
|
||||||
void checkSocialUser(Integer type, String code, String state);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获得社交用户的绑定用户编号
|
* 获得社交用户的绑定用户编号
|
||||||
* 注意,返回的是 MemberUser 或者 AdminUser 的 id 编号!
|
* 注意,返回的是 MemberUser 或者 AdminUser 的 id 编号!
|
||||||
* 该方法会执行和 {@link #checkSocialUser(Integer, String, String)} 一样的逻辑。
|
* 在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常
|
||||||
* 所以在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常
|
|
||||||
*
|
*
|
||||||
* @param userType 用户类型
|
* @param userType 用户类型
|
||||||
* @param type 社交平台的类型
|
* @param type 社交平台的类型
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
package cn.iocoder.yudao.module.system.service.social;
|
package cn.iocoder.yudao.module.system.service.social;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
|
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
|
||||||
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
|
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
|
||||||
|
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
|
||||||
|
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper;
|
||||||
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper;
|
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper;
|
||||||
import cn.iocoder.yudao.module.system.dal.redis.social.SocialAuthUserRedisDAO;
|
|
||||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
import com.xkcoding.justauth.AuthRequestFactory;
|
import com.xkcoding.justauth.AuthRequestFactory;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import me.zhyd.oauth.model.AuthCallback;
|
import me.zhyd.oauth.model.AuthCallback;
|
||||||
|
@ -21,10 +21,11 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
|
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||||
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
|
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
|
||||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
|
||||||
|
|
||||||
|
@ -42,8 +43,7 @@ public class SocialUserServiceImpl implements SocialUserService {
|
||||||
private AuthRequestFactory authRequestFactory;
|
private AuthRequestFactory authRequestFactory;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private SocialAuthUserRedisDAO authSocialUserRedisDAO;
|
private SocialUserBindMapper socialUserBindMapper;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private SocialUserMapper socialUserMapper;
|
private SocialUserMapper socialUserMapper;
|
||||||
|
|
||||||
|
@ -57,171 +57,111 @@ public class SocialUserServiceImpl implements SocialUserService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthUser getAuthUser(Integer type, String code, String state) {
|
public SocialUserDO authSocialUser(Integer type, String code, String state) {
|
||||||
AuthCallback authCallback = buildAuthCallback(code, state);
|
// 优先从 DB 中获取,因为 code 有且可以使用一次。
|
||||||
// 从缓存中获取
|
// 在社交登录时,当未绑定 User 时,需要绑定登录,此时需要 code 使用两次
|
||||||
AuthUser authUser = authSocialUserRedisDAO.get(type, authCallback);
|
SocialUserDO socialUser = socialUserMapper.selectByTypeAndCodeAnState(type, code, state);
|
||||||
if (authUser != null) {
|
if (socialUser != null) {
|
||||||
return authUser;
|
return socialUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 请求获取
|
// 请求获取
|
||||||
authUser = this.getAuthUser0(type, authCallback);
|
AuthUser authUser = getAuthUser(type, code, state);
|
||||||
// 缓存。原因是 code 有且可以使用一次。在社交登录时,当未绑定 User 时,需要绑定登录,此时需要 code 使用两次
|
Assert.notNull(authUser, "三方用户不能为空");
|
||||||
authSocialUserRedisDAO.set(type, authCallback, authUser);
|
|
||||||
return authUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// 保存到 DB 中
|
||||||
* 获得 unionId 对应的某个社交平台的“所有”社交用户
|
socialUser = socialUserMapper.selectByTypeAndOpenid(type, authUser.getUuid());
|
||||||
* 注意,这里的“所有”,指的是类似【微信】平台,包括了小程序、公众号、PC 网站,他们的 unionId 是一致的
|
if (socialUser == null) {
|
||||||
*
|
socialUser = new SocialUserDO();
|
||||||
* @param type 社交平台的类型 {@link SocialTypeEnum}
|
}
|
||||||
* @param unionId 社交平台的 unionId
|
socialUser.setType(type).setCode(code).setState(state) // 需要保存 code + state 字段,保证后续可查询
|
||||||
* @param userType 全局用户类型
|
.setOpenid(authUser.getUuid()).setToken(authUser.getToken().getAccessToken()).setRawTokenInfo((toJsonString(authUser.getToken())))
|
||||||
* @return 社交用户列表
|
.setNickname(authUser.getNickname()).setAvatar(authUser.getAvatar()).setRawUserInfo(toJsonString(authUser.getRawUserInfo()));
|
||||||
*/
|
if (socialUser.getId() == null) {
|
||||||
private List<SocialUserDO> getAllSocialUserList(Integer type, String unionId, Integer userType) {
|
socialUserMapper.insert(socialUser);
|
||||||
List<Integer> types = SocialTypeEnum.getRelationTypes(type);
|
} else {
|
||||||
return socialUserMapper.selectListByTypeAndUnionId(userType, types, unionId);
|
socialUserMapper.updateById(socialUser);
|
||||||
|
}
|
||||||
|
return socialUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SocialUserDO> getSocialUserList(Long userId, Integer userType) {
|
public List<SocialUserDO> getSocialUserList(Long userId, Integer userType) {
|
||||||
return socialUserMapper.selectListByUserId(userType, userId);
|
// 获得绑定
|
||||||
|
List<SocialUserBindDO> socialUserBinds = socialUserBindMapper.selectListByUserIdAndUserType(userId, userType);
|
||||||
|
if (CollUtil.isEmpty(socialUserBinds)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
// 获得社交用户
|
||||||
|
return socialUserMapper.selectBatchIds(convertSet(socialUserBinds, SocialUserBindDO::getSocialUserId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional
|
||||||
public void bindSocialUser(SocialUserBindReqDTO reqDTO) {
|
public void bindSocialUser(SocialUserBindReqDTO reqDTO) {
|
||||||
// 使用 code 授权
|
// 获得社交用户
|
||||||
AuthUser authUser = getAuthUser(reqDTO.getType(), reqDTO.getCode(),
|
SocialUserDO socialUser = authSocialUser(reqDTO.getType(), reqDTO.getCode(), reqDTO.getState());
|
||||||
reqDTO.getState());
|
Assert.notNull(socialUser, "社交用户不能为空");
|
||||||
if (authUser == null) {
|
|
||||||
throw exception(SOCIAL_USER_NOT_FOUND);
|
// 社交用户可能之前绑定过别的用户,需要进行解绑
|
||||||
|
socialUserBindMapper.deleteByUserTypeAndSocialUserId(reqDTO.getUserType(), socialUser.getId());
|
||||||
|
|
||||||
|
// 用户可能之前已经绑定过该社交类型,需要进行解绑
|
||||||
|
socialUserBindMapper.deleteByUserTypeAndUserIdAndSocialType(reqDTO.getUserType(), reqDTO.getUserId(),
|
||||||
|
socialUser.getType());
|
||||||
|
|
||||||
|
// 绑定当前登录的社交用户
|
||||||
|
SocialUserBindDO socialUserBind = SocialUserBindDO.builder()
|
||||||
|
.userId(reqDTO.getUserId()).userType(reqDTO.getUserType())
|
||||||
|
.socialUserId(socialUser.getId()).socialType(socialUser.getType()).build();
|
||||||
|
socialUserBindMapper.insert(socialUserBind);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 绑定社交用户(新增)
|
@Override
|
||||||
bindSocialUser(reqDTO.getUserId(), reqDTO.getUserType(),
|
public void unbindSocialUser(Long userId, Integer userType, Integer type, String openid) {
|
||||||
reqDTO.getType(), authUser);
|
// 获得 openid 对应的 SocialUserDO 社交用户
|
||||||
}
|
SocialUserDO socialUser = socialUserMapper.selectByTypeAndOpenid(type, openid);
|
||||||
|
|
||||||
/**
|
|
||||||
* 绑定社交用户
|
|
||||||
* @param userId 用户编号
|
|
||||||
* @param userType 用户类型
|
|
||||||
* @param type 社交平台的类型 {@link SocialTypeEnum}
|
|
||||||
* @param authUser 授权用户
|
|
||||||
*/
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
|
||||||
protected void bindSocialUser(Long userId, Integer userType, Integer type, AuthUser authUser) {
|
|
||||||
// 获得 unionId 对应的 SocialUserDO 列表
|
|
||||||
String unionId = getAuthUserUnionId(authUser);
|
|
||||||
List<SocialUserDO> socialUsers = this.getAllSocialUserList(type, unionId, userType);
|
|
||||||
|
|
||||||
// 逻辑一:如果 userId 之前绑定过该 type 的其它账号,需要进行解绑
|
|
||||||
this.unbindOldSocialUser(userId, userType, type, unionId);
|
|
||||||
|
|
||||||
// 逻辑二:如果 socialUsers 指定的 userId 改变,需要进行更新
|
|
||||||
// 例如说,一个微信 unionId 对应了多个社交账号,结果其中有个关联了新的 userId,则其它也要跟着修改
|
|
||||||
// 考虑到 socialUsers 一般比较少,直接 for 循环更新即可
|
|
||||||
socialUsers.forEach(socialUser -> {
|
|
||||||
if (Objects.equals(socialUser.getUserId(), userId)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
socialUserMapper.updateById(new SocialUserDO().setId(socialUser.getId()).setUserId(userId));
|
|
||||||
});
|
|
||||||
|
|
||||||
// 逻辑三:如果 authUser 不存在于 socialUsers 中,则进行新增;否则,进行更新
|
|
||||||
SocialUserDO socialUser = CollUtil.findOneByField(socialUsers, "openid", authUser.getUuid());
|
|
||||||
SocialUserDO saveSocialUser = SocialUserDO.builder() // 新增和更新的通用属性
|
|
||||||
.token(authUser.getToken().getAccessToken()).rawTokenInfo(toJsonString(authUser.getToken()))
|
|
||||||
.nickname(authUser.getNickname()).avatar(authUser.getAvatar()).rawUserInfo(toJsonString(authUser.getRawUserInfo()))
|
|
||||||
.build();
|
|
||||||
if (socialUser == null) {
|
if (socialUser == null) {
|
||||||
saveSocialUser.setUserId(userId).setUserType(userType)
|
|
||||||
.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 userType, Integer type, String unionId) {
|
|
||||||
// 获得 unionId 对应的所有 SocialUserDO 社交用户
|
|
||||||
List<SocialUserDO> socialUsers = this.getAllSocialUserList(type, unionId, userType);
|
|
||||||
if (CollUtil.isEmpty(socialUsers)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 校验,是否解绑的是非自己的
|
|
||||||
socialUsers.forEach(socialUser -> {
|
|
||||||
if (!Objects.equals(socialUser.getUserId(), userId)) {
|
|
||||||
throw exception(SOCIAL_USER_UNBIND_NOT_SELF);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 解绑
|
|
||||||
socialUserMapper.deleteBatchIds(CollectionUtils.convertSet(socialUsers, SocialUserDO::getId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void checkSocialUser(Integer type, String code, String state) {
|
|
||||||
AuthUser authUser = getAuthUser(type, code, state);
|
|
||||||
if (authUser == null) {
|
|
||||||
throw exception(SOCIAL_USER_NOT_FOUND);
|
throw exception(SOCIAL_USER_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获得对应的社交绑定关系
|
||||||
|
socialUserBindMapper.deleteByUserTypeAndUserIdAndSocialType(userType, userId, socialUser.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long getBindUserId(Integer userType, Integer type, String code, String state) {
|
public Long getBindUserId(Integer userType, Integer type, String code, String state) {
|
||||||
AuthUser authUser = getAuthUser(type, code, state);
|
// 获得社交用户
|
||||||
if (authUser == null) {
|
SocialUserDO socialUser = authSocialUser(type, code, state);
|
||||||
throw exception(SOCIAL_USER_NOT_FOUND);
|
Assert.notNull(socialUser, "社交用户不能为空");
|
||||||
}
|
|
||||||
|
|
||||||
// 如果未绑定 SocialUserDO 用户,则无法自动登录,进行报错
|
// 如果未绑定的社交用户,则无法自动登录,进行报错
|
||||||
String unionId = getAuthUserUnionId(authUser);
|
SocialUserBindDO socialUserBind = socialUserBindMapper.selectByUserTypeAndSocialUserId(userType,
|
||||||
List<SocialUserDO> socialUsers = getAllSocialUserList(type, unionId, userType);
|
socialUser.getId());
|
||||||
if (CollUtil.isEmpty(socialUsers)) {
|
if (socialUserBind == null) {
|
||||||
throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
|
throw exception(AUTH_THIRD_LOGIN_NOT_BIND);
|
||||||
}
|
}
|
||||||
return socialUsers.get(0).getUserId();
|
return socialUserBind.getUserId();
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public void unbindOldSocialUser(Long userId, Integer userType, Integer type, String newUnionId) {
|
|
||||||
List<Integer> types = SocialTypeEnum.getRelationTypes(type);
|
|
||||||
List<SocialUserDO> oldSocialUsers = socialUserMapper.selectListByTypeAndUserId(userType, types, userId);
|
|
||||||
// 如果新老的 unionId 是一致的,说明无需解绑
|
|
||||||
if (CollUtil.isEmpty(oldSocialUsers) || Objects.equals(newUnionId, oldSocialUsers.get(0).getUnionId())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解绑
|
|
||||||
socialUserMapper.deleteBatchIds(CollectionUtils.convertSet(oldSocialUsers, SocialUserDO::getId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 请求社交平台,获得授权的用户
|
* 请求社交平台,获得授权的用户
|
||||||
*
|
*
|
||||||
* @param type 社交平台的类型
|
* @param type 社交平台的类型
|
||||||
* @param authCallback 授权回调
|
* @param code 授权码
|
||||||
|
* @param state 授权 state
|
||||||
* @return 授权的用户
|
* @return 授权的用户
|
||||||
*/
|
*/
|
||||||
private AuthUser getAuthUser0(Integer type, AuthCallback authCallback) {
|
private AuthUser getAuthUser(Integer type, String code, String state) {
|
||||||
AuthRequest authRequest = authRequestFactory.get(SocialTypeEnum.valueOfType(type).getSource());
|
AuthRequest authRequest = authRequestFactory.get(SocialTypeEnum.valueOfType(type).getSource());
|
||||||
|
AuthCallback authCallback = AuthCallback.builder().code(code).state(state).build();
|
||||||
AuthResponse<?> authResponse = authRequest.login(authCallback);
|
AuthResponse<?> authResponse = authRequest.login(authCallback);
|
||||||
log.info("[getAuthUser0][请求社交平台 type({}) request({}) response({})]", type, toJsonString(authCallback),
|
log.info("[getAuthUser][请求社交平台 type({}) request({}) response({})]", type,
|
||||||
toJsonString(authResponse));
|
toJsonString(authCallback), toJsonString(authResponse));
|
||||||
if (!authResponse.ok()) {
|
if (!authResponse.ok()) {
|
||||||
throw exception(SOCIAL_USER_AUTH_FAILURE, authResponse.getMsg());
|
throw exception(SOCIAL_USER_AUTH_FAILURE, authResponse.getMsg());
|
||||||
}
|
}
|
||||||
return (AuthUser) authResponse.getData();
|
return (AuthUser) authResponse.getData();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AuthCallback buildAuthCallback(String code, String state) {
|
|
||||||
return AuthCallback.builder().code(code).state(state).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,165 +1,256 @@
|
||||||
package cn.iocoder.yudao.module.system.service.social;
|
package cn.iocoder.yudao.module.system.service.social;
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
|
|
||||||
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper;
|
|
||||||
import cn.iocoder.yudao.module.system.dal.redis.social.SocialAuthUserRedisDAO;
|
|
||||||
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
|
||||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest;
|
import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest;
|
||||||
|
import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO;
|
||||||
|
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO;
|
||||||
|
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO;
|
||||||
|
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper;
|
||||||
|
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper;
|
||||||
|
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
|
||||||
import com.xkcoding.justauth.AuthRequestFactory;
|
import com.xkcoding.justauth.AuthRequestFactory;
|
||||||
|
import me.zhyd.oauth.enums.AuthResponseStatus;
|
||||||
|
import me.zhyd.oauth.model.AuthCallback;
|
||||||
|
import me.zhyd.oauth.model.AuthResponse;
|
||||||
import me.zhyd.oauth.model.AuthUser;
|
import me.zhyd.oauth.model.AuthUser;
|
||||||
|
import me.zhyd.oauth.request.AuthRequest;
|
||||||
|
import me.zhyd.oauth.utils.AuthStateUtils;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static cn.hutool.core.util.RandomUtil.randomEle;
|
import static cn.hutool.core.util.RandomUtil.randomLong;
|
||||||
import static cn.hutool.core.util.RandomUtil.randomString;
|
import static cn.hutool.core.util.RandomUtil.randomString;
|
||||||
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
|
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
|
||||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
|
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
|
||||||
|
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
|
||||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||||
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_USER_AUTH_FAILURE;
|
||||||
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_USER_NOT_FOUND;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
@Import({SocialUserServiceImpl.class, SocialAuthUserRedisDAO.class})
|
@Import(SocialUserServiceImpl.class)
|
||||||
public class SocialUserServiceTest extends BaseDbAndRedisUnitTest {
|
public class SocialUserServiceTest extends BaseDbAndRedisUnitTest {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private SocialUserServiceImpl socialService;
|
private SocialUserServiceImpl socialUserService;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private SocialUserMapper socialUserMapper;
|
private SocialUserMapper socialUserMapper;
|
||||||
|
@Resource
|
||||||
|
private SocialUserBindMapper socialUserBindMapper;
|
||||||
|
|
||||||
@MockBean
|
@MockBean
|
||||||
private AuthRequestFactory authRequestFactory;
|
private AuthRequestFactory authRequestFactory;
|
||||||
|
|
||||||
/**
|
|
||||||
* 情况一,创建 SocialUserDO 的情况
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testBindSocialUser_create() {
|
public void testGetAuthorizeUrl() {
|
||||||
// mock 数据
|
try (MockedStatic<AuthStateUtils> authStateUtilsMock = mockStatic(AuthStateUtils.class)) {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
Long userId = randomLongId();
|
Integer type = SocialTypeEnum.WECHAT_MP.getType();
|
||||||
Integer type = randomEle(SocialTypeEnum.values()).getType();
|
String redirectUri = "sss";
|
||||||
AuthUser authUser = randomPojo(AuthUser.class);
|
// mock 获得对应的 AuthRequest 实现
|
||||||
|
AuthRequest authRequest = mock(AuthRequest.class);
|
||||||
|
when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest);
|
||||||
// mock 方法
|
// mock 方法
|
||||||
|
authStateUtilsMock.when(AuthStateUtils::createState).thenReturn("aoteman");
|
||||||
|
when(authRequest.authorize(eq("aoteman"))).thenReturn("https://www.iocoder.cn?redirect_uri=yyy");
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
socialService.bindSocialUser(userId, UserTypeEnum.ADMIN.getValue(), type, authUser);
|
String url = socialUserService.getAuthorizeUrl(type, redirectUri);
|
||||||
// 断言
|
// 断言
|
||||||
List<SocialUserDO> socialUsers = socialUserMapper.selectList("user_id", userId);
|
assertEquals("https://www.iocoder.cn/?redirect_uri=sss", url);
|
||||||
assertEquals(1, socialUsers.size());
|
}
|
||||||
assertBindSocialUser(socialUsers.get(0), authUser, userId, type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 情况二,更新 SocialUserDO 的情况
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testBindSocialUser_update() {
|
public void testAuthSocialUser_exists() {
|
||||||
// mock 数据
|
|
||||||
SocialUserDO dbSocialUser = randomPojo(SocialUserDO.class, socialUserDO -> {
|
|
||||||
socialUserDO.setUserType(UserTypeEnum.ADMIN.getValue());
|
|
||||||
socialUserDO.setType(randomEle(SocialTypeEnum.values()).getType());
|
|
||||||
});
|
|
||||||
socialUserMapper.insert(dbSocialUser);
|
|
||||||
// 准备参数
|
// 准备参数
|
||||||
Long userId = dbSocialUser.getUserId();
|
Integer type = SocialTypeEnum.GITEE.getType();
|
||||||
Integer type = dbSocialUser.getType();
|
String code = "tudou";
|
||||||
AuthUser authUser = randomPojo(AuthUser.class);
|
String state = "yuanma";
|
||||||
// mock 方法
|
// mock 方法
|
||||||
|
SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(type).setCode(code).setState(state);
|
||||||
|
socialUserMapper.insert(socialUser);
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
socialService.bindSocialUser(userId, UserTypeEnum.ADMIN.getValue(), type, authUser);
|
SocialUserDO result = socialUserService.authSocialUser(type, code, state);
|
||||||
// 断言
|
// 断言
|
||||||
List<SocialUserDO> socialUsers = socialUserMapper.selectList("user_id", userId);
|
assertPojoEquals(socialUser, result);
|
||||||
assertEquals(1, socialUsers.size());
|
|
||||||
assertBindSocialUser(socialUsers.get(0), authUser, userId, type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 情况一和二都存在的,逻辑二的场景
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testBindSocialUser_userId() {
|
public void testAuthSocialUser_authFailure() {
|
||||||
// mock 数据
|
|
||||||
SocialUserDO dbSocialUser = randomPojo(SocialUserDO.class, socialUserDO -> {
|
|
||||||
socialUserDO.setUserType(UserTypeEnum.ADMIN.getValue());
|
|
||||||
socialUserDO.setType(randomEle(SocialTypeEnum.values()).getType());
|
|
||||||
});
|
|
||||||
socialUserMapper.insert(dbSocialUser);
|
|
||||||
// 准备参数
|
// 准备参数
|
||||||
Long userId = randomLongId();
|
Integer type = SocialTypeEnum.GITEE.getType();
|
||||||
Integer type = dbSocialUser.getType();
|
|
||||||
AuthUser authUser = randomPojo(AuthUser.class);
|
|
||||||
// mock 方法
|
// mock 方法
|
||||||
|
AuthRequest authRequest = mock(AuthRequest.class);
|
||||||
|
when(authRequestFactory.get(anyString())).thenReturn(authRequest);
|
||||||
|
AuthResponse<?> authResponse = new AuthResponse<>(0, "模拟失败", null);
|
||||||
|
when(authRequest.login(any(AuthCallback.class))).thenReturn(authResponse);
|
||||||
|
|
||||||
|
// 调用并断言
|
||||||
|
assertServiceException(
|
||||||
|
() -> socialUserService.authSocialUser(type, randomString(10), randomString(10)),
|
||||||
|
SOCIAL_USER_AUTH_FAILURE, "模拟失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAuthSocialUser_insert() {
|
||||||
|
// 准备参数
|
||||||
|
Integer type = SocialTypeEnum.GITEE.getType();
|
||||||
|
String code = "tudou";
|
||||||
|
String state = "yuanma";
|
||||||
|
// mock 方法
|
||||||
|
AuthRequest authRequest = mock(AuthRequest.class);
|
||||||
|
when(authRequestFactory.get(eq(SocialTypeEnum.GITEE.getSource()))).thenReturn(authRequest);
|
||||||
|
AuthUser authUser = randomPojo(AuthUser.class);
|
||||||
|
AuthResponse<AuthUser> authResponse = new AuthResponse<>(AuthResponseStatus.SUCCESS.getCode(), null, authUser);
|
||||||
|
when(authRequest.login(any(AuthCallback.class))).thenReturn(authResponse);
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
socialService.bindSocialUser(userId, UserTypeEnum.ADMIN.getValue(), type, authUser);
|
SocialUserDO result = socialUserService.authSocialUser(type, code, state);
|
||||||
// 断言
|
// 断言
|
||||||
List<SocialUserDO> socialUsers = socialUserMapper.selectList("user_id", userId);
|
assertBindSocialUser(type, result, authResponse.getData());
|
||||||
assertEquals(1, socialUsers.size());
|
assertEquals(code, result.getCode());
|
||||||
|
assertEquals(state, result.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertBindSocialUser(SocialUserDO socialUser, AuthUser authUser, Long userId,
|
@Test
|
||||||
Integer type) {
|
public void testAuthSocialUser_update() {
|
||||||
|
// 准备参数
|
||||||
|
Integer type = SocialTypeEnum.GITEE.getType();
|
||||||
|
String code = "tudou";
|
||||||
|
String state = "yuanma";
|
||||||
|
// mock 数据
|
||||||
|
socialUserMapper.insert(randomPojo(SocialUserDO.class).setType(type).setOpenid("test_openid"));
|
||||||
|
// mock 方法
|
||||||
|
AuthRequest authRequest = mock(AuthRequest.class);
|
||||||
|
when(authRequestFactory.get(eq(SocialTypeEnum.GITEE.getSource()))).thenReturn(authRequest);
|
||||||
|
AuthUser authUser = randomPojo(AuthUser.class);
|
||||||
|
authUser.getToken().setOpenId("test_openid");
|
||||||
|
AuthResponse<AuthUser> authResponse = new AuthResponse<>(AuthResponseStatus.SUCCESS.getCode(), null, authUser);
|
||||||
|
when(authRequest.login(any(AuthCallback.class))).thenReturn(authResponse);
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
SocialUserDO result = socialUserService.authSocialUser(type, code, state);
|
||||||
|
// 断言
|
||||||
|
assertBindSocialUser(type, result, authResponse.getData());
|
||||||
|
assertEquals(code, result.getCode());
|
||||||
|
assertEquals(state, result.getState());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertBindSocialUser(Integer type, SocialUserDO socialUser, AuthUser authUser) {
|
||||||
assertEquals(authUser.getToken().getAccessToken(), socialUser.getToken());
|
assertEquals(authUser.getToken().getAccessToken(), socialUser.getToken());
|
||||||
assertEquals(toJsonString(authUser.getToken()), socialUser.getRawTokenInfo());
|
assertEquals(toJsonString(authUser.getToken()), socialUser.getRawTokenInfo());
|
||||||
assertEquals(authUser.getNickname(), socialUser.getNickname());
|
assertEquals(authUser.getNickname(), socialUser.getNickname());
|
||||||
assertEquals(authUser.getAvatar(), socialUser.getAvatar());
|
assertEquals(authUser.getAvatar(), socialUser.getAvatar());
|
||||||
assertEquals(toJsonString(authUser.getRawUserInfo()), socialUser.getRawUserInfo());
|
assertEquals(toJsonString(authUser.getRawUserInfo()), socialUser.getRawUserInfo());
|
||||||
assertEquals(userId, socialUser.getUserId());
|
|
||||||
assertEquals(UserTypeEnum.ADMIN.getValue(), socialUser.getUserType());
|
|
||||||
assertEquals(type, socialUser.getType());
|
assertEquals(type, socialUser.getType());
|
||||||
assertEquals(authUser.getUuid(), socialUser.getOpenid());
|
assertEquals(authUser.getUuid(), socialUser.getOpenid());
|
||||||
assertEquals(socialService.getAuthUserUnionId(authUser), socialUser.getUnionId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 情况一,如果新老的 unionId 是一致的,无需解绑
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testUnbindOldSocialUser_no() {
|
public void testGetSocialUserList() {
|
||||||
// mock 数据
|
Long userId = 1L;
|
||||||
SocialUserDO oldSocialUser = randomPojo(SocialUserDO.class, socialUserDO -> {
|
Integer userType = UserTypeEnum.ADMIN.getValue();
|
||||||
socialUserDO.setUserType(UserTypeEnum.ADMIN.getValue());
|
// mock 获得社交用户
|
||||||
socialUserDO.setType(randomEle(SocialTypeEnum.values()).getType());
|
SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(SocialTypeEnum.GITEE.getType());
|
||||||
});
|
socialUserMapper.insert(socialUser); // 可被查到
|
||||||
socialUserMapper.insert(oldSocialUser);
|
socialUserMapper.insert(randomPojo(SocialUserDO.class)); // 不可被查到
|
||||||
// 准备参数
|
// mock 获得绑定
|
||||||
Long userId = oldSocialUser.getUserId();
|
socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class) // 可被查询到
|
||||||
Integer type = oldSocialUser.getType();
|
.setUserId(userId).setUserType(userType).setSocialType(SocialTypeEnum.GITEE.getType())
|
||||||
String newUnionId = oldSocialUser.getUnionId();
|
.setSocialUserId(socialUser.getId()));
|
||||||
|
socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class) // 不可被查询到
|
||||||
|
.setUserId(2L).setUserType(userType).setSocialType(SocialTypeEnum.DINGTALK.getType()));
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
socialService.unbindOldSocialUser(userId, UserTypeEnum.ADMIN.getValue(), type, newUnionId);
|
List<SocialUserDO> result = socialUserService.getSocialUserList(userId, userType);
|
||||||
// 断言
|
// 断言
|
||||||
assertEquals(1L, socialUserMapper.selectCount(null).longValue());
|
assertEquals(1, result.size());
|
||||||
|
assertPojoEquals(socialUser, result.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 情况二,如果新老的 unionId 不一致的,需解绑
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testUnbindOldSocialUser_yes() {
|
public void testBindSocialUser() {
|
||||||
// mock 数据
|
|
||||||
SocialUserDO oldSocialUser = randomPojo(SocialUserDO.class, socialUserDO -> {
|
|
||||||
socialUserDO.setUserType(UserTypeEnum.ADMIN.getValue());
|
|
||||||
socialUserDO.setType(randomEle(SocialTypeEnum.values()).getType());
|
|
||||||
});
|
|
||||||
socialUserMapper.insert(oldSocialUser);
|
|
||||||
// 准备参数
|
// 准备参数
|
||||||
Long userId = oldSocialUser.getUserId();
|
SocialUserBindReqDTO reqDTO = new SocialUserBindReqDTO()
|
||||||
Integer type = oldSocialUser.getType();
|
.setUserId(1L).setUserType(UserTypeEnum.ADMIN.getValue())
|
||||||
String newUnionId = randomString(10);
|
.setType(SocialTypeEnum.GITEE.getType()).setCode("test_code").setState("test_state");
|
||||||
|
// mock 数据:获得社交用户
|
||||||
|
SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(reqDTO.getType())
|
||||||
|
.setCode(reqDTO.getCode()).setState(reqDTO.getState());
|
||||||
|
socialUserMapper.insert(socialUser);
|
||||||
|
// mock 数据:用户可能之前已经绑定过该社交类型
|
||||||
|
socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class).setUserId(1L).setUserType(UserTypeEnum.ADMIN.getValue())
|
||||||
|
.setSocialType(SocialTypeEnum.GITEE.getType()).setSocialUserId(-1L));
|
||||||
|
// mock 数据:社交用户可能之前绑定过别的用户
|
||||||
|
socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class).setUserType(UserTypeEnum.ADMIN.getValue())
|
||||||
|
.setSocialType(SocialTypeEnum.GITEE.getType()).setSocialUserId(socialUser.getId()));
|
||||||
|
|
||||||
// 调用
|
// 调用
|
||||||
socialService.unbindOldSocialUser(userId, UserTypeEnum.ADMIN.getValue(), type, newUnionId);
|
socialUserService.bindSocialUser(reqDTO);
|
||||||
// 断言
|
// 断言
|
||||||
assertEquals(0L, socialUserMapper.selectCount(null).longValue());
|
List<SocialUserBindDO> socialUserBinds = socialUserBindMapper.selectList();
|
||||||
|
assertEquals(1, socialUserBinds.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnbindSocialUser_success() {
|
||||||
|
// 准备参数
|
||||||
|
Long userId = 1L;
|
||||||
|
Integer userType = UserTypeEnum.ADMIN.getValue();
|
||||||
|
Integer type = SocialTypeEnum.GITEE.getType();
|
||||||
|
String openid = "test_openid";
|
||||||
|
// mock 数据:社交用户
|
||||||
|
SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(type).setOpenid(openid);
|
||||||
|
socialUserMapper.insert(socialUser);
|
||||||
|
// mock 数据:社交绑定关系
|
||||||
|
SocialUserBindDO socialUserBind = randomPojo(SocialUserBindDO.class).setUserType(userType)
|
||||||
|
.setUserId(userId).setSocialType(type);
|
||||||
|
socialUserBindMapper.insert(socialUserBind);
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
socialUserService.unbindSocialUser(userId, userType, type, openid);
|
||||||
|
// 断言
|
||||||
|
assertEquals(0, socialUserBindMapper.selectCount(null).intValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnbindSocialUser_notFound() {
|
||||||
|
// 调用,并断言
|
||||||
|
assertServiceException(
|
||||||
|
() -> socialUserService.unbindSocialUser(randomLong(), UserTypeEnum.ADMIN.getValue(),
|
||||||
|
SocialTypeEnum.GITEE.getType(), "test_openid"),
|
||||||
|
SOCIAL_USER_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetBindUserId() {
|
||||||
|
// 准备参数
|
||||||
|
Integer userType = UserTypeEnum.ADMIN.getValue();
|
||||||
|
Integer type = SocialTypeEnum.GITEE.getType();
|
||||||
|
String code = "tudou";
|
||||||
|
String state = "yuanma";
|
||||||
|
// mock 社交用户
|
||||||
|
SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(type).setCode(code).setState(state);
|
||||||
|
socialUserMapper.insert(socialUser);
|
||||||
|
// mock 社交用户的绑定
|
||||||
|
Long userId = randomLong();
|
||||||
|
SocialUserBindDO socialUserBind = randomPojo(SocialUserBindDO.class).setUserType(userType).setUserId(userId)
|
||||||
|
.setSocialType(type).setSocialUserId(socialUser.getId());
|
||||||
|
socialUserBindMapper.insert(socialUserBind);
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
Long result = socialUserService.getBindUserId(userType, type, code, state);
|
||||||
|
// 断言
|
||||||
|
assertEquals(userId, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ DELETE FROM "system_sms_template";
|
||||||
DELETE FROM "system_sms_log";
|
DELETE FROM "system_sms_log";
|
||||||
DELETE FROM "system_error_code";
|
DELETE FROM "system_error_code";
|
||||||
DELETE FROM "system_social_user";
|
DELETE FROM "system_social_user";
|
||||||
|
DELETE FROM "system_social_user_bind";
|
||||||
DELETE FROM "system_tenant";
|
DELETE FROM "system_tenant";
|
||||||
DELETE FROM "system_tenant_package";
|
DELETE FROM "system_tenant_package";
|
||||||
DELETE FROM "system_sensitive_word";
|
DELETE FROM "system_sensitive_word";
|
||||||
|
|
|
@ -378,16 +378,15 @@ CREATE TABLE IF NOT EXISTS "system_error_code" (
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS "system_social_user" (
|
CREATE TABLE IF NOT EXISTS "system_social_user" (
|
||||||
"id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
"id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||||
"user_id" bigint NOT NULL,
|
|
||||||
"user_type" tinyint NOT NULL DEFAULT '0',
|
|
||||||
"type" tinyint NOT NULL,
|
"type" tinyint NOT NULL,
|
||||||
"openid" varchar(32) NOT NULL,
|
"openid" varchar(64) NOT NULL,
|
||||||
"token" varchar(256) DEFAULT NULL,
|
"token" varchar(256) DEFAULT NULL,
|
||||||
"union_id" varchar(32) NOT NULL,
|
|
||||||
"raw_token_info" varchar(1024) NOT NULL,
|
"raw_token_info" varchar(1024) NOT NULL,
|
||||||
"nickname" varchar(32) NOT NULL,
|
"nickname" varchar(32) NOT NULL,
|
||||||
"avatar" varchar(255) DEFAULT NULL,
|
"avatar" varchar(255) DEFAULT NULL,
|
||||||
"raw_user_info" varchar(1024) NOT NULL,
|
"raw_user_info" varchar(1024) NOT NULL,
|
||||||
|
"code" varchar(64) NOT NULL,
|
||||||
|
"state" varchar(64),
|
||||||
"creator" varchar(64) DEFAULT '',
|
"creator" varchar(64) DEFAULT '',
|
||||||
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
"updater" varchar(64) DEFAULT '',
|
"updater" varchar(64) DEFAULT '',
|
||||||
|
@ -396,6 +395,20 @@ CREATE TABLE IF NOT EXISTS "system_social_user" (
|
||||||
PRIMARY KEY ("id")
|
PRIMARY KEY ("id")
|
||||||
) COMMENT '社交用户';
|
) COMMENT '社交用户';
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "system_social_user_bind" (
|
||||||
|
"id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||||
|
"user_id" bigint NOT NULL,
|
||||||
|
"user_type" tinyint NOT NULL,
|
||||||
|
"social_type" tinyint NOT NULL,
|
||||||
|
"social_user_id" number NOT NULL,
|
||||||
|
"creator" varchar(64) DEFAULT '',
|
||||||
|
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updater" varchar(64) DEFAULT '',
|
||||||
|
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||||
|
PRIMARY KEY ("id")
|
||||||
|
) COMMENT '社交用户的绑定';
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS "system_tenant" (
|
CREATE TABLE IF NOT EXISTS "system_tenant" (
|
||||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||||
"name" varchar(63) NOT NULL,
|
"name" varchar(63) NOT NULL,
|
||||||
|
|
|
@ -186,10 +186,6 @@ yudao:
|
||||||
justauth:
|
justauth:
|
||||||
enabled: true
|
enabled: true
|
||||||
type:
|
type:
|
||||||
GITEE: # Gitee
|
|
||||||
client-id: ee61f0374a4c6c404a8717094caa7a410d76950e45ff60348015830c519ba5c1
|
|
||||||
client-secret: 7c044a5671be3b051414db0cf2cec6ad702dd298d2416ba24ceaf608e6fa26f9
|
|
||||||
ignore-check-redirect-uri: true
|
|
||||||
DINGTALK: # 钉钉
|
DINGTALK: # 钉钉
|
||||||
client-id: dingvrnreaje3yqvzhxg
|
client-id: dingvrnreaje3yqvzhxg
|
||||||
client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI
|
client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI
|
||||||
|
|
|
@ -196,10 +196,6 @@ yudao:
|
||||||
justauth:
|
justauth:
|
||||||
enabled: true
|
enabled: true
|
||||||
type:
|
type:
|
||||||
GITEE: # Gitee
|
|
||||||
client-id: ee61f0374a4c6c404a8717094caa7a410d76950e45ff60348015830c519ba5c1
|
|
||||||
client-secret: 7c044a5671be3b051414db0cf2cec6ad702dd298d2416ba24ceaf608e6fa26f9
|
|
||||||
ignore-check-redirect-uri: true
|
|
||||||
DINGTALK: # 钉钉
|
DINGTALK: # 钉钉
|
||||||
client-id: dingvrnreaje3yqvzhxg
|
client-id: dingvrnreaje3yqvzhxg
|
||||||
client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI
|
client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI
|
||||||
|
|
|
@ -9,7 +9,7 @@ export function login(username, password, code, uuid) {
|
||||||
uuid
|
uuid
|
||||||
}
|
}
|
||||||
return request({
|
return request({
|
||||||
url: '/system/login',
|
url: '/system/auth/login',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
|
@ -18,7 +18,7 @@ export function login(username, password, code, uuid) {
|
||||||
// 获取用户详细信息
|
// 获取用户详细信息
|
||||||
export function getInfo() {
|
export function getInfo() {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/get-permission-info',
|
url: '/system/auth/get-permission-info',
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -43,15 +43,15 @@ export function getCodeImg() {
|
||||||
// 社交授权的跳转
|
// 社交授权的跳转
|
||||||
export function socialAuthRedirect(type, redirectUri) {
|
export function socialAuthRedirect(type, redirectUri) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/social-auth-redirect?type=' + type + '&redirectUri=' + redirectUri,
|
url: '/system/auth/social-auth-redirect?type=' + type + '&redirectUri=' + redirectUri,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 社交登录,使用 code 授权码
|
// 社交快捷登录,使用 code 授权码
|
||||||
export function socialLogin(type, code, state) {
|
export function socialQuickLogin(type, code, state) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/social-login',
|
url: '/system/auth/social-quick-login',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: {
|
data: {
|
||||||
type,
|
type,
|
||||||
|
@ -61,10 +61,10 @@ export function socialLogin(type, code, state) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 社交登录,使用 code 授权码 + + 账号密码
|
// 社交绑定登录,使用 code 授权码 + + 账号密码
|
||||||
export function socialLogin2(type, code, state, username, password) {
|
export function socialBindLogin(type, code, state, username, password) {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/social-login2',
|
url: '/system/auth/social-bind-login',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: {
|
data: {
|
||||||
type,
|
type,
|
||||||
|
@ -75,28 +75,3 @@ export function socialLogin2(type, code, state, username, password) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 社交绑定,使用 code 授权码
|
|
||||||
export function socialBind(type, code, state) {
|
|
||||||
return request({
|
|
||||||
url: '/system/social-bind',
|
|
||||||
method: 'post',
|
|
||||||
data: {
|
|
||||||
type,
|
|
||||||
code,
|
|
||||||
state,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 取消社交绑定
|
|
||||||
export function socialUnbind(type, unionId) {
|
|
||||||
return request({
|
|
||||||
url: '/system/social-unbind',
|
|
||||||
method: 'delete',
|
|
||||||
data: {
|
|
||||||
type,
|
|
||||||
unionId
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import request from '@/utils/request'
|
||||||
// 获取路由
|
// 获取路由
|
||||||
export const getRouters = () => {
|
export const getRouters = () => {
|
||||||
return request({
|
return request({
|
||||||
url: '/system/list-menus',
|
url: '/system/auth/list-menus',
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import request from "@/utils/request";
|
||||||
|
|
||||||
|
// 社交绑定,使用 code 授权码
|
||||||
|
export function socialBind(type, code, state) {
|
||||||
|
return request({
|
||||||
|
url: '/system/social-user/bind',
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
type,
|
||||||
|
code,
|
||||||
|
state,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消社交绑定
|
||||||
|
export function socialUnbind(type, openid) {
|
||||||
|
return request({
|
||||||
|
url: '/system/social-user/unbind',
|
||||||
|
method: 'delete',
|
||||||
|
data: {
|
||||||
|
type,
|
||||||
|
openid
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import {login, logout, getInfo, socialLogin, socialLogin2} from '@/api/login'
|
import {login, logout, getInfo, socialQuickLogin, socialBindLogin} from '@/api/login'
|
||||||
import { getToken, setToken, removeToken } from '@/utils/auth'
|
import { getToken, setToken, removeToken } from '@/utils/auth'
|
||||||
|
|
||||||
const user = {
|
const user = {
|
||||||
|
@ -57,7 +57,7 @@ const user = {
|
||||||
const state = userInfo.state
|
const state = userInfo.state
|
||||||
const type = userInfo.type
|
const type = userInfo.type
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
socialLogin(type, code, state).then(res => {
|
socialQuickLogin(type, code, state).then(res => {
|
||||||
res = res.data;
|
res = res.data;
|
||||||
setToken(res.token)
|
setToken(res.token)
|
||||||
commit('SET_TOKEN', res.token)
|
commit('SET_TOKEN', res.token)
|
||||||
|
@ -76,7 +76,7 @@ const user = {
|
||||||
const username = userInfo.username.trim()
|
const username = userInfo.username.trim()
|
||||||
const password = userInfo.password
|
const password = userInfo.password
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
socialLogin2(type, code, state, username, password).then(res => {
|
socialBindLogin(type, code, state, username, password).then(res => {
|
||||||
res = res.data;
|
res = res.data;
|
||||||
setToken(res.token)
|
setToken(res.token)
|
||||||
commit('SET_TOKEN', res.token)
|
commit('SET_TOKEN', res.token)
|
||||||
|
|
|
@ -71,12 +71,6 @@ export const InfraApiErrorLogProcessStatusEnum = {
|
||||||
* 用户的社交平台的类型枚举
|
* 用户的社交平台的类型枚举
|
||||||
*/
|
*/
|
||||||
export const SystemUserSocialTypeEnum = {
|
export const SystemUserSocialTypeEnum = {
|
||||||
// GITEE: {
|
|
||||||
// title: "码云",
|
|
||||||
// type: 10,
|
|
||||||
// source: "gitee",
|
|
||||||
// img: "https://cdn.jsdelivr.net/gh/justauth/justauth-oauth-logo@1.11/gitee.png",
|
|
||||||
// },
|
|
||||||
DINGTALK: {
|
DINGTALK: {
|
||||||
title: "钉钉",
|
title: "钉钉",
|
||||||
type: 20,
|
type: 20,
|
||||||
|
|
|
@ -176,7 +176,6 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
doSocialLogin(socialTypeEnum) {
|
doSocialLogin(socialTypeEnum) {
|
||||||
// console.log("开始Oauth登录...%o", socialTypeEnum.code);
|
|
||||||
// 设置登录中
|
// 设置登录中
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
// 计算 redirectUri
|
// 计算 redirectUri
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" align="left" >
|
<el-table-column label="操作" align="left" >
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<div v-if="scope.row.unionId">
|
<div v-if="scope.row.openid">
|
||||||
已绑定
|
已绑定
|
||||||
<el-button size="large" type="text" @click="unbind(scope.row)">(解绑)</el-button>
|
<el-button size="large" type="text" @click="unbind(scope.row)">(解绑)</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -23,7 +23,8 @@
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import {SystemUserSocialTypeEnum} from "@/utils/constants";
|
import {SystemUserSocialTypeEnum} from "@/utils/constants";
|
||||||
import {socialAuthRedirect, socialBind, socialUnbind} from "@/api/login";
|
import {socialAuthRedirect} from "@/api/login";
|
||||||
|
import {socialBind, socialUnbind} from "@/api/system/socialUser";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
|
@ -50,7 +51,7 @@ export default {
|
||||||
if (this.user.socialUsers) {
|
if (this.user.socialUsers) {
|
||||||
for (const j in this.user.socialUsers) {
|
for (const j in this.user.socialUsers) {
|
||||||
if (socialUser.type === this.user.socialUsers[j].type) {
|
if (socialUser.type === this.user.socialUsers[j].type) {
|
||||||
socialUser.unionId = this.user.socialUsers[j].unionId;
|
socialUser.openid = this.user.socialUsers[j].openid;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,9 +87,9 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
unbind(socialUser) {
|
unbind(socialUser) {
|
||||||
socialUnbind(socialUser.type, socialUser.unionId).then(resp => {
|
socialUnbind(socialUser.type, socialUser.openid).then(resp => {
|
||||||
this.$modal.msgSuccess("解绑成功");
|
this.$modal.msgSuccess("解绑成功");
|
||||||
socialUser.unionId = undefined;
|
socialUser.openid = undefined;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
close() {
|
close() {
|
||||||
|
|
Loading…
Reference in New Issue