From 845b57e9311945b323c760ad66af59621e290573 Mon Sep 17 00:00:00 2001
From: xlsea <m@xlsea.cn>
Date: 星期一, 08 一月 2024 11:54:30 +0800
Subject: [PATCH] JustAuth 整合 TopIam 单点登录

---
 ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java                      |  100 +++++++++++++++++++++++++++++++++
 script/sql/ry_vue_5.X.sql                                                                                                   |    2 
 ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java                             |    5 +
 ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopiamSource.java                       |   51 +++++++++++++++++
 ruoyi-admin/src/main/resources/application-dev.yml                                                                          |    7 ++
 ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java |    7 ++
 6 files changed, 170 insertions(+), 2 deletions(-)

diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml
index 4aaea57..4929c1b 100644
--- a/ruoyi-admin/src/main/resources/application-dev.yml
+++ b/ruoyi-admin/src/main/resources/application-dev.yml
@@ -189,6 +189,13 @@
       client-id: 876892492581044224
       client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
       redirect-uri: ${justauth.address}/social-callback?source=maxkey
+    topiam:
+      # topiam 鏈嶅姟鍣ㄥ湴鍧�
+      server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol
+      client-id: 449c4*********937************759
+      client-secret: ac7***********1e0************28d
+      redirect-uri: ${justauth.address}/social-callback?source=topiam
+      scopes: [openid, email, phone, profile]
     qq:
       client-id: 10**********6
       client-secret: 1f7d08**********5b7**********29e
diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java
index 76be234..5f49d9c 100644
--- a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java
+++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java
@@ -2,6 +2,8 @@
 
 import lombok.Data;
 
+import java.util.List;
+
 /**
  * 绀句氦鐧诲綍閰嶇疆
  *
@@ -65,4 +67,9 @@
      */
     private String serverUrl;
 
+    /**
+     * 璇锋眰鑼冨洿
+     */
+    private List<String> scopes;
+
 }
diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java
new file mode 100644
index 0000000..13649f9
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopIamRequest.java
@@ -0,0 +1,100 @@
+package org.dromara.common.social.topiam;
+
+import cn.hutool.core.lang.Dict;
+import cn.hutool.core.util.StrUtil;
+import com.xkcoding.http.support.HttpHeader;
+import lombok.extern.slf4j.Slf4j;
+import me.zhyd.oauth.cache.AuthStateCache;
+import me.zhyd.oauth.config.AuthConfig;
+import me.zhyd.oauth.exception.AuthException;
+import me.zhyd.oauth.model.AuthCallback;
+import me.zhyd.oauth.model.AuthToken;
+import me.zhyd.oauth.model.AuthUser;
+import me.zhyd.oauth.request.AuthDefaultRequest;
+import me.zhyd.oauth.utils.HttpUtils;
+import me.zhyd.oauth.utils.UrlBuilder;
+import org.dromara.common.core.utils.SpringUtils;
+import org.dromara.common.json.utils.JsonUtils;
+
+import static org.dromara.common.social.topiam.AuthTopiamSource.TOPIAM;
+
+/**
+ * TopIAM 璁よ瘉璇锋眰
+ *
+ * @author xlsea
+ * @since 2024-01-06
+ */
+@Slf4j
+public class AuthTopIamRequest extends AuthDefaultRequest {
+
+    public static final String SERVER_URL = SpringUtils.getProperty("justauth.type.topiam.server-url");
+
+    /**
+     * 璁惧畾褰掑睘鍩�
+     */
+    public AuthTopIamRequest(AuthConfig config) {
+        super(config, TOPIAM);
+    }
+
+    public AuthTopIamRequest(AuthConfig config, AuthStateCache authStateCache) {
+        super(config, TOPIAM, authStateCache);
+    }
+
+    @Override
+    protected AuthToken getAccessToken(AuthCallback authCallback) {
+        String body = doPostAuthorizationCode(authCallback.getCode());
+        Dict object = JsonUtils.parseMap(body);
+        checkResponse(object);
+        return AuthToken.builder()
+            .accessToken(object.getStr("access_token"))
+            .refreshToken(object.getStr("refresh_token"))
+            .idToken(object.getStr("id_token"))
+            .tokenType(object.getStr("token_type"))
+            .scope(object.getStr("scope"))
+            .build();
+    }
+
+    @Override
+    protected AuthUser getUserInfo(AuthToken authToken) {
+        String body = doGetUserInfo(authToken);
+        Dict object = JsonUtils.parseMap(body);
+        checkResponse(object);
+        return AuthUser.builder()
+            .uuid(object.getStr("sub"))
+            .username(object.getStr("preferred_username"))
+            .nickname(object.getStr("nickname"))
+            .avatar(object.getStr("picture"))
+            .email(object.getStr("email"))
+            .token(authToken)
+            .source(source.toString())
+            .build();
+    }
+
+
+    @Override
+    protected String doGetUserInfo(AuthToken authToken) {
+        return new HttpUtils(config.getHttpConfig()).get(source.userInfo(), null, new HttpHeader()
+            .add("Content-Type", "application/json")
+            .add("Authorization", "Bearer " + authToken.getAccessToken()), false).getBody();
+    }
+
+
+    @Override
+    public String authorize(String state) {
+        return UrlBuilder.fromBaseUrl(super.authorize(state))
+            .queryParam("scope", StrUtil.join("%20", config.getScopes()))
+            .build();
+    }
+
+    public static void checkResponse(Dict object) {
+        // oauth/token 楠岃瘉寮傚父
+        if (object.containsKey("error")) {
+            throw new AuthException(object.getStr("error_description"));
+        }
+        // user 楠岃瘉寮傚父
+        if (object.containsKey("message")) {
+            throw new AuthException(object.getStr("message"));
+        }
+    }
+
+}
diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopiamSource.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopiamSource.java
new file mode 100644
index 0000000..e47d6c6
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/topiam/AuthTopiamSource.java
@@ -0,0 +1,51 @@
+package org.dromara.common.social.topiam;
+
+import me.zhyd.oauth.config.AuthSource;
+import me.zhyd.oauth.request.AuthDefaultRequest;
+
+/**
+ * Oauth2 榛樿鎺ュ彛璇存槑
+ *
+ * @author xlsea
+ * @since 2024-01-06
+ */
+public enum AuthTopiamSource implements AuthSource {
+
+    /**
+     * 娴嬭瘯
+     */
+    TOPIAM {
+        /**
+         * 鎺堟潈鐨刟pi
+         */
+        @Override
+        public String authorize() {
+            return AuthTopIamRequest.SERVER_URL + "/oauth2/auth";
+        }
+
+        /**
+         * 鑾峰彇accessToken鐨刟pi
+         */
+        @Override
+        public String accessToken() {
+            return AuthTopIamRequest.SERVER_URL + "/oauth2/token";
+        }
+
+        /**
+         * 鑾峰彇鐢ㄦ埛淇℃伅鐨刟pi
+         */
+        @Override
+        public String userInfo() {
+            return AuthTopIamRequest.SERVER_URL + "/oauth2/userinfo";
+        }
+
+        /**
+         * 骞冲彴瀵瑰簲鐨� AuthRequest 瀹炵幇绫伙紝蹇呴』缁ф壙鑷� {@link AuthDefaultRequest}
+         */
+        @Override
+        public Class<? extends AuthDefaultRequest> getTargetClass() {
+            return AuthTopIamRequest.class;
+        }
+
+    }
+}
diff --git a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java
index 51b7e13..04f6214 100644
--- a/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java
+++ b/ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java
@@ -11,6 +11,7 @@
 import org.dromara.common.social.config.properties.SocialLoginConfigProperties;
 import org.dromara.common.social.config.properties.SocialProperties;
 import org.dromara.common.social.maxkey.AuthMaxKeyRequest;
