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 | 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 | 16 | import cn.hutool.core.util.DesensitizedUtil; |
| 17 | 17 | import cn.hutool.core.util.ObjUtil; |
| 18 | 18 | import cn.hutool.core.util.ReflectUtil; |
| 19 | +import cn.hutool.core.util.StrUtil; | |
| 19 | 20 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 20 | 21 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 21 | 22 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| ... | ... | @@ -187,7 +188,12 @@ public class SocialClientServiceImpl implements SocialClientService { |
| 187 | 188 | if (!authResponse.ok()) { |
| 188 | 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 | 146 | } |
| 147 | 147 | |
| 148 | 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 | 191 | public void testBuildAuthRequest_clientNull() { |
| 150 | 192 | // 准备参数 |
| 151 | 193 | Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); | ... | ... |
yudao-server/src/main/resources/application-dev.yaml
| ... | ... | @@ -181,7 +181,7 @@ yudao: |
| 181 | 181 | justauth: |
| 182 | 182 | enabled: true |
| 183 | 183 | type: |
| 184 | - DINGTALK: # 钉钉 | |
| 184 | + DINGTALK_V2: # 钉钉(新版登录) | |
| 185 | 185 | client-id: dingvrnreaje3yqvzhxg |
| 186 | 186 | client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI |
| 187 | 187 | ignore-check-redirect-uri: true | ... | ... |
yudao-server/src/main/resources/application-local.yaml
| ... | ... | @@ -254,7 +254,7 @@ yudao: |
| 254 | 254 | justauth: |
| 255 | 255 | enabled: true |
| 256 | 256 | type: |
| 257 | - DINGTALK: # 钉钉 | |
| 257 | + DINGTALK_V2: # 钉钉(新版登录) | |
| 258 | 258 | client-id: dingvrnreaje3yqvzhxg |
| 259 | 259 | client-secret: i8E6iZyDvZj51JIb0tYsYfVQYOks9Cq1lgryEjFRqC79P3iJcrxEwT6Qk2QvLrLI |
| 260 | 260 | ignore-check-redirect-uri: true | ... | ... |