Commit 66034d26c06cbb7ad37e2e6579299260de9b728e
1 parent
6ca88277
完成 oauth2 code 授权码模式的实现
Showing
5 changed files
with
57 additions
and
13 deletions
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http
| 1 | -### 请求 /system/oauth2/authorize 接口 => 成功 | |
| 1 | +### 请求 /system/oauth2/authorize + token 接口 => 成功 | |
| 2 | 2 | POST {{baseUrl}}/system/oauth2/authorize |
| 3 | 3 | Content-Type: application/x-www-form-urlencoded |
| 4 | 4 | Authorization: Bearer {{token}} |
| 5 | 5 | tenant-id: {{adminTenentId}} |
| 6 | 6 | |
| 7 | 7 | response_type=token&client_id=default&scope={"user_info": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true |
| 8 | + | |
| 9 | +### 请求 /system/oauth2/authorize + code 接口 => 成功 | |
| 10 | +POST {{baseUrl}}/system/oauth2/authorize | |
| 11 | +Content-Type: application/x-www-form-urlencoded | |
| 12 | +Authorization: Bearer {{token}} | |
| 13 | +tenant-id: {{adminTenentId}} | |
| 14 | + | |
| 15 | +response_type=code&client_id=default&scope={"user_info": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.java
| ... | ... | @@ -49,7 +49,7 @@ public class OAuth2Controller { |
| 49 | 49 | // GET oauth/authorize AuthorizationEndpoint |
| 50 | 50 | |
| 51 | 51 | @Resource |
| 52 | - private OAuth2GrantService oAuth2GrantService; | |
| 52 | + private OAuth2GrantService oauth2GrantService; | |
| 53 | 53 | @Resource |
| 54 | 54 | private OAuth2ClientService oauth2ClientService; |
| 55 | 55 | @Resource |
| ... | ... | @@ -121,17 +121,18 @@ public class OAuth2Controller { |
| 121 | 121 | } else { // 2.2 假设 approved 非 null,说明是场景二 |
| 122 | 122 | // 如果计算后不通过,则跳转一个错误链接 |
| 123 | 123 | if (!oauth2ApproveService.updateAfterApproval(getLoginUserId(), getUserType(), clientId, scopes)) { |
| 124 | - return success("TODO"); | |
| 124 | + return success(OAuth2Utils.buildUnsuccessfulRedirect(redirectUri, responseType, state, | |
| 125 | + "access_denied", "User denied access")); | |
| 125 | 126 | } |
| 126 | 127 | } |
| 127 | 128 | |
| 128 | 129 | // 3.1 如果是 code 授权码模式,则发放 code 授权码,并重定向 |
| 129 | 130 | List<String> approveScopes = convertList(scopes.entrySet(), Map.Entry::getKey, Map.Entry::getValue); |
| 130 | 131 | if (grantTypeEnum == OAuth2GrantTypeEnum.AUTHORIZATION_CODE) { |
| 131 | - return success(getAuthorizationCodeRedirect()); | |
| 132 | + return success(getAuthorizationCodeRedirect(getLoginUserId(), client, approveScopes, redirectUri, state)); | |
| 132 | 133 | } |
| 133 | 134 | // 3.2 如果是 token 则是 implicit 简化模式,则发送 accessToken 访问令牌,并重定向 |
| 134 | - return success(getImplicitGrantRedirect(getLoginUserId(), client, redirectUri, state, approveScopes)); | |
| 135 | + return success(getImplicitGrantRedirect(getLoginUserId(), client, approveScopes, redirectUri, state)); | |
| 135 | 136 | } |
| 136 | 137 | |
| 137 | 138 | private static OAuth2GrantTypeEnum getGrantTypeEnum(String responseType) { |
| ... | ... | @@ -145,17 +146,23 @@ public class OAuth2Controller { |
| 145 | 146 | } |
| 146 | 147 | |
| 147 | 148 | private String getImplicitGrantRedirect(Long userId, OAuth2ClientDO client, |
| 148 | - String redirectUri, String state, List<String> scopes) { | |
| 149 | - OAuth2AccessTokenDO accessTokenDO = oAuth2GrantService.grantImplicit(userId, getUserType(), client.getClientId(), scopes); | |
| 149 | + List<String> scopes, String redirectUri, String state) { | |
| 150 | + // 1. 创建 access token 访问令牌 | |
| 151 | + OAuth2AccessTokenDO accessTokenDO = oauth2GrantService.grantImplicit(userId, getUserType(), client.getClientId(), scopes); | |
| 150 | 152 | Assert.notNull(accessTokenDO, "访问令牌不能为空"); // 防御性检查 |
| 151 | - // 拼接 URL | |
| 153 | + // 2. 拼接重定向的 URL | |
| 152 | 154 | // noinspection unchecked |
| 153 | 155 | return OAuth2Utils.buildImplicitRedirectUri(redirectUri, accessTokenDO.getAccessToken(), state, accessTokenDO.getExpiresTime(), |
| 154 | 156 | scopes, JsonUtils.parseObject(client.getAdditionalInformation(), Map.class)); |
| 155 | 157 | } |
| 156 | 158 | |
| 157 | - private String getAuthorizationCodeRedirect() { | |
| 158 | - return ""; | |
| 159 | + private String getAuthorizationCodeRedirect(Long userId, OAuth2ClientDO client, | |
| 160 | + List<String> scopes, String redirectUri, String state) { | |
| 161 | + // 1. 创建 code 授权码 | |
| 162 | + String authorizationCode = oauth2GrantService.grantAuthorizationCode(userId,getUserType(), client.getClientId(), scopes, | |
| 163 | + redirectUri, state); | |
| 164 | + // 2. 拼接重定向的 URL | |
| 165 | + return OAuth2Utils.buildAuthorizationCodeRedirectUri(redirectUri, authorizationCode, state); | |
| 159 | 166 | } |
| 160 | 167 | |
| 161 | 168 | private Integer getUserType() { | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImpl.java
| ... | ... | @@ -20,7 +20,7 @@ public class OAuth2ApproveServiceImpl implements OAuth2ApproveService { |
| 20 | 20 | |
| 21 | 21 | @Override |
| 22 | 22 | public boolean updateAfterApproval(Long userId, Integer userType, String clientId, Map<String, Boolean> scopes) { |
| 23 | - return true; | |
| 23 | + return false; | |
| 24 | 24 | } |
| 25 | 25 | |
| 26 | 26 | } | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java
| ... | ... | @@ -4,7 +4,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; |
| 4 | 4 | import org.springframework.stereotype.Service; |
| 5 | 5 | |
| 6 | 6 | import javax.annotation.Resource; |
| 7 | -import java.util.Collection; | |
| 8 | 7 | import java.util.List; |
| 9 | 8 | |
| 10 | 9 | /** |
| ... | ... | @@ -28,7 +27,7 @@ public class OAuth2GrantServiceImpl implements OAuth2GrantService { |
| 28 | 27 | public String grantAuthorizationCode(Long userId, Integer userType, |
| 29 | 28 | String clientId, List<String> scopes, |
| 30 | 29 | String redirectUri, String state) { |
| 31 | - return null; | |
| 30 | + return "test"; | |
| 32 | 31 | } |
| 33 | 32 | |
| 34 | 33 | } | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java
| ... | ... | @@ -14,6 +14,25 @@ import java.util.*; |
| 14 | 14 | public class OAuth2Utils { |
| 15 | 15 | |
| 16 | 16 | /** |
| 17 | + * 构建授权码模式下,重定向的 URI | |
| 18 | + * | |
| 19 | + * copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 getSuccessfulRedirect 方法 | |
| 20 | + * | |
| 21 | + * @param redirectUri 重定向 URI | |
| 22 | + * @param authorizationCode 授权码 | |
| 23 | + * @param state 状态 | |
| 24 | + * @return 授权码模式下的重定向 URI | |
| 25 | + */ | |
| 26 | + public static String buildAuthorizationCodeRedirectUri(String redirectUri, String authorizationCode, String state) { | |
| 27 | + Map<String, String> query = new LinkedHashMap<>(); | |
| 28 | + query.put("code", authorizationCode); | |
| 29 | + if (state != null) { | |
| 30 | + query.put("state", state); | |
| 31 | + } | |
| 32 | + return HttpUtils.append(redirectUri, query, null, false); | |
| 33 | + } | |
| 34 | + | |
| 35 | + /** | |
| 17 | 36 | * 构建简化模式下,重定向的 URI |
| 18 | 37 | * |
| 19 | 38 | * copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 appendAccessToken 方法 |
| ... | ... | @@ -53,4 +72,15 @@ public class OAuth2Utils { |
| 53 | 72 | return HttpUtils.append(redirectUri, vars, keys, true); |
| 54 | 73 | } |
| 55 | 74 | |
| 75 | + public static String buildUnsuccessfulRedirect(String redirectUri, String responseType, String state, | |
| 76 | + String error, String description) { | |
| 77 | + Map<String, String> query = new LinkedHashMap<String, String>(); | |
| 78 | + query.put("error", error); | |
| 79 | + query.put("error_description", description); | |
| 80 | + if (state != null) { | |
| 81 | + query.put("state", state); | |
| 82 | + } | |
| 83 | + return HttpUtils.append(redirectUri, query, null, !responseType.contains("code")); | |
| 84 | + } | |
| 85 | + | |
| 56 | 86 | } | ... | ... |