Commit 495f121463febd9a0c7ce280444757277d090f0b
1 parent
f87c4f75
完善 OAuth2OpenControllerTest 单元测试
Showing
6 changed files
with
404 additions
and
68 deletions
yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/RandomUtils.java
| ... | ... | @@ -45,7 +45,7 @@ public class RandomUtils { |
| 45 | 45 | } |
| 46 | 46 | // 如果是 type、status 结尾的字段,返回 tinyint 范围 |
| 47 | 47 | if (StrUtil.endWithAnyIgnoreCase(attributeMetadata.getAttributeName(), |
| 48 | - "type", "status", "category")) { | |
| 48 | + "type", "status", "category", "scope")) { | |
| 49 | 49 | return RandomUtil.randomInt(0, TINYINT_MAX + 1); |
| 50 | 50 | } |
| 51 | 51 | return RandomUtil.randomInt(); | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java
| ... | ... | @@ -52,8 +52,6 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti |
| 52 | 52 | * 考虑到【本系统】暂时不想做的过于复杂,默认只有获取到 access token 之后,可以访问【本系统】管理后台的 /system-api/* 所有接口,除非手动添加 scope 控制。 |
| 53 | 53 | * scope 的使用示例,可见 {@link OAuth2UserController} 类 |
| 54 | 54 | * |
| 55 | - * | |
| 56 | - * | |
| 57 | 55 | * @author 芋道源码 |
| 58 | 56 | */ |
| 59 | 57 | @Api(tags = "管理后台 - OAuth2.0 授权") |
| ... | ... | @@ -185,8 +183,7 @@ public class OAuth2OpenController { |
| 185 | 183 | // 0. 校验用户已经登录。通过 Spring Security 实现 |
| 186 | 184 | |
| 187 | 185 | // 1. 获得 Client 客户端的信息 |
| 188 | - OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId, null, | |
| 189 | - null, null, null); | |
| 186 | + OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId); | |
| 190 | 187 | // 2. 获得用户已经授权的信息 |
| 191 | 188 | List<OAuth2ApproveDO> approves = oauth2ApproveService.getApproveList(getLoginUserId(), getUserType(), clientId); |
| 192 | 189 | // 拼接返回 |
| ... | ... | @@ -223,7 +220,6 @@ public class OAuth2OpenController { |
| 223 | 220 | @SuppressWarnings("unchecked") |
| 224 | 221 | Map<String, Boolean> scopes = JsonUtils.parseObject(scope, Map.class); |
| 225 | 222 | scopes = ObjectUtil.defaultIfNull(scopes, Collections.emptyMap()); |
| 226 | - // TODO 芋艿:针对 approved + scopes 在看看 spring security 的实现 | |
| 227 | 223 | // 0. 校验用户已经登录。通过 Spring Security 实现 |
| 228 | 224 | |
| 229 | 225 | // 1.1 校验 responseType 是否满足 code 或者 token 值 |
| ... | ... | @@ -262,7 +258,7 @@ public class OAuth2OpenController { |
| 262 | 258 | if (StrUtil.equalsAny(responseType, "token")) { |
| 263 | 259 | return OAuth2GrantTypeEnum.IMPLICIT; |
| 264 | 260 | } |
| 265 | - throw exception0(BAD_REQUEST.getCode(), "response_type 参数值允许 code 和 token"); | |
| 261 | + throw exception0(BAD_REQUEST.getCode(), "response_type 参数值只允许 code 和 token"); | |
| 266 | 262 | } |
| 267 | 263 | |
| 268 | 264 | private String getImplicitGrantRedirect(Long userId, OAuth2ClientDO client, |
| ... | ... | @@ -279,7 +275,7 @@ public class OAuth2OpenController { |
| 279 | 275 | private String getAuthorizationCodeRedirect(Long userId, OAuth2ClientDO client, |
| 280 | 276 | List<String> scopes, String redirectUri, String state) { |
| 281 | 277 | // 1. 创建 code 授权码 |
| 282 | - String authorizationCode = oauth2GrantService.grantAuthorizationCodeForCode(userId,getUserType(), client.getClientId(), scopes, | |
| 278 | + String authorizationCode = oauth2GrantService.grantAuthorizationCodeForCode(userId, getUserType(), client.getClientId(), scopes, | |
| 283 | 279 | redirectUri, state); |
| 284 | 280 | // 2. 拼接重定向的 URL |
| 285 | 281 | return OAuth2Utils.buildAuthorizationCodeRedirectUri(redirectUri, authorizationCode, state); | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenCheckTokenRespVO.java
| ... | ... | @@ -7,7 +7,7 @@ import lombok.AllArgsConstructor; |
| 7 | 7 | import lombok.Data; |
| 8 | 8 | import lombok.NoArgsConstructor; |
| 9 | 9 | |
| 10 | -import java.util.Set; | |
| 10 | +import java.util.List; | |
| 11 | 11 | |
| 12 | 12 | @ApiModel("管理后台 - 【开放接口】校验令牌 Response VO") |
| 13 | 13 | @Data |
| ... | ... | @@ -28,13 +28,13 @@ public class OAuth2OpenCheckTokenRespVO { |
| 28 | 28 | @ApiModelProperty(value = "客户端编号", required = true, example = "car") |
| 29 | 29 | private String clientId; |
| 30 | 30 | @ApiModelProperty(value = "授权范围", required = true, example = "user_info") |
| 31 | - private Set<String> scopes; | |
| 31 | + private List<String> scopes; | |
| 32 | 32 | |
| 33 | 33 | @ApiModelProperty(value = "访问令牌", required = true, example = "tudou") |
| 34 | 34 | @JsonProperty("access_token") |
| 35 | 35 | private String accessToken; |
| 36 | 36 | |
| 37 | 37 | @ApiModelProperty(value = "过期时间", required = true, example = "1593092157", notes = "时间戳 / 1000,即单位:秒") |
| 38 | - @JsonProperty("exp") | |
| 39 | 38 | private Long exp; |
| 39 | + | |
| 40 | 40 | } | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java
| ... | ... | @@ -61,11 +61,13 @@ public class OAuth2Utils { |
| 61 | 61 | if (CollUtil.isNotEmpty(scopes)) { |
| 62 | 62 | vars.put("scope", buildScopeStr(scopes)); |
| 63 | 63 | } |
| 64 | - for (String key : additionalInformation.keySet()) { | |
| 65 | - Object value = additionalInformation.get(key); | |
| 66 | - if (value != null) { | |
| 67 | - keys.put("extra_" + key, key); | |
| 68 | - vars.put("extra_" + key, value); | |
| 64 | + if (CollUtil.isNotEmpty(additionalInformation)) { | |
| 65 | + for (String key : additionalInformation.keySet()) { | |
| 66 | + Object value = additionalInformation.get(key); | |
| 67 | + if (value != null) { | |
| 68 | + keys.put("extra_" + key, key); | |
| 69 | + vars.put("extra_" + key, value); | |
| 70 | + } | |
| 69 | 71 | } |
| 70 | 72 | } |
| 71 | 73 | // Do not include the refresh token (even if there is one) | ... | ... |
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenControllerTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.controller.admin.oauth2; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.ListUtil; | |
| 4 | +import cn.hutool.core.map.MapUtil; | |
| 5 | +import cn.iocoder.yudao.framework.common.core.KeyValue; | |
| 6 | +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | |
| 7 | +import cn.iocoder.yudao.framework.common.exception.ErrorCode; | |
| 8 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 9 | +import cn.iocoder.yudao.framework.common.util.collection.SetUtils; | |
| 10 | +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | |
| 11 | +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAccessTokenRespVO; | |
| 12 | +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAuthorizeInfoRespVO; | |
| 13 | +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenCheckTokenRespVO; | |
| 14 | +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; | |
| 15 | +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; | |
| 16 | +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; | |
| 17 | +import cn.iocoder.yudao.module.system.enums.auth.OAuth2GrantTypeEnum; | |
| 18 | +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ApproveService; | |
| 19 | +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientService; | |
| 20 | +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2GrantService; | |
| 21 | +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; | |
| 22 | +import org.assertj.core.util.Lists; | |
| 23 | +import org.junit.jupiter.api.Test; | |
| 24 | +import org.mockito.InjectMocks; | |
| 25 | +import org.mockito.Mock; | |
| 26 | + | |
| 27 | +import javax.servlet.http.HttpServletRequest; | |
| 28 | +import java.time.Duration; | |
| 29 | +import java.util.ArrayList; | |
| 30 | +import java.util.Date; | |
| 31 | +import java.util.LinkedHashMap; | |
| 32 | +import java.util.List; | |
| 33 | + | |
| 34 | +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; | |
| 35 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime; | |
| 36 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | |
| 37 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | |
| 38 | +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | |
| 39 | +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; | |
| 40 | +import static java.util.Arrays.asList; | |
| 41 | +import static org.junit.jupiter.api.Assertions.*; | |
| 42 | +import static org.mockito.ArgumentMatchers.eq; | |
| 43 | +import static org.mockito.ArgumentMatchers.isNull; | |
| 44 | +import static org.mockito.Mockito.mock; | |
| 45 | +import static org.mockito.Mockito.when; | |
| 46 | + | |
| 47 | +/** | |
| 48 | + * {@link OAuth2OpenController} 的单元测试 | |
| 49 | + * | |
| 50 | + * @author 芋道源码 | |
| 51 | + */ | |
| 52 | +public class OAuth2OpenControllerTest extends BaseMockitoUnitTest { | |
| 53 | + | |
| 54 | + @InjectMocks | |
| 55 | + private OAuth2OpenController oauth2OpenController; | |
| 56 | + | |
| 57 | + @Mock | |
| 58 | + private OAuth2GrantService oauth2GrantService; | |
| 59 | + @Mock | |
| 60 | + private OAuth2ClientService oauth2ClientService; | |
| 61 | + @Mock | |
| 62 | + private OAuth2ApproveService oauth2ApproveService; | |
| 63 | + @Mock | |
| 64 | + private OAuth2TokenService oauth2TokenService; | |
| 65 | + | |
| 66 | + @Test | |
| 67 | + public void testPostAccessToken_authorizationCode() { | |
| 68 | + // 准备参数 | |
| 69 | + String granType = OAuth2GrantTypeEnum.AUTHORIZATION_CODE.getGrantType(); | |
| 70 | + String code = randomString(); | |
| 71 | + String redirectUri = randomString(); | |
| 72 | + String state = randomString(); | |
| 73 | + HttpServletRequest request = mockRequest("test_client_id", "test_client_secret"); | |
| 74 | + // mock 方法(client) | |
| 75 | + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("test_client_id"); | |
| 76 | + when(oauth2ClientService.validOAuthClientFromCache(eq("test_client_id"), eq("test_client_secret"), eq(granType), eq(new ArrayList<>()), eq(redirectUri))).thenReturn(client); | |
| 77 | + | |
| 78 | + // mock 方法(访问令牌) | |
| 79 | + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) | |
| 80 | + .setExpiresTime(addTime(Duration.ofMillis(30010L))); // 多给 10 毫秒,保证可执行完 | |
| 81 | + when(oauth2GrantService.grantAuthorizationCodeForAccessToken(eq("test_client_id"), | |
| 82 | + eq(code), eq(redirectUri), eq(state))).thenReturn(accessTokenDO); | |
| 83 | + | |
| 84 | + // 调用 | |
| 85 | + CommonResult<OAuth2OpenAccessTokenRespVO> result = oauth2OpenController.postAccessToken(request, granType, | |
| 86 | + code, redirectUri, state, null, null, null, null); | |
| 87 | + // 断言 | |
| 88 | + assertEquals(0, result.getCode()); | |
| 89 | + assertPojoEquals(accessTokenDO, result.getData()); | |
| 90 | + assertEquals(30L, result.getData().getExpiresIn()); // 执行过程会过去几毫秒 | |
| 91 | + } | |
| 92 | + | |
| 93 | + @Test | |
| 94 | + public void testPostAccessToken_password() { | |
| 95 | + // 准备参数 | |
| 96 | + String granType = OAuth2GrantTypeEnum.PASSWORD.getGrantType(); | |
| 97 | + String username = randomString(); | |
| 98 | + String password = randomString(); | |
| 99 | + String scope = "write read"; | |
| 100 | + HttpServletRequest request = mockRequest("test_client_id", "test_client_secret"); | |
| 101 | + // mock 方法(client) | |
| 102 | + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("test_client_id"); | |
| 103 | + when(oauth2ClientService.validOAuthClientFromCache(eq("test_client_id"), eq("test_client_secret"), | |
| 104 | + eq(granType), eq(Lists.newArrayList("write", "read")), isNull())).thenReturn(client); | |
| 105 | + | |
| 106 | + // mock 方法(访问令牌) | |
| 107 | + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) | |
| 108 | + .setExpiresTime(addTime(Duration.ofMillis(30010L))); // 多给 10 毫秒,保证可执行完 | |
| 109 | + when(oauth2GrantService.grantPassword(eq(username), eq(password), eq("test_client_id"), | |
| 110 | + eq(Lists.newArrayList("write", "read")))).thenReturn(accessTokenDO); | |
| 111 | + | |
| 112 | + // 调用 | |
| 113 | + CommonResult<OAuth2OpenAccessTokenRespVO> result = oauth2OpenController.postAccessToken(request, granType, | |
| 114 | + null, null, null, username, password, scope, null); | |
| 115 | + // 断言 | |
| 116 | + assertEquals(0, result.getCode()); | |
| 117 | + assertPojoEquals(accessTokenDO, result.getData()); | |
| 118 | + assertEquals(30L, result.getData().getExpiresIn()); // 执行过程会过去几毫秒 | |
| 119 | + } | |
| 120 | + | |
| 121 | + @Test | |
| 122 | + public void testPostAccessToken_refreshToken() { | |
| 123 | + // 准备参数 | |
| 124 | + String granType = OAuth2GrantTypeEnum.REFRESH_TOKEN.getGrantType(); | |
| 125 | + String refreshToken = randomString(); | |
| 126 | + String password = randomString(); | |
| 127 | + HttpServletRequest request = mockRequest("test_client_id", "test_client_secret"); | |
| 128 | + // mock 方法(client) | |
| 129 | + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("test_client_id"); | |
| 130 | + when(oauth2ClientService.validOAuthClientFromCache(eq("test_client_id"), eq("test_client_secret"), | |
| 131 | + eq(granType), eq(Lists.newArrayList()), isNull())).thenReturn(client); | |
| 132 | + | |
| 133 | + // mock 方法(访问令牌) | |
| 134 | + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) | |
| 135 | + .setExpiresTime(addTime(Duration.ofMillis(30010L))); // 多给 10 毫秒,保证可执行完 | |
| 136 | + when(oauth2GrantService.grantRefreshToken(eq(refreshToken), eq("test_client_id"))).thenReturn(accessTokenDO); | |
| 137 | + | |
| 138 | + // 调用 | |
| 139 | + CommonResult<OAuth2OpenAccessTokenRespVO> result = oauth2OpenController.postAccessToken(request, granType, | |
| 140 | + null, null, null, null, password, null, refreshToken); | |
| 141 | + // 断言 | |
| 142 | + assertEquals(0, result.getCode()); | |
| 143 | + assertPojoEquals(accessTokenDO, result.getData()); | |
| 144 | + assertEquals(30L, result.getData().getExpiresIn()); // 执行过程会过去几毫秒 | |
| 145 | + } | |
| 146 | + | |
| 147 | + @Test | |
| 148 | + public void testPostAccessToken_implicit() { | |
| 149 | + // 调用,并断言 | |
| 150 | + assertServiceException(() -> oauth2OpenController.postAccessToken(null, | |
| 151 | + OAuth2GrantTypeEnum.IMPLICIT.getGrantType(), null, null, null, | |
| 152 | + null, null, null, null), | |
| 153 | + new ErrorCode(400, "Token 接口不支持 implicit 授权模式")); | |
| 154 | + } | |
| 155 | + | |
| 156 | + @Test | |
| 157 | + public void testRevokeToken() { | |
| 158 | + // 准备参数 | |
| 159 | + HttpServletRequest request = mockRequest("demo_client_id", "demo_client_secret"); | |
| 160 | + String token = randomString(); | |
| 161 | + // mock 方法(client) | |
| 162 | + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("demo_client_id"); | |
| 163 | + when(oauth2ClientService.validOAuthClientFromCache(eq("demo_client_id"), | |
| 164 | + eq("demo_client_secret"), isNull(), isNull(), isNull())).thenReturn(client); | |
| 165 | + // mock 方法(移除) | |
| 166 | + when(oauth2GrantService.revokeToken(eq("demo_client_id"), eq(token))).thenReturn(true); | |
| 167 | + | |
| 168 | + // 调用 | |
| 169 | + CommonResult<Boolean> result = oauth2OpenController.revokeToken(request, token); | |
| 170 | + // 断言 | |
| 171 | + assertEquals(0, result.getCode()); | |
| 172 | + assertTrue(result.getData()); | |
| 173 | + } | |
| 174 | + | |
| 175 | + @Test | |
| 176 | + public void testCheckToken() { | |
| 177 | + // 准备参数 | |
| 178 | + HttpServletRequest request = mockRequest("demo_client_id", "demo_client_secret"); | |
| 179 | + String token = randomString(); | |
| 180 | + // mock 方法 | |
| 181 | + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class).setUserType(UserTypeEnum.ADMIN.getValue()).setExpiresTime(new Date(1653485731195L)); | |
| 182 | + when(oauth2TokenService.checkAccessToken(eq(token))).thenReturn(accessTokenDO); | |
| 183 | + | |
| 184 | + // 调用 | |
| 185 | + CommonResult<OAuth2OpenCheckTokenRespVO> result = oauth2OpenController.checkToken(request, token); | |
| 186 | + // 断言 | |
| 187 | + assertEquals(0, result.getCode()); | |
| 188 | + assertPojoEquals(accessTokenDO, result.getData()); | |
| 189 | + assertEquals(1653485731L, result.getData().getExp()); // 执行过程会过去几毫秒 | |
| 190 | + } | |
| 191 | + | |
| 192 | + @Test | |
| 193 | + public void testAuthorize() { | |
| 194 | + // 准备参数 | |
| 195 | + String clientId = randomString(); | |
| 196 | + // mock 方法(client) | |
| 197 | + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("demo_client_id").setScopes(ListUtil.toList("read", "write", "all")); | |
| 198 | + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(client); | |
| 199 | + // mock 方法(approve) | |
| 200 | + List<OAuth2ApproveDO> approves = asList( | |
| 201 | + randomPojo(OAuth2ApproveDO.class).setScope("read").setApproved(true), | |
| 202 | + randomPojo(OAuth2ApproveDO.class).setScope("write").setApproved(false)); | |
| 203 | + when(oauth2ApproveService.getApproveList(isNull(), eq(UserTypeEnum.ADMIN.getValue()), eq(clientId))).thenReturn(approves); | |
| 204 | + | |
| 205 | + // 调用 | |
| 206 | + CommonResult<OAuth2OpenAuthorizeInfoRespVO> result = oauth2OpenController.authorize(clientId); | |
| 207 | + // 断言 | |
| 208 | + assertEquals(0, result.getCode()); | |
| 209 | + assertPojoEquals(client, result.getData().getClient()); | |
| 210 | + assertEquals(new KeyValue<>("read", true), result.getData().getScopes().get(0)); | |
| 211 | + assertEquals(new KeyValue<>("write", false), result.getData().getScopes().get(1)); | |
| 212 | + assertEquals(new KeyValue<>("all", false), result.getData().getScopes().get(2)); | |
| 213 | + } | |
| 214 | + | |
| 215 | + @Test | |
| 216 | + public void testApproveOrDeny_grantTypeError() { | |
| 217 | + // 调用,并断言 | |
| 218 | + assertServiceException(() -> oauth2OpenController.approveOrDeny(randomString(), null, | |
| 219 | + null, null, null, null), | |
| 220 | + new ErrorCode(400, "response_type 参数值只允许 code 和 token")); | |
| 221 | + } | |
| 222 | + | |
| 223 | + @Test // autoApprove = true,但是不通过 | |
| 224 | + public void testApproveOrDeny_autoApproveNo() { | |
| 225 | + // 准备参数 | |
| 226 | + String responseType = "code"; | |
| 227 | + String clientId = randomString(); | |
| 228 | + String scope = "{\"read\": true, \"write\": false}"; | |
| 229 | + String redirectUri = randomString(); | |
| 230 | + String state = randomString(); | |
| 231 | + // mock 方法 | |
| 232 | + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class); | |
| 233 | + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("authorization_code"), | |
| 234 | + eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client); | |
| 235 | + | |
| 236 | + // 调用 | |
| 237 | + CommonResult<String> result = oauth2OpenController.approveOrDeny(responseType, clientId, | |
| 238 | + scope, redirectUri, true, state); | |
| 239 | + // 断言 | |
| 240 | + assertEquals(0, result.getCode()); | |
| 241 | + assertNull(result.getData()); | |
| 242 | + } | |
| 243 | + | |
| 244 | + @Test // autoApprove = false,但是不通过 | |
| 245 | + public void testApproveOrDeny_ApproveNo() { | |
| 246 | + // 准备参数 | |
| 247 | + String responseType = "token"; | |
| 248 | + String clientId = randomString(); | |
| 249 | + String scope = "{\"read\": true, \"write\": false}"; | |
| 250 | + String redirectUri = "https://www.iocoder.cn"; | |
| 251 | + String state = "test"; | |
| 252 | + // mock 方法 | |
| 253 | + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class); | |
| 254 | + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("implicit"), | |
| 255 | + eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client); | |
| 256 | + | |
| 257 | + // 调用 | |
| 258 | + CommonResult<String> result = oauth2OpenController.approveOrDeny(responseType, clientId, | |
| 259 | + scope, redirectUri, false, state); | |
| 260 | + // 断言 | |
| 261 | + assertEquals(0, result.getCode()); | |
| 262 | + assertEquals("https://www.iocoder.cn#error=access_denied&error_description=User%20denied%20access&state=test", result.getData()); | |
| 263 | + } | |
| 264 | + | |
| 265 | + @Test // autoApprove = true,通过 + token | |
| 266 | + public void testApproveOrDeny_autoApproveWithToken() { | |
| 267 | + // 准备参数 | |
| 268 | + String responseType = "token"; | |
| 269 | + String clientId = randomString(); | |
| 270 | + String scope = "{\"read\": true, \"write\": false}"; | |
| 271 | + String redirectUri = "https://www.iocoder.cn"; | |
| 272 | + String state = "test"; | |
| 273 | + // mock 方法(client) | |
| 274 | + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId(clientId).setAdditionalInformation(null); | |
| 275 | + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("implicit"), | |
| 276 | + eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client); | |
| 277 | + // mock 方法(场景一) | |
| 278 | + when(oauth2ApproveService.checkForPreApproval(isNull(), eq(UserTypeEnum.ADMIN.getValue()), | |
| 279 | + eq(clientId), eq(SetUtils.asSet("read", "write")))).thenReturn(true); | |
| 280 | + // mock 方法(访问令牌) | |
| 281 | + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) | |
| 282 | + .setAccessToken("test_access_token").setExpiresTime(addTime(Duration.ofMillis(30010L))); | |
| 283 | + when(oauth2GrantService.grantImplicit(isNull(), eq(UserTypeEnum.ADMIN.getValue()), | |
| 284 | + eq(clientId), eq(ListUtil.toList("read")))).thenReturn(accessTokenDO); | |
| 285 | + | |
| 286 | + // 调用 | |
| 287 | + CommonResult<String> result = oauth2OpenController.approveOrDeny(responseType, clientId, | |
| 288 | + scope, redirectUri, true, state); | |
| 289 | + // 断言 | |
| 290 | + assertEquals(0, result.getCode()); | |
| 291 | + assertEquals("https://www.iocoder.cn#access_token=test_access_token&token_type=bearer&state=test&expires_in=30&scope=read", result.getData()); | |
| 292 | + } | |
| 293 | + | |
| 294 | + @Test // autoApprove = false,通过 + code | |
| 295 | + public void testApproveOrDeny_approveWithCode() { | |
| 296 | + // 准备参数 | |
| 297 | + String responseType = "code"; | |
| 298 | + String clientId = randomString(); | |
| 299 | + String scope = "{\"read\": true, \"write\": false}"; | |
| 300 | + String redirectUri = "https://www.iocoder.cn"; | |
| 301 | + String state = "test"; | |
| 302 | + // mock 方法(client) | |
| 303 | + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId(clientId).setAdditionalInformation(null); | |
| 304 | + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("authorization_code"), | |
| 305 | + eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client); | |
| 306 | + // mock 方法(场景二) | |
| 307 | + when(oauth2ApproveService.updateAfterApproval(isNull(), eq(UserTypeEnum.ADMIN.getValue()), eq(clientId), | |
| 308 | + eq(MapUtil.builder(new LinkedHashMap<String, Boolean>()).put("read", true).put("write", false).build()))) | |
| 309 | + .thenReturn(true); | |
| 310 | + // mock 方法(访问令牌) | |
| 311 | + String authorizationCode = "test_code"; | |
| 312 | + when(oauth2GrantService.grantAuthorizationCodeForCode(isNull(), eq(UserTypeEnum.ADMIN.getValue()), | |
| 313 | + eq(clientId), eq(ListUtil.toList("read")), eq(redirectUri), eq(state))).thenReturn(authorizationCode); | |
| 314 | + | |
| 315 | + // 调用 | |
| 316 | + CommonResult<String> result = oauth2OpenController.approveOrDeny(responseType, clientId, | |
| 317 | + scope, redirectUri, false, state); | |
| 318 | + // 断言 | |
| 319 | + assertEquals(0, result.getCode()); | |
| 320 | + assertEquals("https://www.iocoder.cn?code=test_code&state=test", result.getData()); | |
| 321 | + } | |
| 322 | + | |
| 323 | + private HttpServletRequest mockRequest(String clientId, String secret) { | |
| 324 | + HttpServletRequest request = mock(HttpServletRequest.class); | |
| 325 | + when(request.getParameter(eq("client_id"))).thenReturn(clientId); | |
| 326 | + when(request.getParameter(eq("client_secret"))).thenReturn(secret); | |
| 327 | + return request; | |
| 328 | + } | |
| 329 | + | |
| 330 | +} | ... | ... |
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceTest.java
| ... | ... | @@ -3,15 +3,17 @@ package cn.iocoder.yudao.module.system.service.permission; |
| 3 | 3 | import cn.hutool.core.util.RandomUtil; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 5 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | -import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; | |
| 6 | +import cn.iocoder.yudao.framework.common.util.date.DateUtils; | |
| 7 | +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | |
| 7 | 8 | import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleCreateReqVO; |
| 9 | +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleExportReqVO; | |
| 8 | 10 | import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; |
| 9 | 11 | import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleUpdateReqVO; |
| 10 | 12 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; |
| 11 | 13 | import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper; |
| 14 | +import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; | |
| 12 | 15 | import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; |
| 13 | 16 | import cn.iocoder.yudao.module.system.mq.producer.permission.RoleProducer; |
| 14 | -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | |
| 15 | 17 | import org.junit.jupiter.api.Test; |
| 16 | 18 | import org.springframework.boot.test.mock.mockito.MockBean; |
| 17 | 19 | import org.springframework.context.annotation.Import; |
| ... | ... | @@ -20,6 +22,7 @@ import javax.annotation.Resource; |
| 20 | 22 | import java.util.*; |
| 21 | 23 | import java.util.stream.Collectors; |
| 22 | 24 | |
| 25 | +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | |
| 23 | 26 | import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.max; |
| 24 | 27 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
| 25 | 28 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; |
| ... | ... | @@ -155,61 +158,66 @@ public class RoleServiceTest extends BaseDbUnitTest { |
| 155 | 158 | } |
| 156 | 159 | |
| 157 | 160 | @Test |
| 158 | - public void testGetRoles_success() { | |
| 159 | - Map<Long, RoleDO> idRoleMap = new HashMap<>(); | |
| 160 | - // 验证查询状态为1的角色 | |
| 161 | - RoleDO roleDO1 = createRoleDO("role1", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1); | |
| 162 | - roleMapper.insert(roleDO1); | |
| 163 | - idRoleMap.put(roleDO1.getId(), roleDO1); | |
| 164 | - | |
| 165 | - RoleDO roleDO2 = createRoleDO("role2", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1); | |
| 166 | - roleMapper.insert(roleDO2); | |
| 167 | - idRoleMap.put(roleDO2.getId(), roleDO2); | |
| 168 | - | |
| 169 | - // 以下是排除的角色 | |
| 170 | - RoleDO roleDO3 = createRoleDO("role3", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 2); | |
| 171 | - roleMapper.insert(roleDO3); | |
| 172 | - | |
| 173 | - //调用 | |
| 174 | - List<RoleDO> roles = roleService.getRoles(Arrays.asList(1)); | |
| 175 | - | |
| 176 | - //断言 | |
| 177 | - assertEquals(2, roles.size()); | |
| 178 | - roles.stream().forEach(r -> assertPojoEquals(idRoleMap.get(r.getId()), r)); | |
| 161 | + public void testGetRoles() { | |
| 162 | + // mock 数据 | |
| 163 | + RoleDO dbRole = randomPojo(RoleDO.class, o -> { // 等会查询到 | |
| 164 | + o.setName("土豆"); | |
| 165 | + o.setCode("tudou"); | |
| 166 | + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 167 | + o.setCreateTime(DateUtils.buildTime(2022, 2, 8)); | |
| 168 | + }); | |
| 169 | + roleMapper.insert(dbRole); | |
| 170 | + // 测试 name 不匹配 | |
| 171 | + roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setName("红薯"))); | |
| 172 | + // 测试 code 不匹配 | |
| 173 | + roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setCode("hong"))); | |
| 174 | + // 测试 createTime 不匹配 | |
| 175 | + roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setCreateTime(DateUtils.buildTime(2022, 2, 16)))); | |
| 176 | + // 准备参数 | |
| 177 | + RoleExportReqVO reqVO = new RoleExportReqVO(); | |
| 178 | + reqVO.setName("土豆"); | |
| 179 | + reqVO.setCode("tu"); | |
| 180 | + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 181 | + reqVO.setBeginTime(DateUtils.buildTime(2022, 2, 1)); | |
| 182 | + reqVO.setEndTime(DateUtils.buildTime(2022, 2, 12)); | |
| 179 | 183 | |
| 184 | + // 调用 | |
| 185 | + List<RoleDO> list = roleService.getRoleList(reqVO); | |
| 186 | + // 断言 | |
| 187 | + assertEquals(1, list.size()); | |
| 188 | + assertPojoEquals(dbRole, list.get(0)); | |
| 180 | 189 | } |
| 181 | 190 | |
| 182 | 191 | @Test |
| 183 | - public void testGetRolePage_success() { | |
| 184 | - Map<Long, RoleDO> idRoleMap = new HashMap<>(); | |
| 185 | - // 验证名称包含"role", 状态为1,code为"code"的角色 | |
| 186 | - // 第一页 | |
| 187 | - RoleDO roleDO = createRoleDO("role1", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1, "code"); | |
| 188 | - roleMapper.insert(roleDO); | |
| 189 | - idRoleMap.put(roleDO.getId(), roleDO); | |
| 190 | - // 第二页 | |
| 191 | - roleDO = createRoleDO("role2", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1, "code"); | |
| 192 | - roleMapper.insert(roleDO); | |
| 193 | - | |
| 194 | - // 以下是排除的角色 | |
| 195 | - roleDO = createRoleDO("role3", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 2, "code"); | |
| 196 | - roleMapper.insert(roleDO); | |
| 197 | - roleDO = createRoleDO("role4", RoleTypeEnum.CUSTOM, DataScopeEnum.ALL, 1, "xxxxx"); | |
| 198 | - roleMapper.insert(roleDO); | |
| 199 | - | |
| 200 | - //调用 | |
| 201 | - RolePageReqVO reqVO = randomPojo(RolePageReqVO.class, o -> { | |
| 202 | - o.setName("role"); | |
| 203 | - o.setCode("code"); | |
| 204 | - o.setStatus(1); | |
| 205 | - o.setPageNo(1); | |
| 206 | - o.setPageSize(1); | |
| 207 | - o.setBeginTime(null); | |
| 208 | - o.setEndTime(null); | |
| 192 | + public void testGetRolePage() { | |
| 193 | + // mock 数据 | |
| 194 | + RoleDO dbRole = randomPojo(RoleDO.class, o -> { // 等会查询到 | |
| 195 | + o.setName("土豆"); | |
| 196 | + o.setCode("tudou"); | |
| 197 | + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 198 | + o.setCreateTime(DateUtils.buildTime(2022, 2, 8)); | |
| 209 | 199 | }); |
| 210 | - PageResult<RoleDO> result = roleService.getRolePage(reqVO); | |
| 211 | - assertEquals(2, result.getTotal()); | |
| 212 | - result.getList().stream().forEach(r -> assertPojoEquals(idRoleMap.get(r.getId()), r)); | |
| 200 | + roleMapper.insert(dbRole); | |
| 201 | + // 测试 name 不匹配 | |
| 202 | + roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setName("红薯"))); | |
| 203 | + // 测试 code 不匹配 | |
| 204 | + roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setCode("hong"))); | |
| 205 | + // 测试 createTime 不匹配 | |
| 206 | + roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setCreateTime(DateUtils.buildTime(2022, 2, 16)))); | |
| 207 | + // 准备参数 | |
| 208 | + RolePageReqVO reqVO = new RolePageReqVO(); | |
| 209 | + reqVO.setName("土豆"); | |
| 210 | + reqVO.setCode("tu"); | |
| 211 | + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 212 | + reqVO.setBeginTime(DateUtils.buildTime(2022, 2, 1)); | |
| 213 | + reqVO.setEndTime(DateUtils.buildTime(2022, 2, 12)); | |
| 214 | + | |
| 215 | + // 调用 | |
| 216 | + PageResult<RoleDO> pageResult = roleService.getRolePage(reqVO); | |
| 217 | + // 断言 | |
| 218 | + assertEquals(1, pageResult.getTotal()); | |
| 219 | + assertEquals(1, pageResult.getList().size()); | |
| 220 | + assertPojoEquals(dbRole, pageResult.getList().get(0)); | |
| 213 | 221 | } |
| 214 | 222 | |
| 215 | 223 | @Test | ... | ... |