Commit ea5fd5d9034685196f68f7da875308cdc329d99c
1 parent
8aed362e
feat(system): 升级钉钉 V2 社交登录并校验用户标识
Showing
5 changed files
with
55 additions
and
5 deletions
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/social/SocialTypeEnum.java
| @@ -25,9 +25,11 @@ public enum SocialTypeEnum implements ArrayValuable<Integer> { | @@ -25,9 +25,11 @@ public enum SocialTypeEnum implements ArrayValuable<Integer> { | ||
| 25 | /** | 25 | /** |
| 26 | * 钉钉 | 26 | * 钉钉 |
| 27 | * | 27 | * |
| 28 | - * @see <a href="https://developers.dingtalk.com/document/app/obtain-identity-credentials">接入文档</a> | 28 | + * 使用新版登录,对应 JustAuth 的 DINGTALK_V2,相关反馈:https://t.zsxq.com/QY7xI |
| 29 | + * | ||
| 30 | + * @see <a href="https://open.dingtalk.com/document/app/obtain-identity-credentials">接入文档</a> | ||
| 29 | */ | 31 | */ |
| 30 | - DINGTALK(20, "DINGTALK"), | 32 | + DINGTALK(20, "DINGTALK_V2"), |
| 31 | 33 | ||
| 32 | /** | 34 | /** |
| 33 | * 企业微信 | 35 | * 企业微信 |
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java
| @@ -16,6 +16,7 @@ import cn.hutool.core.thread.ThreadUtil; | @@ -16,6 +16,7 @@ import cn.hutool.core.thread.ThreadUtil; | ||
| 16 | import cn.hutool.core.util.DesensitizedUtil; | 16 | import cn.hutool.core.util.DesensitizedUtil; |
| 17 | import cn.hutool.core.util.ObjUtil; | 17 | import cn.hutool.core.util.ObjUtil; |
| 18 | import cn.hutool.core.util.ReflectUtil; | 18 | import cn.hutool.core.util.ReflectUtil; |
| 19 | +import cn.hutool.core.util.StrUtil; | ||
| 19 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 20 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 20 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | 21 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 21 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 22 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| @@ -187,7 +188,12 @@ public class SocialClientServiceImpl implements SocialClientService { | @@ -187,7 +188,12 @@ public class SocialClientServiceImpl implements SocialClientService { | ||
| 187 | if (!authResponse.ok()) { | 188 | if (!authResponse.ok()) { |
| 188 | throw exception(SOCIAL_USER_AUTH_FAILURE, authResponse.getMsg()); | 189 | throw exception(SOCIAL_USER_AUTH_FAILURE, authResponse.getMsg()); |
| 189 | } | 190 | } |
| 190 | - return (AuthUser) authResponse.getData(); | 191 | + // 部分平台获取用户信息失败时,仍然返回成功,但没有用户标识。例如说,钉钉新版登录缺少权限时,接口返回 403,但 JustAuth 不校验 |
| 192 | + AuthUser authUser = (AuthUser) authResponse.getData(); | ||
| 193 | + if (authUser == null || StrUtil.isBlank(authUser.getUuid())) { | ||
| 194 | + throw exception(SOCIAL_USER_AUTH_FAILURE, "未获取到用户标识"); | ||
| 195 | + } | ||
| 196 | + return authUser; | ||
| 191 | } | 197 | } |
| 192 | 198 | ||
| 193 | /** | 199 | /** |
yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImplTest.java
| @@ -146,6 +146,48 @@ public class SocialClientServiceImplTest extends BaseDbUnitTest { | @@ -146,6 +146,48 @@ public class SocialClientServiceImplTest extends BaseDbUnitTest { | ||
| 146 | } | 146 | } |
| 147 | 147 | ||
| 148 | @Test | 148 | @Test |
| 149 | + public void testAuthSocialUser_uuidNull() { | ||
| 150 | + // 准备参数 | ||
| 151 | + Integer socialType = SocialTypeEnum.DINGTALK.getType(); | ||
| 152 | + Integer userType = randomPojo(UserTypeEnum.class).getValue(); | ||
| 153 | + String code = randomString(); | ||
| 154 | + String state = randomString(); | ||
| 155 | + // mock 方法(AuthRequest) | ||
| 156 | + AuthRequest authRequest = mock(AuthRequest.class); | ||
| 157 | + when(authRequestFactory.get(eq("DINGTALK_V2"))).thenReturn(authRequest); | ||
| 158 | + // mock 方法(AuthResponse):获取用户信息失败时,JustAuth 仍然返回成功,但 uuid 为空 | ||
| 159 | + AuthUser authUser = randomPojo(AuthUser.class, o -> o.setUuid(null)); | ||
| 160 | + AuthResponse<AuthUser> authResponse = new AuthResponse<>(2000, null, authUser); | ||
| 161 | + when(authRequest.login(any())).thenReturn(authResponse); | ||
| 162 | + | ||
| 163 | + // 调用并断言 | ||
| 164 | + assertServiceException( | ||
| 165 | + () -> socialClientService.getAuthUser(socialType, userType, code, state), | ||
| 166 | + SOCIAL_USER_AUTH_FAILURE, "未获取到用户标识"); | ||
| 167 | + } | ||
| 168 | + | ||
| 169 | + @Test | ||
| 170 | + public void testAuthSocialUser_uuidBlank() { | ||
| 171 | + // 准备参数 | ||
| 172 | + Integer socialType = SocialTypeEnum.DINGTALK.getType(); | ||
| 173 | + Integer userType = randomPojo(UserTypeEnum.class).getValue(); | ||
| 174 | + String code = randomString(); | ||
| 175 | + String state = randomString(); | ||
| 176 | + // mock 方法(AuthRequest) | ||
| 177 | + AuthRequest authRequest = mock(AuthRequest.class); | ||
| 178 | + when(authRequestFactory.get(eq("DINGTALK_V2"))).thenReturn(authRequest); | ||
| 179 | + // mock 方法(AuthResponse) | ||
| 180 | + AuthUser authUser = randomPojo(AuthUser.class, o -> o.setUuid(" ")); | ||
| 181 | + AuthResponse<AuthUser> authResponse = new AuthResponse<>(2000, null, authUser); | ||
| 182 | + when(authRequest.login(any())).thenReturn(authResponse); | ||
| 183 | + | ||
| 184 | + // 调用并断言 | ||
| 185 | + assertServiceException( | ||
| 186 | + () -> socialClientService.getAuthUser(socialType, userType, code, state), | ||
| 187 | + SOCIAL_USER_AUTH_FAILURE, "未获取到用户标识"); | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + @Test | ||
| 149 | public void testBuildAuthRequest_clientNull() { | 191 | public void testBuildAuthRequest_clientNull() { |
| 150 | // 准备参数 | 192 | // 准备参数 |
| 151 | Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); | 193 | Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); |
yudao-server/src/main/resources/application-dev.yaml
| @@ -181,7 +181,7 @@ yudao: | @@ -181,7 +181,7 @@ yudao: | ||
| 181 | justauth: | 181 | justauth: |
| 182 | enabled: true | 182 | enabled: true |
| 183 | type: | 183 | type: |
| 184 | - DINGTALK: # 钉钉 | 184 | + DINGTALK_V2: # 钉钉(新版登录) |
| 185 | client-id: dingvrnreaje3yqvzhxg | 185 | client-id: dingvrnreaje3yqvzhxg |
| 186 | client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI | 186 | client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI |
| 187 | ignore-check-redirect-uri: true | 187 | ignore-check-redirect-uri: true |
yudao-server/src/main/resources/application-local.yaml
| @@ -254,7 +254,7 @@ yudao: | @@ -254,7 +254,7 @@ yudao: | ||
| 254 | justauth: | 254 | justauth: |
| 255 | enabled: true | 255 | enabled: true |
| 256 | type: | 256 | type: |
| 257 | - DINGTALK: # 钉钉 | 257 | + DINGTALK_V2: # 钉钉(新版登录) |
| 258 | client-id: dingvrnreaje3yqvzhxg | 258 | client-id: dingvrnreaje3yqvzhxg |
| 259 | client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI | 259 | client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI |
| 260 | ignore-check-redirect-uri: true | 260 | ignore-check-redirect-uri: true |