瀏覽代碼

完成 oauth2 code 授权码模式的实现

YunaiV 3 年之前
父節點
當前提交
66034d26c0

+ 9 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2Controller.http

@@ -1,7 +1,15 @@
-### 请求 /system/oauth2/authorize 接口 => 成功
+### 请求 /system/oauth2/authorize + token 接口 => 成功
 POST {{baseUrl}}/system/oauth2/authorize
 Content-Type: application/x-www-form-urlencoded
 Authorization: Bearer {{token}}
 tenant-id: {{adminTenentId}}
 
 response_type=token&client_id=default&scope={"user_info": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true
+
+### 请求 /system/oauth2/authorize + code 接口 => 成功
+POST {{baseUrl}}/system/oauth2/authorize
+Content-Type: application/x-www-form-urlencoded
+Authorization: Bearer {{token}}
+tenant-id: {{adminTenentId}}
+
+response_type=code&client_id=default&scope={"user_info": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true

+ 16 - 9
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 {
 //    GET  oauth/authorize AuthorizationEndpoint
 
     @Resource
-    private OAuth2GrantService oAuth2GrantService;
+    private OAuth2GrantService oauth2GrantService;
     @Resource
     private OAuth2ClientService oauth2ClientService;
     @Resource
@@ -121,17 +121,18 @@ public class OAuth2Controller {
         } else { // 2.2 假设 approved 非 null,说明是场景二
             // 如果计算后不通过,则跳转一个错误链接
             if (!oauth2ApproveService.updateAfterApproval(getLoginUserId(), getUserType(), clientId, scopes)) {
-                return success("TODO");
+                return success(OAuth2Utils.buildUnsuccessfulRedirect(redirectUri, responseType, state,
+                        "access_denied", "User denied access"));
             }
         }
 
         // 3.1 如果是 code 授权码模式,则发放 code 授权码,并重定向
         List<String> approveScopes = convertList(scopes.entrySet(), Map.Entry::getKey, Map.Entry::getValue);
         if (grantTypeEnum == OAuth2GrantTypeEnum.AUTHORIZATION_CODE) {
-            return success(getAuthorizationCodeRedirect());
+            return success(getAuthorizationCodeRedirect(getLoginUserId(), client, approveScopes, redirectUri, state));
         }
         // 3.2 如果是 token 则是 implicit 简化模式,则发送 accessToken 访问令牌,并重定向
-        return success(getImplicitGrantRedirect(getLoginUserId(), client, redirectUri, state, approveScopes));
+        return success(getImplicitGrantRedirect(getLoginUserId(), client, approveScopes, redirectUri, state));
     }
 
     private static OAuth2GrantTypeEnum getGrantTypeEnum(String responseType) {
@@ -145,17 +146,23 @@ public class OAuth2Controller {
     }
 
     private String getImplicitGrantRedirect(Long userId, OAuth2ClientDO client,
-                                            String redirectUri, String state, List<String> scopes) {
-        OAuth2AccessTokenDO accessTokenDO = oAuth2GrantService.grantImplicit(userId, getUserType(), client.getClientId(), scopes);
+                                            List<String> scopes, String redirectUri, String state) {
+        // 1. 创建 access token 访问令牌
+        OAuth2AccessTokenDO accessTokenDO = oauth2GrantService.grantImplicit(userId, getUserType(), client.getClientId(), scopes);
         Assert.notNull(accessTokenDO, "访问令牌不能为空"); // 防御性检查
-        // 拼接 URL
+        // 2. 拼接重定向的 URL
         // noinspection unchecked
         return OAuth2Utils.buildImplicitRedirectUri(redirectUri, accessTokenDO.getAccessToken(), state, accessTokenDO.getExpiresTime(),
                 scopes, JsonUtils.parseObject(client.getAdditionalInformation(), Map.class));
     }
 
-    private String getAuthorizationCodeRedirect() {
-        return "";
+    private String getAuthorizationCodeRedirect(Long userId, OAuth2ClientDO client,
+                                                List<String> scopes, String redirectUri, String state) {
+        // 1. 创建 code 授权码
+        String authorizationCode = oauth2GrantService.grantAuthorizationCode(userId,getUserType(), client.getClientId(), scopes,
+                redirectUri, state);
+        // 2. 拼接重定向的 URL
+        return OAuth2Utils.buildAuthorizationCodeRedirectUri(redirectUri, authorizationCode, state);
     }
 
     private Integer getUserType() {

+ 1 - 1
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 {
 
     @Override
     public boolean updateAfterApproval(Long userId, Integer userType, String clientId, Map<String, Boolean> scopes) {
-        return true;
+        return false;
     }
 
 }

+ 1 - 2
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;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
-import java.util.Collection;
 import java.util.List;
 
 /**
@@ -28,7 +27,7 @@ public class OAuth2GrantServiceImpl implements OAuth2GrantService {
     public String grantAuthorizationCode(Long userId, Integer userType,
                                          String clientId, List<String> scopes,
                                          String redirectUri, String state) {
-        return null;
+        return "test";
     }
 
 }

+ 30 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java

@@ -13,6 +13,25 @@ import java.util.*;
  */
 public class OAuth2Utils {
 
+    /**
+     * 构建授权码模式下,重定向的 URI
+     *
+     * copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 getSuccessfulRedirect 方法
+     *
+     * @param redirectUri 重定向 URI
+     * @param authorizationCode 授权码
+     * @param state 状态
+     * @return 授权码模式下的重定向 URI
+     */
+    public static String buildAuthorizationCodeRedirectUri(String redirectUri, String authorizationCode, String state) {
+        Map<String, String> query = new LinkedHashMap<>();
+        query.put("code", authorizationCode);
+        if (state != null) {
+            query.put("state", state);
+        }
+        return HttpUtils.append(redirectUri, query, null, false);
+    }
+
     /**
      * 构建简化模式下,重定向的 URI
      *
@@ -53,4 +72,15 @@ public class OAuth2Utils {
         return HttpUtils.append(redirectUri, vars, keys, true);
     }
 
+    public static String buildUnsuccessfulRedirect(String redirectUri, String responseType, String state,
+                                                   String error, String description) {
+        Map<String, String> query = new LinkedHashMap<String, String>();
+        query.put("error", error);
+        query.put("error_description", description);
+        if (state != null) {
+            query.put("state", state);
+        }
+        return HttpUtils.append(redirectUri, query, null, !responseType.contains("code"));
+    }
+
 }