Commit 0bf5b20fd7c3cf27c4f0e5bf07ca14ec8afed832

Authored by 芋道源码
Committed by Gitee
2 parents 78275a16 c4020779

!137 简化三方登录的实现,降低理解成本

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