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,7 +45,7 @@ public class RandomUtils { | ||
| 45 | } | 45 | } |
| 46 | // 如果是 type、status 结尾的字段,返回 tinyint 范围 | 46 | // 如果是 type、status 结尾的字段,返回 tinyint 范围 |
| 47 | if (StrUtil.endWithAnyIgnoreCase(attributeMetadata.getAttributeName(), | 47 | if (StrUtil.endWithAnyIgnoreCase(attributeMetadata.getAttributeName(), |
| 48 | - "type", "status", "category")) { | 48 | + "type", "status", "category", "scope")) { |
| 49 | return RandomUtil.randomInt(0, TINYINT_MAX + 1); | 49 | return RandomUtil.randomInt(0, TINYINT_MAX + 1); |
| 50 | } | 50 | } |
| 51 | return RandomUtil.randomInt(); | 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,8 +52,6 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti | ||
| 52 | * 考虑到【本系统】暂时不想做的过于复杂,默认只有获取到 access token 之后,可以访问【本系统】管理后台的 /system-api/* 所有接口,除非手动添加 scope 控制。 | 52 | * 考虑到【本系统】暂时不想做的过于复杂,默认只有获取到 access token 之后,可以访问【本系统】管理后台的 /system-api/* 所有接口,除非手动添加 scope 控制。 |
| 53 | * scope 的使用示例,可见 {@link OAuth2UserController} 类 | 53 | * scope 的使用示例,可见 {@link OAuth2UserController} 类 |
| 54 | * | 54 | * |
| 55 | - * | ||
| 56 | - * | ||
| 57 | * @author 芋道源码 | 55 | * @author 芋道源码 |
| 58 | */ | 56 | */ |
| 59 | @Api(tags = "管理后台 - OAuth2.0 授权") | 57 | @Api(tags = "管理后台 - OAuth2.0 授权") |
| @@ -185,8 +183,7 @@ public class OAuth2OpenController { | @@ -185,8 +183,7 @@ public class OAuth2OpenController { | ||
| 185 | // 0. 校验用户已经登录。通过 Spring Security 实现 | 183 | // 0. 校验用户已经登录。通过 Spring Security 实现 |
| 186 | 184 | ||
| 187 | // 1. 获得 Client 客户端的信息 | 185 | // 1. 获得 Client 客户端的信息 |
| 188 | - OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId, null, | ||
| 189 | - null, null, null); | 186 | + OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId); |
| 190 | // 2. 获得用户已经授权的信息 | 187 | // 2. 获得用户已经授权的信息 |
| 191 | List<OAuth2ApproveDO> approves = oauth2ApproveService.getApproveList(getLoginUserId(), getUserType(), clientId); | 188 | List<OAuth2ApproveDO> approves = oauth2ApproveService.getApproveList(getLoginUserId(), getUserType(), clientId); |
| 192 | // 拼接返回 | 189 | // 拼接返回 |
| @@ -223,7 +220,6 @@ public class OAuth2OpenController { | @@ -223,7 +220,6 @@ public class OAuth2OpenController { | ||
| 223 | @SuppressWarnings("unchecked") | 220 | @SuppressWarnings("unchecked") |
| 224 | Map<String, Boolean> scopes = JsonUtils.parseObject(scope, Map.class); | 221 | Map<String, Boolean> scopes = JsonUtils.parseObject(scope, Map.class); |
| 225 | scopes = ObjectUtil.defaultIfNull(scopes, Collections.emptyMap()); | 222 | scopes = ObjectUtil.defaultIfNull(scopes, Collections.emptyMap()); |
| 226 | - // TODO 芋艿:针对 approved + scopes 在看看 spring security 的实现 | ||
| 227 | // 0. 校验用户已经登录。通过 Spring Security 实现 | 223 | // 0. 校验用户已经登录。通过 Spring Security 实现 |
| 228 | 224 | ||
| 229 | // 1.1 校验 responseType 是否满足 code 或者 token 值 | 225 | // 1.1 校验 responseType 是否满足 code 或者 token 值 |
| @@ -262,7 +258,7 @@ public class OAuth2OpenController { | @@ -262,7 +258,7 @@ public class OAuth2OpenController { | ||
| 262 | if (StrUtil.equalsAny(responseType, "token")) { | 258 | if (StrUtil.equalsAny(responseType, "token")) { |
| 263 | return OAuth2GrantTypeEnum.IMPLICIT; | 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 | private String getImplicitGrantRedirect(Long userId, OAuth2ClientDO client, | 264 | private String getImplicitGrantRedirect(Long userId, OAuth2ClientDO client, |
| @@ -279,7 +275,7 @@ public class OAuth2OpenController { | @@ -279,7 +275,7 @@ public class OAuth2OpenController { | ||
| 279 | private String getAuthorizationCodeRedirect(Long userId, OAuth2ClientDO client, | 275 | private String getAuthorizationCodeRedirect(Long userId, OAuth2ClientDO client, |
| 280 | List<String> scopes, String redirectUri, String state) { | 276 | List<String> scopes, String redirectUri, String state) { |
| 281 | // 1. 创建 code 授权码 | 277 | // 1. 创建 code 授权码 |
| 282 | - String authorizationCode = oauth2GrantService.grantAuthorizationCodeForCode(userId,getUserType(), client.getClientId(), scopes, | 278 | + String authorizationCode = oauth2GrantService.grantAuthorizationCodeForCode(userId, getUserType(), client.getClientId(), scopes, |
| 283 | redirectUri, state); | 279 | redirectUri, state); |
| 284 | // 2. 拼接重定向的 URL | 280 | // 2. 拼接重定向的 URL |
| 285 | return OAuth2Utils.buildAuthorizationCodeRedirectUri(redirectUri, authorizationCode, state); | 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 +7,7 @@ import lombok.AllArgsConstructor; | ||
| 7 | import lombok.Data; | 7 | import lombok.Data; |
| 8 | import lombok.NoArgsConstructor; | 8 | import lombok.NoArgsConstructor; |
| 9 | 9 | ||
| 10 | -import java.util.Set; | 10 | +import java.util.List; |
| 11 | 11 | ||
| 12 | @ApiModel("管理后台 - 【开放接口】校验令牌 Response VO") | 12 | @ApiModel("管理后台 - 【开放接口】校验令牌 Response VO") |
| 13 | @Data | 13 | @Data |
| @@ -28,13 +28,13 @@ public class OAuth2OpenCheckTokenRespVO { | @@ -28,13 +28,13 @@ public class OAuth2OpenCheckTokenRespVO { | ||
| 28 | @ApiModelProperty(value = "客户端编号", required = true, example = "car") | 28 | @ApiModelProperty(value = "客户端编号", required = true, example = "car") |
| 29 | private String clientId; | 29 | private String clientId; |
| 30 | @ApiModelProperty(value = "授权范围", required = true, example = "user_info") | 30 | @ApiModelProperty(value = "授权范围", required = true, example = "user_info") |
| 31 | - private Set<String> scopes; | 31 | + private List<String> scopes; |
| 32 | 32 | ||
| 33 | @ApiModelProperty(value = "访问令牌", required = true, example = "tudou") | 33 | @ApiModelProperty(value = "访问令牌", required = true, example = "tudou") |
| 34 | @JsonProperty("access_token") | 34 | @JsonProperty("access_token") |
| 35 | private String accessToken; | 35 | private String accessToken; |
| 36 | 36 | ||
| 37 | @ApiModelProperty(value = "过期时间", required = true, example = "1593092157", notes = "时间戳 / 1000,即单位:秒") | 37 | @ApiModelProperty(value = "过期时间", required = true, example = "1593092157", notes = "时间戳 / 1000,即单位:秒") |
| 38 | - @JsonProperty("exp") | ||
| 39 | private Long exp; | 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,11 +61,13 @@ public class OAuth2Utils { | ||
| 61 | if (CollUtil.isNotEmpty(scopes)) { | 61 | if (CollUtil.isNotEmpty(scopes)) { |
| 62 | vars.put("scope", buildScopeStr(scopes)); | 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 | // Do not include the refresh token (even if there is one) | 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,15 +3,17 @@ package cn.iocoder.yudao.module.system.service.permission; | ||
| 3 | import cn.hutool.core.util.RandomUtil; | 3 | import cn.hutool.core.util.RandomUtil; |
| 4 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 4 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 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 | import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleCreateReqVO; | 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 | import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; | 10 | import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; |
| 9 | import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleUpdateReqVO; | 11 | import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleUpdateReqVO; |
| 10 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; | 12 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; |
| 11 | import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper; | 13 | import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper; |
| 14 | +import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; | ||
| 12 | import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; | 15 | import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; |
| 13 | import cn.iocoder.yudao.module.system.mq.producer.permission.RoleProducer; | 16 | import cn.iocoder.yudao.module.system.mq.producer.permission.RoleProducer; |
| 14 | -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||
| 15 | import org.junit.jupiter.api.Test; | 17 | import org.junit.jupiter.api.Test; |
| 16 | import org.springframework.boot.test.mock.mockito.MockBean; | 18 | import org.springframework.boot.test.mock.mockito.MockBean; |
| 17 | import org.springframework.context.annotation.Import; | 19 | import org.springframework.context.annotation.Import; |
| @@ -20,6 +22,7 @@ import javax.annotation.Resource; | @@ -20,6 +22,7 @@ import javax.annotation.Resource; | ||
| 20 | import java.util.*; | 22 | import java.util.*; |
| 21 | import java.util.stream.Collectors; | 23 | import java.util.stream.Collectors; |
| 22 | 24 | ||
| 25 | +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||
| 23 | import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.max; | 26 | import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.max; |
| 24 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | 27 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
| 25 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | 28 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; |
| @@ -155,61 +158,66 @@ public class RoleServiceTest extends BaseDbUnitTest { | @@ -155,61 +158,66 @@ public class RoleServiceTest extends BaseDbUnitTest { | ||
| 155 | } | 158 | } |
| 156 | 159 | ||
| 157 | @Test | 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 | @Test | 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 | @Test | 223 | @Test |