Commit 0bf5b20fd7c3cf27c4f0e5bf07ca14ec8afed832
Committed by
Gitee
!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 | 1 | package cn.iocoder.yudao.framework.social.config; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.util.ReflectUtil; | |
| 3 | 4 | import cn.iocoder.yudao.framework.social.core.YudaoAuthRequestFactory; |
| 5 | +import com.xkcoding.http.HttpUtil; | |
| 6 | +import com.xkcoding.http.support.hutool.HutoolImpl; | |
| 4 | 7 | import com.xkcoding.justauth.autoconfigure.JustAuthProperties; |
| 5 | 8 | import lombok.extern.slf4j.Slf4j; |
| 6 | 9 | import me.zhyd.oauth.cache.AuthStateCache; |
| ... | ... | @@ -23,6 +26,9 @@ public class YudaoSocialAutoConfiguration { |
| 23 | 26 | @Bean |
| 24 | 27 | @ConditionalOnProperty(prefix = "justauth", value = "enabled", havingValue = "true", matchIfMissing = true) |
| 25 | 28 | public YudaoAuthRequestFactory yudaoAuthRequestFactory(JustAuthProperties properties, AuthStateCache authStateCache) { |
| 29 | + // 需要修改 HttpUtil 使用的实现,避免类报错 | |
| 30 | + HttpUtil.setHttp(new HutoolImpl()); | |
| 31 | + // 创建 YudaoAuthRequestFactory | |
| 26 | 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 | 23 | |
| 24 | 24 | @Api(tags = "用户 APP - 认证") |
| 25 | 25 | @RestController |
| 26 | -@RequestMapping("/member/") | |
| 26 | +@RequestMapping("/member/auth") | |
| 27 | 27 | @Validated |
| 28 | 28 | @Slf4j |
| 29 | 29 | public class AppAuthController { |
| ... | ... | @@ -33,7 +33,6 @@ public class AppAuthController { |
| 33 | 33 | |
| 34 | 34 | @PostMapping("/login") |
| 35 | 35 | @ApiOperation("使用手机 + 密码登录") |
| 36 | - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 | |
| 37 | 36 | public CommonResult<AppAuthLoginRespVO> login(@RequestBody @Valid AppAuthLoginReqVO reqVO) { |
| 38 | 37 | String token = authService.login(reqVO, getClientIP(), getUserAgent()); |
| 39 | 38 | // 返回结果 |
| ... | ... | @@ -42,7 +41,6 @@ public class AppAuthController { |
| 42 | 41 | |
| 43 | 42 | @PostMapping("/sms-login") |
| 44 | 43 | @ApiOperation("使用手机 + 验证码登录") |
| 45 | - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 | |
| 46 | 44 | public CommonResult<AppAuthLoginRespVO> smsLogin(@RequestBody @Valid AppAuthSmsLoginReqVO reqVO) { |
| 47 | 45 | String token = authService.smsLogin(reqVO, getClientIP(), getUserAgent()); |
| 48 | 46 | // 返回结果 |
| ... | ... | @@ -51,7 +49,6 @@ public class AppAuthController { |
| 51 | 49 | |
| 52 | 50 | @PostMapping("/send-sms-code") |
| 53 | 51 | @ApiOperation(value = "发送手机验证码") |
| 54 | - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 | |
| 55 | 52 | public CommonResult<Boolean> sendSmsCode(@RequestBody @Valid AppAuthSendSmsReqVO reqVO) { |
| 56 | 53 | authService.sendSmsCode(getLoginUserId(), reqVO); |
| 57 | 54 | return success(true); |
| ... | ... | @@ -60,7 +57,6 @@ public class AppAuthController { |
| 60 | 57 | @PostMapping("/reset-password") |
| 61 | 58 | @ApiOperation(value = "重置密码", notes = "用户忘记密码时使用") |
| 62 | 59 | @PreAuthenticated |
| 63 | - @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 | |
| 64 | 60 | public CommonResult<Boolean> resetPassword(@RequestBody @Valid AppAuthResetPasswordReqVO reqVO) { |
| 65 | 61 | authService.resetPassword(reqVO); |
| 66 | 62 | return success(true); |
| ... | ... | @@ -87,35 +83,18 @@ public class AppAuthController { |
| 87 | 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 | 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 | 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 | 14 | import javax.validation.constraints.NotNull; |
| 15 | 15 | import javax.validation.constraints.Pattern; |
| 16 | 16 | |
| 17 | -@ApiModel("用户 APP - 社交登录 Request VO,使用 code 授权码 + 账号密码") | |
| 17 | +@ApiModel("用户 APP - 社交绑定登录 Request VO,使用 code 授权码 + 账号密码") | |
| 18 | 18 | @Data |
| 19 | 19 | @NoArgsConstructor |
| 20 | 20 | @AllArgsConstructor |
| 21 | 21 | @Builder |
| 22 | -public class AppAuthSocialLogin2ReqVO { | |
| 22 | +public class AppAuthSocialBindLoginReqVO { | |
| 23 | 23 | |
| 24 | 24 | @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值") |
| 25 | 25 | @InEnum(SocialTypeEnum.class) |
| ... | ... | @@ -45,5 +45,4 @@ public class AppAuthSocialLogin2ReqVO { |
| 45 | 45 | @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字") |
| 46 | 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 | import javax.validation.constraints.NotEmpty; |
| 13 | 13 | import javax.validation.constraints.NotNull; |
| 14 | 14 | |
| 15 | -@ApiModel("用户 APP - 社交绑定 Request VO,使用 code 授权码") | |
| 15 | +@ApiModel("用户 APP - 社交快捷登录 Request VO,使用 code 授权码") | |
| 16 | 16 | @Data |
| 17 | 17 | @NoArgsConstructor |
| 18 | 18 | @AllArgsConstructor |
| 19 | 19 | @Builder |
| 20 | -public class AppAuthSocialBindReqVO { | |
| 20 | +public class AppAuthSocialQuickLoginReqVO { | |
| 21 | 21 | |
| 22 | 22 | @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值") |
| 23 | 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 | 3 | import cn.iocoder.yudao.framework.common.validation.InEnum; |
| 4 | 4 | import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; |
| ... | ... | @@ -12,12 +12,12 @@ import lombok.NoArgsConstructor; |
| 12 | 12 | import javax.validation.constraints.NotEmpty; |
| 13 | 13 | import javax.validation.constraints.NotNull; |
| 14 | 14 | |
| 15 | -@ApiModel("用户 APP - 社交登录 Request VO,使用 code 授权码") | |
| 15 | +@ApiModel("用户 APP - 社交绑定 Request VO,使用 code 授权码") | |
| 16 | 16 | @Data |
| 17 | 17 | @NoArgsConstructor |
| 18 | 18 | @AllArgsConstructor |
| 19 | 19 | @Builder |
| 20 | -public class AppAuthSocialLoginReqVO { | |
| 20 | +public class AppSocialUserBindReqVO { | |
| 21 | 21 | |
| 22 | 22 | @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值") |
| 23 | 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 | 3 | import cn.iocoder.yudao.framework.common.validation.InEnum; |
| 4 | 4 | import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; |
| ... | ... | @@ -12,20 +12,20 @@ import lombok.NoArgsConstructor; |
| 12 | 12 | import javax.validation.constraints.NotEmpty; |
| 13 | 13 | import javax.validation.constraints.NotNull; |
| 14 | 14 | |
| 15 | -@ApiModel("用户 APP - 取消社交绑定 Request VO,使用 code 授权码") | |
| 15 | +@ApiModel("用户 APP - 取消社交绑定 Request VO") | |
| 16 | 16 | @Data |
| 17 | 17 | @NoArgsConstructor |
| 18 | 18 | @AllArgsConstructor |
| 19 | 19 | @Builder |
| 20 | -public class AppAuthSocialUnbindReqVO { | |
| 20 | +public class AppSocialUserUnbindReqVO { | |
| 21 | 21 | |
| 22 | 22 | @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SysUserSocialTypeEnum 枚举值") |
| 23 | 23 | @InEnum(SocialTypeEnum.class) |
| 24 | 24 | @NotNull(message = "社交平台的类型不能为空") |
| 25 | 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 | 3 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 4 | 4 | import cn.iocoder.yudao.framework.security.core.LoginUser; |
| 5 | 5 | import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; |
| 6 | +import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO; | |
| 6 | 7 | import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; |
| 7 | 8 | import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; |
| 8 | 9 | import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; |
| ... | ... | @@ -26,10 +27,9 @@ public interface AuthConvert { |
| 26 | 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 | 34 | SmsCodeSendReqDTO convert(AppAuthSendSmsReqVO reqVO); |
| 35 | 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 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService; |
| 4 | 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 | 8 | import javax.validation.Valid; |
| 7 | 9 | |
| ... | ... | @@ -43,7 +45,7 @@ public interface MemberAuthService extends SecurityAuthFrameworkService { |
| 43 | 45 | * @param userAgent 用户 UA |
| 44 | 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 | 55 | * @param userAgent 用户 UA |
| 54 | 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 | 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 | 8 | import cn.iocoder.yudao.framework.security.core.LoginUser; |
| 9 | 9 | import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken; |
| 10 | 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 | 13 | import cn.iocoder.yudao.module.member.convert.auth.AuthConvert; |
| 12 | 14 | import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; |
| 13 | 15 | import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper; |
| ... | ... | @@ -108,7 +110,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 108 | 110 | } |
| 109 | 111 | |
| 110 | 112 | @Override |
| 111 | - public String socialLogin(AppAuthSocialLoginReqVO reqVO, String userIp, String userAgent) { | |
| 113 | + public String socialQuickLogin(AppAuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent) { | |
| 112 | 114 | // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 |
| 113 | 115 | Long userId = socialUserApi.getBindUserId(UserTypeEnum.MEMBER.getValue(), reqVO.getType(), |
| 114 | 116 | reqVO.getCode(), reqVO.getState()); |
| ... | ... | @@ -125,25 +127,19 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 125 | 127 | // 创建 LoginUser 对象 |
| 126 | 128 | LoginUser loginUser = AuthConvert.INSTANCE.convert(user); |
| 127 | 129 | |
| 128 | - // 绑定社交用户(更新) | |
| 129 | - socialUserApi.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO)); | |
| 130 | - | |
| 131 | 130 | // 缓存登录用户到 Redis 中,返回 sessionId 编号 |
| 132 | 131 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); |
| 133 | 132 | } |
| 134 | 133 | |
| 135 | 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 | 137 | AppAuthSmsLoginReqVO loginReqVO = AppAuthSmsLoginReqVO.builder() |
| 142 | 138 | .mobile(reqVO.getMobile()).code(reqVO.getSmsCode()).build(); |
| 143 | 139 | String sessionId = this.smsLogin(loginReqVO, userIp, userAgent); |
| 144 | 140 | LoginUser loginUser = userSessionApi.getLoginUser(sessionId); |
| 145 | 141 | |
| 146 | - // 绑定社交用户(新增) | |
| 142 | + // 绑定社交用户 | |
| 147 | 143 | socialUserApi.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO)); |
| 148 | 144 | return sessionId; |
| 149 | 145 | } |
| ... | ... | @@ -156,17 +152,6 @@ public class MemberAuthServiceImpl implements MemberAuthService { |
| 156 | 152 | } |
| 157 | 153 | |
| 158 | 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 | 155 | public String getSocialAuthorizeUrl(Integer type, String redirectUri) { |
| 171 | 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 | 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 | 42 | * 注意,返回的是 MemberUser 或者 AdminUser 的 id 编号! |
| 53 | - * 该方法会执行和 {@link #checkSocialUser(Integer, String, String)} 一样的逻辑。 | |
| 54 | - * 所以在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常 | |
| 43 | + * 在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常 | |
| 55 | 44 | * |
| 56 | 45 | * @param userType 用户类型 |
| 57 | 46 | * @param type 社交平台的类型 | ... | ... |
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/social/SocialTypeEnum.java
| 1 | 1 | package cn.iocoder.yudao.module.system.enums.social; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.collection.ListUtil; | |
| 4 | 3 | import cn.hutool.core.util.ArrayUtil; |
| 5 | 4 | import cn.iocoder.yudao.framework.common.core.IntArrayValuable; |
| 6 | 5 | import lombok.AllArgsConstructor; |
| 7 | 6 | import lombok.Getter; |
| 8 | 7 | |
| 9 | 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 | 54 | |
| 54 | 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 | 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 | 37 | } |
| 38 | 38 | |
| 39 | 39 | @Override |
| 40 | - public void checkSocialUser(Integer type, String code, String state) { | |
| 41 | - socialUserService.checkSocialUser(type, code, state); | |
| 42 | - } | |
| 43 | - | |
| 44 | - @Override | |
| 45 | 40 | public Long getBindUserId(Integer userType, Integer type, String code, String state) { |
| 46 | 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 | 36 | |
| 37 | 37 | @Api(tags = "管理后台 - 认证") |
| 38 | 38 | @RestController |
| 39 | -@RequestMapping("/system") // 暂时不跟 /auth 结尾 | |
| 39 | +@RequestMapping("/system/auth") // 暂时不跟 /auth 结尾 | |
| 40 | 40 | @Validated |
| 41 | 41 | @Slf4j |
| 42 | 42 | public class AuthController { |
| ... | ... | @@ -80,7 +80,7 @@ public class AuthController { |
| 80 | 80 | return success(AuthConvert.INSTANCE.convert(user, roleList, menuList)); |
| 81 | 81 | } |
| 82 | 82 | |
| 83 | - @GetMapping("list-menus") | |
| 83 | + @GetMapping("/list-menus") | |
| 84 | 84 | @ApiOperation("获得登录用户的菜单列表") |
| 85 | 85 | public CommonResult<List<AuthMenuRespVO>> getMenus() { |
| 86 | 86 | // 获得用户拥有的菜单列表 |
| ... | ... | @@ -105,36 +105,22 @@ public class AuthController { |
| 105 | 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 | 110 | @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 |
| 111 | - public CommonResult<AuthLoginRespVO> socialLogin(@RequestBody @Valid AuthSocialLoginReqVO reqVO) { | |
| 111 | + public CommonResult<AuthLoginRespVO> socialQuickLogin(@RequestBody @Valid AuthSocialQuickLoginReqVO reqVO) { | |
| 112 | 112 | String token = authService.socialLogin(reqVO, getClientIP(), getUserAgent()); |
| 113 | 113 | // 返回结果 |
| 114 | 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 | 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 | 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 | 14 | import javax.validation.constraints.NotNull; |
| 15 | 15 | import javax.validation.constraints.Pattern; |
| 16 | 16 | |
| 17 | -@ApiModel("管理后台 - 社交登录 Request VO,使用 code 授权码 + 账号密码") | |
| 17 | +@ApiModel("管理后台 - 社交绑定登录 Request VO,使用 code 授权码 + 账号密码") | |
| 18 | 18 | @Data |
| 19 | 19 | @NoArgsConstructor |
| 20 | 20 | @AllArgsConstructor |
| 21 | 21 | @Builder |
| 22 | -public class AuthSocialLogin2ReqVO { | |
| 22 | +public class AuthSocialBindLoginReqVO { | |
| 23 | 23 | |
| 24 | 24 | @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 UserSocialTypeEnum 枚举值") |
| 25 | 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 | import javax.validation.constraints.NotEmpty; |
| 13 | 13 | import javax.validation.constraints.NotNull; |
| 14 | 14 | |
| 15 | -@ApiModel("管理后台 - 社交登录 Request VO,使用 code 授权码") | |
| 15 | +@ApiModel("管理后台 - 社交快捷登录 Request VO,使用 code 授权码") | |
| 16 | 16 | @Data |
| 17 | 17 | @NoArgsConstructor |
| 18 | 18 | @AllArgsConstructor |
| 19 | 19 | @Builder |
| 20 | -public class AuthSocialLoginReqVO { | |
| 20 | +public class AuthSocialQuickLoginReqVO { | |
| 21 | 21 | |
| 22 | 22 | @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 UserSocialTypeEnum 枚举值") |
| 23 | 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 | 3 | import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.validation.InEnum; |
| ... | ... | @@ -17,7 +17,7 @@ import javax.validation.constraints.NotNull; |
| 17 | 17 | @NoArgsConstructor |
| 18 | 18 | @AllArgsConstructor |
| 19 | 19 | @Builder |
| 20 | -public class AuthSocialBindReqVO { | |
| 20 | +public class SocialUserBindReqVO { | |
| 21 | 21 | |
| 22 | 22 | @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 UserSocialTypeEnum 枚举值") |
| 23 | 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 | 3 | import cn.iocoder.yudao.framework.common.validation.InEnum; |
| 4 | 4 | import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; |
| ... | ... | @@ -12,20 +12,20 @@ import lombok.NoArgsConstructor; |
| 12 | 12 | import javax.validation.constraints.NotEmpty; |
| 13 | 13 | import javax.validation.constraints.NotNull; |
| 14 | 14 | |
| 15 | -@ApiModel("管理后台 - 取消社交绑定 Request VO,使用 code 授权码") | |
| 15 | +@ApiModel("管理后台 - 取消社交绑定 Request VO") | |
| 16 | 16 | @Data |
| 17 | 17 | @NoArgsConstructor |
| 18 | 18 | @AllArgsConstructor |
| 19 | 19 | @Builder |
| 20 | -public class AuthSocialUnbindReqVO { | |
| 20 | +public class SocialUserUnbindReqVO { | |
| 21 | 21 | |
| 22 | 22 | @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 UserSocialTypeEnum 枚举值") |
| 23 | 23 | @InEnum(SocialTypeEnum.class) |
| 24 | 24 | @NotNull(message = "社交平台的类型不能为空") |
| 25 | 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 | 96 | @ApiModelProperty(value = "社交平台的类型", required = true, example = "10", notes = "参见 SocialTypeEnum 枚举类") |
| 97 | 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 | 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 | 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 | 3 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 4 | +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; | |
| 6 | 5 | import com.baomidou.mybatisplus.annotation.TableId; |
| 7 | 6 | import com.baomidou.mybatisplus.annotation.TableName; |
| 8 | 7 | import lombok.*; |
| 9 | 8 | |
| 10 | 9 | /** |
| 11 | 10 | * 社交用户 |
| 12 | - * 通过 {@link SocialUserDO#getUserId()} 关联到对应的 {@link AdminUserDO} | |
| 13 | 11 | * |
| 14 | 12 | * @author weir |
| 15 | 13 | */ |
| ... | ... | @@ -27,20 +25,9 @@ public class SocialUserDO extends BaseDO { |
| 27 | 25 | @TableId |
| 28 | 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 | 32 | private Integer type; |
| 46 | 33 | |
| ... | ... | @@ -53,13 +40,6 @@ public class SocialUserDO extends BaseDO { |
| 53 | 40 | */ |
| 54 | 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 | 43 | * 原始 Token 数据,一般是 JSON 格式 |
| 64 | 44 | */ |
| 65 | 45 | private String rawTokenInfo; |
| ... | ... | @@ -77,6 +57,15 @@ public class SocialUserDO extends BaseDO { |
| 77 | 57 | */ |
| 78 | 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 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; |
| 4 | 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 5 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |
| 5 | 6 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| 6 | 7 | import org.apache.ibatis.annotations.Mapper; |
| 7 | 8 | |
| ... | ... | @@ -11,18 +12,17 @@ import java.util.List; |
| 11 | 12 | @Mapper |
| 12 | 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 | 23 | "login_user:%s", // 参数为 sessionId |
| 24 | 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 | 26 | RedisKeyDefine SOCIAL_AUTH_STATE = new RedisKeyDefine("社交登陆的 state", // 注意,它是被 JustAuth 的 justauth.type.prefix 使用到 |
| 31 | 27 | "social_auth_state:%s", // 参数为 state |
| 32 | 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 | 18 | |
| 19 | 19 | @Override |
| 20 | 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 | 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 | 25 | String login(@Valid AuthLoginReqVO reqVO, String userIp, String userAgent); |
| 26 | 26 | |
| 27 | 27 | /** |
| 28 | - * 社交登录,使用 code 授权码 | |
| 28 | + * 社交快捷登录,使用 code 授权码 | |
| 29 | 29 | * |
| 30 | 30 | * @param reqVO 登录信息 |
| 31 | 31 | * @param userIp 用户 IP |
| 32 | 32 | * @param userAgent 用户 UA |
| 33 | 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 | 40 | * @param reqVO 登录信息 |
| 41 | 41 | * @param userIp 用户 IP |
| 42 | 42 | * @param userAgent 用户 UA |
| 43 | 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 | import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken; |
| 10 | 10 | import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; |
| 11 | 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 | 14 | import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; |
| 16 | 15 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; |
| 17 | 16 | import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; |
| ... | ... | @@ -22,7 +21,6 @@ import cn.iocoder.yudao.module.system.service.permission.PermissionService; |
| 22 | 21 | import cn.iocoder.yudao.module.system.service.social.SocialUserService; |
| 23 | 22 | import cn.iocoder.yudao.module.system.service.user.AdminUserService; |
| 24 | 23 | import lombok.extern.slf4j.Slf4j; |
| 25 | -import me.zhyd.oauth.model.AuthUser; | |
| 26 | 24 | import org.springframework.beans.factory.annotation.Autowired; |
| 27 | 25 | import org.springframework.context.annotation.Lazy; |
| 28 | 26 | import org.springframework.security.authentication.AuthenticationManager; |
| ... | ... | @@ -82,7 +80,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 82 | 80 | throw new UsernameNotFoundException(username); |
| 83 | 81 | } |
| 84 | 82 | // 创建 LoginUser 对象 |
| 85 | - return this.buildLoginUser(user); | |
| 83 | + return buildLoginUser(user); | |
| 86 | 84 | } |
| 87 | 85 | |
| 88 | 86 | @Override |
| ... | ... | @@ -92,19 +90,19 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 92 | 90 | if (user == null) { |
| 93 | 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 | 95 | // 创建 LoginUser 对象 |
| 98 | - return this.buildLoginUser(user); | |
| 96 | + return buildLoginUser(user); | |
| 99 | 97 | } |
| 100 | 98 | |
| 101 | 99 | @Override |
| 102 | 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 | 107 | // 缓存登陆用户到 Redis 中,返回 sessionId 编号 |
| 110 | 108 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); |
| ... | ... | @@ -192,7 +190,7 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 192 | 190 | } |
| 193 | 191 | |
| 194 | 192 | @Override |
| 195 | - public String socialLogin(AuthSocialLoginReqVO reqVO, String userIp, String userAgent) { | |
| 193 | + public String socialLogin(AuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent) { | |
| 196 | 194 | // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 |
| 197 | 195 | Long userId = socialUserService.getBindUserId(UserTypeEnum.ADMIN.getValue(), reqVO.getType(), |
| 198 | 196 | reqVO.getCode(), reqVO.getState()); |
| ... | ... | @@ -207,25 +205,18 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 207 | 205 | } |
| 208 | 206 | |
| 209 | 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 | 210 | // 缓存登录用户到 Redis 中,返回 sessionId 编号 |
| 216 | 211 | return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); |
| 217 | 212 | } |
| 218 | 213 | |
| 219 | 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 | 220 | socialUserService.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO)); |
| 230 | 221 | |
| 231 | 222 | // 缓存登录用户到 Redis 中,返回 sessionId 编号 |
| ... | ... | @@ -240,12 +231,6 @@ public class AdminAuthServiceImpl implements AdminAuthService { |
| 240 | 231 | } |
| 241 | 232 | |
| 242 | 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 | 234 | public void logout(String token) { |
| 250 | 235 | // 查询用户信息 |
| 251 | 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 | 1 | package cn.iocoder.yudao.module.system.service.social; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.util.StrUtil; | |
| 4 | 3 | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| 5 | 4 | import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; |
| 6 | 5 | import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; |
| 7 | 6 | import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; |
| 8 | -import me.zhyd.oauth.model.AuthUser; | |
| 9 | 7 | |
| 10 | 8 | import javax.validation.Valid; |
| 11 | 9 | import javax.validation.constraints.NotNull; |
| ... | ... | @@ -28,7 +26,7 @@ public interface SocialUserService { |
| 28 | 26 | String getAuthorizeUrl(Integer type, String redirectUri); |
| 29 | 27 | |
| 30 | 28 | /** |
| 31 | - * 获得授权的用户 | |
| 29 | + * 授权获得对应的社交用户 | |
| 32 | 30 | * 如果授权失败,则会抛出 {@link ServiceException} 异常 |
| 33 | 31 | * |
| 34 | 32 | * @param type 社交平台的类型 {@link SocialTypeEnum} |
| ... | ... | @@ -37,17 +35,7 @@ public interface SocialUserService { |
| 37 | 35 | * @return 授权用户 |
| 38 | 36 | */ |
| 39 | 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 | 59 | * @param userId 用户编号 |
| 72 | 60 | * @param userType 全局用户类型 |
| 73 | 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 | 68 | * 注意,返回的是 MemberUser 或者 AdminUser 的 id 编号! |
| 91 | - * 该方法会执行和 {@link #checkSocialUser(Integer, String, String)} 一样的逻辑。 | |
| 92 | - * 所以在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常 | |
| 69 | + * 在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常 | |
| 93 | 70 | * |
| 94 | 71 | * @param userType 用户类型 |
| 95 | 72 | * @param type 社交平台的类型 | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.system.service.social; |
| 2 | 2 | |
| 3 | 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 | 5 | import cn.iocoder.yudao.framework.common.util.http.HttpUtils; |
| 6 | 6 | import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; |
| 7 | +import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO; | |
| 7 | 8 | import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; |
| 9 | +import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper; | |
| 8 | 10 | import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper; |
| 9 | -import cn.iocoder.yudao.module.system.dal.redis.social.SocialAuthUserRedisDAO; | |
| 10 | 11 | import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; |
| 11 | -import com.google.common.annotations.VisibleForTesting; | |
| 12 | 12 | import com.xkcoding.justauth.AuthRequestFactory; |
| 13 | 13 | import lombok.extern.slf4j.Slf4j; |
| 14 | 14 | import me.zhyd.oauth.model.AuthCallback; |
| ... | ... | @@ -21,10 +21,11 @@ import org.springframework.transaction.annotation.Transactional; |
| 21 | 21 | import org.springframework.validation.annotation.Validated; |
| 22 | 22 | |
| 23 | 23 | import javax.annotation.Resource; |
| 24 | +import java.util.Collections; | |
| 24 | 25 | import java.util.List; |
| 25 | -import java.util.Objects; | |
| 26 | 26 | |
| 27 | 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 | 29 | import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; |
| 29 | 30 | import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; |
| 30 | 31 | |
| ... | ... | @@ -42,8 +43,7 @@ public class SocialUserServiceImpl implements SocialUserService { |
| 42 | 43 | private AuthRequestFactory authRequestFactory; |
| 43 | 44 | |
| 44 | 45 | @Resource |
| 45 | - private SocialAuthUserRedisDAO authSocialUserRedisDAO; | |
| 46 | - | |
| 46 | + private SocialUserBindMapper socialUserBindMapper; | |
| 47 | 47 | @Resource |
| 48 | 48 | private SocialUserMapper socialUserMapper; |
| 49 | 49 | |
| ... | ... | @@ -57,171 +57,111 @@ public class SocialUserServiceImpl implements SocialUserService { |
| 57 | 57 | } |
| 58 | 58 | |
| 59 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 150 | * @param type 社交平台的类型 |
| 209 | - * @param authCallback 授权回调 | |
| 151 | + * @param code 授权码 | |
| 152 | + * @param state 授权 state | |
| 210 | 153 | * @return 授权的用户 |
| 211 | 154 | */ |
| 212 | - private AuthUser getAuthUser0(Integer type, AuthCallback authCallback) { | |
| 155 | + private AuthUser getAuthUser(Integer type, String code, String state) { | |
| 213 | 156 | AuthRequest authRequest = authRequestFactory.get(SocialTypeEnum.valueOfType(type).getSource()); |
| 157 | + AuthCallback authCallback = AuthCallback.builder().code(code).state(state).build(); | |
| 214 | 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 | 161 | if (!authResponse.ok()) { |
| 218 | 162 | throw exception(SOCIAL_USER_AUTH_FAILURE, authResponse.getMsg()); |
| 219 | 163 | } |
| 220 | 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 | 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 | 7 | import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; |
| 8 | +import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper; | |
| 4 | 9 | import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper; |
| 5 | -import cn.iocoder.yudao.module.system.dal.redis.social.SocialAuthUserRedisDAO; | |
| 6 | 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 | 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 | 15 | import me.zhyd.oauth.model.AuthUser; |
| 16 | +import me.zhyd.oauth.request.AuthRequest; | |
| 17 | +import me.zhyd.oauth.utils.AuthStateUtils; | |
| 11 | 18 | import org.junit.jupiter.api.Test; |
| 19 | +import org.mockito.MockedStatic; | |
| 12 | 20 | import org.springframework.boot.test.mock.mockito.MockBean; |
| 13 | 21 | import org.springframework.context.annotation.Import; |
| 14 | 22 | |
| 15 | 23 | import javax.annotation.Resource; |
| 16 | 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 | 27 | import static cn.hutool.core.util.RandomUtil.randomString; |
| 20 | 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 | 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 | 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 | 38 | public class SocialUserServiceTest extends BaseDbAndRedisUnitTest { |
| 27 | 39 | |
| 28 | 40 | @Resource |
| 29 | - private SocialUserServiceImpl socialService; | |
| 41 | + private SocialUserServiceImpl socialUserService; | |
| 30 | 42 | |
| 31 | 43 | @Resource |
| 32 | 44 | private SocialUserMapper socialUserMapper; |
| 45 | + @Resource | |
| 46 | + private SocialUserBindMapper socialUserBindMapper; | |
| 33 | 47 | |
| 34 | 48 | @MockBean |
| 35 | 49 | private AuthRequestFactory authRequestFactory; |
| 36 | 50 | |
| 37 | - /** | |
| 38 | - * 情况一,创建 SocialUserDO 的情况 | |
| 39 | - */ | |
| 40 | 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 | 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 | 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 | 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 | 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 | 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 | 149 | assertEquals(authUser.getToken().getAccessToken(), socialUser.getToken()); |
| 109 | 150 | assertEquals(toJsonString(authUser.getToken()), socialUser.getRawTokenInfo()); |
| 110 | 151 | assertEquals(authUser.getNickname(), socialUser.getNickname()); |
| 111 | 152 | assertEquals(authUser.getAvatar(), socialUser.getAvatar()); |
| 112 | 153 | assertEquals(toJsonString(authUser.getRawUserInfo()), socialUser.getRawUserInfo()); |
| 113 | - assertEquals(userId, socialUser.getUserId()); | |
| 114 | - assertEquals(UserTypeEnum.ADMIN.getValue(), socialUser.getUserType()); | |
| 115 | 154 | assertEquals(type, socialUser.getType()); |
| 116 | 155 | assertEquals(authUser.getUuid(), socialUser.getOpenid()); |
| 117 | - assertEquals(socialService.getAuthUserUnionId(authUser), socialUser.getUnionId()); | |
| 118 | 156 | } |
| 119 | 157 | |
| 120 | - /** | |
| 121 | - * 情况一,如果新老的 unionId 是一致的,无需解绑 | |
| 122 | - */ | |
| 123 | 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 | 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 "system_sms_template"; |
| 15 | 15 | DELETE FROM "system_sms_log"; |
| 16 | 16 | DELETE FROM "system_error_code"; |
| 17 | 17 | DELETE FROM "system_social_user"; |
| 18 | +DELETE FROM "system_social_user_bind"; | |
| 18 | 19 | DELETE FROM "system_tenant"; |
| 19 | 20 | DELETE FROM "system_tenant_package"; |
| 20 | 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 "system_error_code" ( |
| 378 | 378 | |
| 379 | 379 | CREATE TABLE IF NOT EXISTS "system_social_user" ( |
| 380 | 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 | 381 | "type" tinyint NOT NULL, |
| 384 | - "openid" varchar(32) NOT NULL, | |
| 382 | + "openid" varchar(64) NOT NULL, | |
| 385 | 383 | "token" varchar(256) DEFAULT NULL, |
| 386 | - "union_id" varchar(32) NOT NULL, | |
| 387 | 384 | "raw_token_info" varchar(1024) NOT NULL, |
| 388 | 385 | "nickname" varchar(32) NOT NULL, |
| 389 | 386 | "avatar" varchar(255) DEFAULT NULL, |
| 390 | 387 | "raw_user_info" varchar(1024) NOT NULL, |
| 388 | + "code" varchar(64) NOT NULL, | |
| 389 | + "state" varchar(64), | |
| 391 | 390 | "creator" varchar(64) DEFAULT '', |
| 392 | 391 | "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, |
| 393 | 392 | "updater" varchar(64) DEFAULT '', |
| ... | ... | @@ -396,6 +395,20 @@ CREATE TABLE IF NOT EXISTS "system_social_user" ( |
| 396 | 395 | PRIMARY KEY ("id") |
| 397 | 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 | 412 | CREATE TABLE IF NOT EXISTS "system_tenant" ( |
| 400 | 413 | "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, |
| 401 | 414 | "name" varchar(63) NOT NULL, | ... | ... |
yudao-server/src/main/resources/application-dev.yaml
| ... | ... | @@ -186,10 +186,6 @@ yudao: |
| 186 | 186 | justauth: |
| 187 | 187 | enabled: true |
| 188 | 188 | type: |
| 189 | - GITEE: # Gitee | |
| 190 | - client-id: ee61f0374a4c6c404a8717094caa7a410d76950e45ff60348015830c519ba5c1 | |
| 191 | - client-secret: 7c044a5671be3b051414db0cf2cec6ad702dd298d2416ba24ceaf608e6fa26f9 | |
| 192 | - ignore-check-redirect-uri: true | |
| 193 | 189 | DINGTALK: # 钉钉 |
| 194 | 190 | client-id: dingvrnreaje3yqvzhxg |
| 195 | 191 | client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI | ... | ... |
yudao-server/src/main/resources/application-local.yaml
| ... | ... | @@ -196,10 +196,6 @@ yudao: |
| 196 | 196 | justauth: |
| 197 | 197 | enabled: true |
| 198 | 198 | type: |
| 199 | - GITEE: # Gitee | |
| 200 | - client-id: ee61f0374a4c6c404a8717094caa7a410d76950e45ff60348015830c519ba5c1 | |
| 201 | - client-secret: 7c044a5671be3b051414db0cf2cec6ad702dd298d2416ba24ceaf608e6fa26f9 | |
| 202 | - ignore-check-redirect-uri: true | |
| 203 | 199 | DINGTALK: # 钉钉 |
| 204 | 200 | client-id: dingvrnreaje3yqvzhxg |
| 205 | 201 | client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI | ... | ... |
yudao-ui-admin/src/api/login.js
| ... | ... | @@ -9,7 +9,7 @@ export function login(username, password, code, uuid) { |
| 9 | 9 | uuid |
| 10 | 10 | } |
| 11 | 11 | return request({ |
| 12 | - url: '/system/login', | |
| 12 | + url: '/system/auth/login', | |
| 13 | 13 | method: 'post', |
| 14 | 14 | data: data |
| 15 | 15 | }) |
| ... | ... | @@ -18,7 +18,7 @@ export function login(username, password, code, uuid) { |
| 18 | 18 | // 获取用户详细信息 |
| 19 | 19 | export function getInfo() { |
| 20 | 20 | return request({ |
| 21 | - url: '/system/get-permission-info', | |
| 21 | + url: '/system/auth/get-permission-info', | |
| 22 | 22 | method: 'get' |
| 23 | 23 | }) |
| 24 | 24 | } |
| ... | ... | @@ -43,15 +43,15 @@ export function getCodeImg() { |
| 43 | 43 | // 社交授权的跳转 |
| 44 | 44 | export function socialAuthRedirect(type, redirectUri) { |
| 45 | 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 | 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 | 53 | return request({ |
| 54 | - url: '/system/social-login', | |
| 54 | + url: '/system/auth/social-quick-login', | |
| 55 | 55 | method: 'post', |
| 56 | 56 | data: { |
| 57 | 57 | type, |
| ... | ... | @@ -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 | 66 | return request({ |
| 67 | - url: '/system/social-login2', | |
| 67 | + url: '/system/auth/social-bind-login', | |
| 68 | 68 | method: 'post', |
| 69 | 69 | data: { |
| 70 | 70 | type, |
| ... | ... | @@ -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
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 | 2 | import { getToken, setToken, removeToken } from '@/utils/auth' |
| 3 | 3 | |
| 4 | 4 | const user = { |
| ... | ... | @@ -57,7 +57,7 @@ const user = { |
| 57 | 57 | const state = userInfo.state |
| 58 | 58 | const type = userInfo.type |
| 59 | 59 | return new Promise((resolve, reject) => { |
| 60 | - socialLogin(type, code, state).then(res => { | |
| 60 | + socialQuickLogin(type, code, state).then(res => { | |
| 61 | 61 | res = res.data; |
| 62 | 62 | setToken(res.token) |
| 63 | 63 | commit('SET_TOKEN', res.token) |
| ... | ... | @@ -76,7 +76,7 @@ const user = { |
| 76 | 76 | const username = userInfo.username.trim() |
| 77 | 77 | const password = userInfo.password |
| 78 | 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 | 80 | res = res.data; |
| 81 | 81 | setToken(res.token) |
| 82 | 82 | commit('SET_TOKEN', res.token) | ... | ... |
yudao-ui-admin/src/utils/constants.js
| ... | ... | @@ -71,12 +71,6 @@ export const InfraApiErrorLogProcessStatusEnum = { |
| 71 | 71 | * 用户的社交平台的类型枚举 |
| 72 | 72 | */ |
| 73 | 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 | 74 | DINGTALK: { |
| 81 | 75 | title: "钉钉", |
| 82 | 76 | type: 20, | ... | ... |
yudao-ui-admin/src/views/login.vue
yudao-ui-admin/src/views/system/user/profile/userSocial.vue
| ... | ... | @@ -7,7 +7,7 @@ |
| 7 | 7 | </el-table-column> |
| 8 | 8 | <el-table-column label="操作" align="left" > |
| 9 | 9 | <template slot-scope="scope"> |
| 10 | - <div v-if="scope.row.unionId"> | |
| 10 | + <div v-if="scope.row.openid"> | |
| 11 | 11 | 已绑定 |
| 12 | 12 | <el-button size="large" type="text" @click="unbind(scope.row)">(解绑)</el-button> |
| 13 | 13 | </div> |
| ... | ... | @@ -23,7 +23,8 @@ |
| 23 | 23 | <script> |
| 24 | 24 | |
| 25 | 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 | 29 | export default { |
| 29 | 30 | props: { |
| ... | ... | @@ -50,7 +51,7 @@ export default { |
| 50 | 51 | if (this.user.socialUsers) { |
| 51 | 52 | for (const j in this.user.socialUsers) { |
| 52 | 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 | 55 | break; |
| 55 | 56 | } |
| 56 | 57 | } |
| ... | ... | @@ -86,9 +87,9 @@ export default { |
| 86 | 87 | }); |
| 87 | 88 | }, |
| 88 | 89 | unbind(socialUser) { |
| 89 | - socialUnbind(socialUser.type, socialUser.unionId).then(resp => { | |
| 90 | + socialUnbind(socialUser.type, socialUser.openid).then(resp => { | |
| 90 | 91 | this.$modal.msgSuccess("解绑成功"); |
| 91 | - socialUser.unionId = undefined; | |
| 92 | + socialUser.openid = undefined; | |
| 92 | 93 | }); |
| 93 | 94 | }, |
| 94 | 95 | close() { | ... | ... |