+import org.dromara.common.social.topiam.AuthTopIamRequest;
 
 /**
  * 璁よ瘉鎺堟潈宸ュ叿绫�
@@ -38,7 +39,8 @@
         AuthConfig.AuthConfigBuilder builder = AuthConfig.builder()
             .clientId(obj.getClientId())
             .clientSecret(obj.getClientSecret())
-            .redirectUri(obj.getRedirectUri());
+            .redirectUri(obj.getRedirectUri())
+            .scopes(obj.getScopes());
         return switch (source.toLowerCase()) {
             case "dingtalk" -> new AuthDingTalkRequest(builder.build(), STATE_CACHE);
             case "baidu" -> new AuthBaiduRequest(builder.build(), STATE_CACHE);
@@ -63,6 +65,7 @@
             case "wechat_mp" -> new AuthWeChatMpRequest(builder.build(), STATE_CACHE);
             case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE);
             case "maxkey" -> new AuthMaxKeyRequest(builder.build(), STATE_CACHE);
+            case "topiam" -> new AuthTopIamRequest(builder.build(), STATE_CACHE);
             default -> throw new AuthException("鏈幏鍙栧埌鏈夋晥鐨凙uth閰嶇疆");
         };
     }
diff --git a/script/sql/ry_vue_5.X.sql b/script/sql/ry_vue_5.X.sql
index ea365bd..c66782b 100644
--- a/script/sql/ry_vue_5.X.sql
+++ b/script/sql/ry_vue_5.X.sql
@@ -21,7 +21,7 @@
     union_id           varchar(255)     default null    comment '鐢ㄦ埛鐨� unionid',
     scope              varchar(255)     default null    comment '鎺堜簣鐨勬潈闄愶紝閮ㄥ垎骞冲彴鍙兘娌℃湁',
     token_type         varchar(255)     default null    comment '涓埆骞冲彴鐨勬巿鏉冧俊鎭紝閮ㄥ垎骞冲彴鍙兘娌℃湁',
-    id_token           varchar(255)     default null    comment 'id token锛岄儴鍒嗗钩鍙板彲鑳芥病鏈�',
+    id_token           text             default null    comment 'id token锛岄儴鍒嗗钩鍙板彲鑳芥病鏈�',
     mac_algorithm      varchar(255)     default null    comment '灏忕背骞冲彴鐢ㄦ埛鐨勯檮甯﹀睘鎬э紝閮ㄥ垎骞冲彴鍙兘娌℃湁',
     mac_key            varchar(255)     default null    comment '灏忕背骞冲彴鐢ㄦ埛鐨勯檮甯﹀睘鎬э紝閮ㄥ垎骞冲彴鍙兘娌℃湁',
     code               varchar(255)     default null    comment '鐢ㄦ埛鐨勬巿鏉僣ode锛岄儴鍒嗗钩鍙板彲鑳芥病鏈�',

--
Gitblit v1.9.3