Parcourir la source

JustAuth 整合 TopIam 单点登录

xlsea il y a 1 an
Parent
commit
845b57e931

+ 7 - 0
ruoyi-admin/src/main/resources/application-dev.yml

@@ -189,6 +189,13 @@ justauth:
       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

+ 7 - 0
ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/config/properties/SocialLoginConfigProperties.java

@@ -2,6 +2,8 @@ package org.dromara.common.social.config.properties;
 
 import lombok.Data;
 
+import java.util.List;
+
 /**
  * 社交登录配置
  *
@@ -65,4 +67,9 @@ public class SocialLoginConfigProperties {
      */
     private String serverUrl;
 
+    /**
+     * 请求范围
+     */
+    private List<String> scopes;
+
 }

+ 100 - 0
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"));
+        }
+    }
+
+}

+ 51 - 0
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 {
+        /**
+         * 授权的api
+         */
+        @Override
+        public String authorize() {
+            return AuthTopIamRequest.SERVER_URL + "/oauth2/auth";
+        }
+
+        /**
+         * 获取accessToken的api
+         */
+        @Override
+        public String accessToken() {
+            return AuthTopIamRequest.SERVER_URL + "/oauth2/token";
+        }
+
+        /**
+         * 获取用户信息的api
+         */
+        @Override
+        public String userInfo() {
+            return AuthTopIamRequest.SERVER_URL + "/oauth2/userinfo";
+        }
+
+        /**
+         * 平台对应的 AuthRequest 实现类,必须继承自 {@link AuthDefaultRequest}
+         */
+        @Override
+        public Class<? extends AuthDefaultRequest> getTargetClass() {
+            return AuthTopIamRequest.class;
+        }
+
+    }
+}

+ 4 - 1
ruoyi-common/ruoyi-common-social/src/main/java/org/dromara/common/social/utils/SocialUtils.java

@@ -11,6 +11,7 @@ import org.dromara.common.core.utils.SpringUtils;
 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 @@ public class SocialUtils  {
         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 @@ public class SocialUtils  {
             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("未获取到有效的Auth配置");
         };
     }

+ 1 - 1
script/sql/ry_vue_5.X.sql

@@ -21,7 +21,7 @@ create table sys_social
     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 '用户的授权code,部分平台可能没有',