From a365eb1d7213c5f28c6d2fc2b8f87099d71d17d4 Mon Sep 17 00:00:00 2001
From: 潘志宝 <979469083@qq.com>
Date: 星期一, 03 三月 2025 16:09:21 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'

---
 /dev/null                                                              |    6 -
 sdk/src/main/resources/META-INF/spring.factories                       |    1 
 sdk/src/main/java/com/iailab/sdk/auth/enums/ErrorCodeConstants.java    |   16 +++
 sdk/src/main/resources/application.yaml                                |   17 --
 sdk/pom.xml                                                            |   23 ---
 sdk/src/main/java/com/iailab/sdk/auth/client/IailabAuthClient.java     |  122 +++++++++++++++---------
 sdk/src/main/java/com/iailab/sdk/auth/config/SdkAutoConfiguration.java |   28 +++++
 sdk/src/main/java/com/iailab/sdk/auth/client/vo/AuthLoginRespVO.java   |   24 ++++
 sdk/src/main/java/com/iailab/sdk/util/http/IailabHttpUtils.java        |   32 +++++-
 sdk/src/test/java/com/iailab/sdk/IailabClientTest.java                 |   14 --
 sdk/src/main/java/com/iailab/sdk/auth/config/AuthProperties.java       |   15 +++
 11 files changed, 198 insertions(+), 100 deletions(-)

diff --git a/sdk/pom.xml b/sdk/pom.xml
index 7d6080e..316109e 100644
--- a/sdk/pom.xml
+++ b/sdk/pom.xml
@@ -45,27 +45,12 @@
             <groupId>com.iailab</groupId>
             <artifactId>iailab-common-test</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-validation</artifactId>
+        </dependency>
     </dependencies>
 
-<!--    <build>-->
-<!--        &lt;!&ndash; 设置构建的 jar 包名 &ndash;&gt;-->
-<!--        <finalName>${project.artifactId}</finalName>-->
-<!--        <plugins>-->
-<!--            &lt;!&ndash; 打包 &ndash;&gt;-->
-<!--            <plugin>-->
-<!--                <groupId>org.springframework.boot</groupId>-->
-<!--                <artifactId>spring-boot-maven-plugin</artifactId>-->
-<!--                <version>${spring.boot.version}</version>-->
-<!--                <executions>-->
-<!--                    <execution>-->
-<!--                        <goals>-->
-<!--                            <goal>repackage</goal> &lt;!&ndash; 将引入的 jar 打入其中 &ndash;&gt;-->
-<!--                        </goals>-->
-<!--                    </execution>-->
-<!--                </executions>-->
-<!--            </plugin>-->
-<!--        </plugins>-->
-<!--    </build>-->
     <build>
         <plugins>
             <plugin>
diff --git a/sdk/src/main/java/com/iailab/sdk/auth/client/IailabAuthClient.java b/sdk/src/main/java/com/iailab/sdk/auth/client/IailabAuthClient.java
index 4ba282b..e8d4772 100644
--- a/sdk/src/main/java/com/iailab/sdk/auth/client/IailabAuthClient.java
+++ b/sdk/src/main/java/com/iailab/sdk/auth/client/IailabAuthClient.java
@@ -1,16 +1,23 @@
 package com.iailab.sdk.auth.client;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.iailab.sdk.auth.client.dto.TokenDTO;
 import com.iailab.sdk.auth.client.vo.AuthLoginReqVO;
+import com.iailab.sdk.auth.config.AuthProperties;
 import org.springframework.core.ParameterizedTypeReference;
 import org.springframework.http.*;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Service;
 import org.springframework.util.Assert;
+import org.springframework.util.Base64Utils;
+import org.springframework.util.ObjectUtils;
 import org.springframework.web.client.RestTemplate;
 
+import java.nio.charset.StandardCharsets;
 import java.util.*;
+
+import static com.iailab.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
+import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.iailab.sdk.auth.enums.ErrorCodeConstants.*;
 
 /**
  * @author Houzhongjian
@@ -21,48 +28,42 @@
 @Service
 public class IailabAuthClient {
 
-    /**
-     * 平台地址
-     */
-    public static String BASE_URL = "http://172.16.8.100:48080/admin-api";
-
-    /**
-     * 租户编号
-     */
-    public static String TENANT_ID = "1";
-
     private static final RestTemplate restTemplate = new RestTemplate();
+
+    private static AuthProperties authProperties;
+
+    public static void setAuthProperties(AuthProperties properties) {
+        authProperties = properties;
+    }
+
+    private static final String GRAND_TYPE = "password";
+
+    private static final String SCOPE = "user.read user.write";
+
+    // 鉴权token
+    public static String accessToken;
+    // 刷新token
+    public static String refreshToken;
+    // 鉴权token过期时间
+    public static Long expireTime;
 
     /**
      * 用户名密码方式获取平台token
      */
-    public static synchronized TokenDTO login(AuthLoginReqVO loginReqVO) throws Exception {
+    public static synchronized void authenticate() throws Exception {
         System.out.println("登录获取平台token");
-        ObjectMapper objectMapper = new ObjectMapper();
-        String paramString = objectMapper.writeValueAsString(loginReqVO);
         // 1.1 构建请求头
         HttpHeaders headers = new HttpHeaders();
         addClientHeader(headers);
         headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
         // 1.2 构建authenticate请求URL
-        String authenticateUrl = BASE_URL + "/system/auth/login";
-        // 2. 执行请求
-        ResponseEntity<Map<String, Object>> exchange = restTemplate.exchange(
-                authenticateUrl,
-                HttpMethod.POST,
-                new HttpEntity<>(paramString, headers),
-                new ParameterizedTypeReference<Map<String, Object>>() {
-                });
-        return handleResponse(exchange);
-    }
-
-    public static synchronized TokenDTO refreshToken(String refreshToken) {
-        System.out.println("刷新token");
-        // 1.1 构建请求头
-        HttpHeaders headers = new HttpHeaders();
-        addClientHeader(headers);
         // 1.2 构建authenticate请求URL
-        String authenticateUrl = BASE_URL + "/system/auth/refresh-token?refreshToken=" + refreshToken;
+        String authenticateUrl = authProperties.getBaseUrl() + "/system/oauth2/token?"
+                // 密码模式的参数
+                + "&grant_type=" + GRAND_TYPE
+                + "&username=" + authProperties.getUsername()
+                + "&password=" + authProperties.getPassword()
+                + "&scope=" + SCOPE;
         // 2. 执行请求
         ResponseEntity<Map<String, Object>> exchange = restTemplate.exchange(
                 authenticateUrl,
@@ -70,24 +71,55 @@
                 new HttpEntity<>(headers),
                 new ParameterizedTypeReference<Map<String, Object>>() {
                 });
-        return handleResponse(exchange);
+        Map<String, Object> authMap = exchange.getBody();
+        if(AUTH_BAD_CREDENTIALS.getCode().equals(authMap.get("code"))) {
+            throw exception(AUTH_BAD_CREDENTIALS);
+        } else if(AUTH_LOGIN_BAD_CREDENTIALS.getCode().equals(authMap.get("code"))) {
+            throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
+        }
+        Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
+        accessToken = authMap.get("access_token").toString();
+        refreshToken = authMap.get("refresh_token").toString();
+        expireTime = Long.valueOf(authMap.get("expires_time").toString());
+    }
+
+    public static synchronized void refreshToken() throws Exception {
+        System.out.println("刷新token");
+        // 1.1 构建请求头
+        HttpHeaders headers = new HttpHeaders();
+        addClientHeader(headers);
+        // 1.2 构建authenticate请求URL
+        String authenticateUrl = authProperties.getBaseUrl() + "/system/auth/client-refresh-token?refreshToken=" + refreshToken+ "&clientId=" + authProperties.getClientId();
+        // 2. 执行请求
+        ResponseEntity<Map<String, Object>> exchange = restTemplate.exchange(
+                authenticateUrl,
+                HttpMethod.POST,
+                new HttpEntity<>(headers),
+                new ParameterizedTypeReference<Map<String, Object>>() {
+                });
+        Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
+        Map<String, Object> authMap = exchange.getBody();
+        //刷新token过期,重新获取token
+        if (!ObjectUtils.isEmpty(authMap.get("code"))) {
+            Integer code = Integer.valueOf(authMap.get("code").toString());
+            if (code == 401) {
+                authenticate();
+            } else {
+                throw exception(AUTH_REFRESH_TOKEN_ERROR);
+            }
+        } else {
+            accessToken = authMap.get("access_token").toString();
+            expireTime = Long.valueOf(authMap.get("expires_time").toString());
+        }
     }
 
     private static void addClientHeader(HttpHeaders headers) {
+        // client 拼接,需要 BASE64 编码
+        String client = authProperties.getClientId() + ":" + authProperties.getClientSecret();
+        client = Base64Utils.encodeToString(client.getBytes(StandardCharsets.UTF_8));
+        headers.add("Authorization", "Basic " + client);
         headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
-        headers.set("tenant-id", TENANT_ID);
+        headers.set("tenant-id", authProperties.getTenantId());
     }
 
-    // 统一处理响应
-    private static <T> TokenDTO handleResponse(ResponseEntity<T> response) {
-        Assert.isTrue(response.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
-        System.out.println(response);
-        TokenDTO authTokenDTO = new TokenDTO();
-        Map<String, Object> authMap = (Map<String, Object>)response.getBody();
-        Map<String, Object> tokenData = (Map<String, Object>)authMap.get("data");
-        authTokenDTO.setAccessToken(tokenData.get("accessToken").toString());
-        authTokenDTO.setRefreshToken(tokenData.get("refreshToken").toString());
-        authTokenDTO.setExpiresTime(Long.valueOf(tokenData.get("expiresTime").toString()));
-        return authTokenDTO;
-    }
 }
\ No newline at end of file
diff --git a/sdk/src/main/java/com/iailab/sdk/auth/client/vo/AuthLoginRespVO.java b/sdk/src/main/java/com/iailab/sdk/auth/client/vo/AuthLoginRespVO.java
new file mode 100644
index 0000000..d72d61d
--- /dev/null
+++ b/sdk/src/main/java/com/iailab/sdk/auth/client/vo/AuthLoginRespVO.java
@@ -0,0 +1,24 @@
+package com.iailab.sdk.auth.client.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class AuthLoginRespVO {
+
+    private Long userId;
+
+    private String accessToken;
+
+    private String refreshToken;
+
+    private LocalDateTime expiresTime;
+
+}
diff --git a/sdk/src/main/java/com/iailab/sdk/auth/config/AuthProperties.java b/sdk/src/main/java/com/iailab/sdk/auth/config/AuthProperties.java
new file mode 100644
index 0000000..08a8d60
--- /dev/null
+++ b/sdk/src/main/java/com/iailab/sdk/auth/config/AuthProperties.java
@@ -0,0 +1,15 @@
+package com.iailab.sdk.auth.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties(prefix = "iailab.token")
+@Data
+public class AuthProperties {
+    private String baseUrl;
+    private String tenantId;
+    private String username;
+    private String password;
+    private String clientId;
+    private String clientSecret;
+}
\ No newline at end of file
diff --git a/sdk/src/main/java/com/iailab/sdk/auth/config/SdkAutoConfiguration.java b/sdk/src/main/java/com/iailab/sdk/auth/config/SdkAutoConfiguration.java
new file mode 100644
index 0000000..b9cf3fe
--- /dev/null
+++ b/sdk/src/main/java/com/iailab/sdk/auth/config/SdkAutoConfiguration.java
@@ -0,0 +1,28 @@
+package com.iailab.sdk.auth.config;
+
+import com.iailab.sdk.auth.client.IailabAuthClient;
+import com.iailab.sdk.util.http.IailabHttpUtils;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import javax.annotation.PostConstruct;
+
+
+/**
+ * SDK配置文件
+ */
+@Configuration
+@EnableConfigurationProperties(AuthProperties.class)
+public class SdkAutoConfiguration {
+    private final AuthProperties authProperties;
+
+    public SdkAutoConfiguration(AuthProperties authProperties) {
+        this.authProperties = authProperties;
+    }
+
+    @PostConstruct
+    public void init() {
+        IailabAuthClient.setAuthProperties(authProperties);
+        IailabHttpUtils.setAuthProperties(authProperties);
+    }
+}
diff --git a/sdk/src/main/java/com/iailab/sdk/auth/config/SdkConfiguration.java b/sdk/src/main/java/com/iailab/sdk/auth/config/SdkConfiguration.java
deleted file mode 100644
index 6b7d958..0000000
--- a/sdk/src/main/java/com/iailab/sdk/auth/config/SdkConfiguration.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.iailab.sdk.auth.config;
-
-import org.springframework.context.annotation.Configuration;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Properties;
-
-/**
- * SDK配置文件
- */
-@Configuration
-public class SdkConfiguration {
-
-    private String baseUrl;
-
-    public String getTenantId() {
-        return tenantId;
-    }
-
-    private String tenantId;
-
-    public static SdkConfiguration load() {
-        SdkConfiguration config = new SdkConfiguration();
-
-        try(InputStream is = SdkConfiguration.class.getResourceAsStream("/application.yaml")) {
-            Properties props = new Properties();
-            props.load(is);
-            config.baseUrl = props.getProperty("base-url");
-            config.tenantId = props.getProperty("tenant-id");
-        } catch (IOException e) {
-            // 处理异常或使用默认值
-        }
-
-        if(config.baseUrl == null) {
-            throw new IllegalStateException("BaseUrl must be configured");
-        }
-        if(config.tenantId  == null) {
-            throw new IllegalStateException("TenantId must be configured");
-        }
-
-        return config;
-    }
-
-    public String getBaseUrl() {
-        return baseUrl;
-    }
-}
diff --git a/sdk/src/main/java/com/iailab/sdk/auth/enums/ErrorCodeConstants.java b/sdk/src/main/java/com/iailab/sdk/auth/enums/ErrorCodeConstants.java
new file mode 100644
index 0000000..46af76a
--- /dev/null
+++ b/sdk/src/main/java/com/iailab/sdk/auth/enums/ErrorCodeConstants.java
@@ -0,0 +1,16 @@
+package com.iailab.sdk.auth.enums;
+
+import com.iailab.framework.common.exception.ErrorCode;
+
+/**
+ * System 错误码枚举类
+ *
+ * system 系统,使用 1-002-000-000 段
+ */
+public interface ErrorCodeConstants {
+
+    // ========== AUTH 模块 1-002-000-000 ==========
+    ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1_002_000_000, "登录失败,账号密码不正确");
+    ErrorCode AUTH_BAD_CREDENTIALS = new ErrorCode(400, "client_id 或 client_secret 未正确传递");
+    ErrorCode AUTH_REFRESH_TOKEN_ERROR = new ErrorCode(1_002_000_001, "刷新token失败");
+}
diff --git a/sdk/src/main/java/com/iailab/sdk/util/http/IailabHttpUtils.java b/sdk/src/main/java/com/iailab/sdk/util/http/IailabHttpUtils.java
index b5cf6c4..9c1eda9 100644
--- a/sdk/src/main/java/com/iailab/sdk/util/http/IailabHttpUtils.java
+++ b/sdk/src/main/java/com/iailab/sdk/util/http/IailabHttpUtils.java
@@ -1,6 +1,7 @@
 package com.iailab.sdk.util.http;
 
 import com.iailab.sdk.auth.client.IailabAuthClient;
+import com.iailab.sdk.auth.config.AuthProperties;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.methods.HttpGet;
@@ -8,6 +9,7 @@
 import org.apache.http.entity.StringEntity;
 import org.apache.http.util.EntityUtils;
 import org.springframework.util.CollectionUtils;
+import org.springframework.util.ObjectUtils;
 
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
@@ -16,6 +18,12 @@
 
 public class IailabHttpUtils {
 
+    private static AuthProperties authProperties;
+
+    public static void setAuthProperties(AuthProperties properties) {
+        authProperties = properties;
+    }
+
     /**
      *
      * @param url
@@ -23,8 +31,9 @@
      * @param charset
      * @return
      */
-    public static String doGet(String url, Map<String, String> map, String charset, String accessToken) {
+    public static String doGet(String url, Map<String, String> map, String charset) throws Exception {
         System.out.println("start doGet url: " + url);
+        checkToken();
         org.apache.http.client.HttpClient httpClient = null;
         HttpGet httpGet = null;
         String result = null;
@@ -51,8 +60,9 @@
             //设置参数
             httpGet.addHeader("Accept", "application/json");
             httpGet.addHeader("Content-Type", "application/json;charset=UTF-8");
-            httpGet.addHeader("Authorization", "Bearer " + accessToken);
-            httpGet.addHeader("Tenant-Id", String.valueOf(IailabAuthClient.TENANT_ID));
+            httpGet.addHeader("Authorization", "Bearer " + IailabAuthClient.accessToken);
+            httpGet.addHeader("Tenant-Id", String.valueOf(authProperties.getTenantId()));
+
             HttpResponse response = httpClient.execute(httpGet);
             if (response != null) {
                 HttpEntity resEntity = response.getEntity();
@@ -73,8 +83,9 @@
      * @param charset
      * @return
      */
-    public static String doPost(String url, String json, String charset, String accessToken) {
+    public static String doPost(String url, String json, String charset) throws Exception {
         System.out.println("start doPost url: " + url);
+        checkToken();
         org.apache.http.client.HttpClient httpClient = null;
         HttpPost httpPost = null;
         String result = null;
@@ -84,8 +95,8 @@
             //设置参数
             httpPost.addHeader("Accept", "application/json");
             httpPost.addHeader("Content-Type", "application/json;charset=UTF-8");
-            httpPost.addHeader("Authorization", "Bearer " + accessToken);
-            httpPost.addHeader("Tenant-Id", String.valueOf(IailabAuthClient.TENANT_ID));
+            httpPost.addHeader("Authorization", "Bearer " + IailabAuthClient.accessToken);
+            httpPost.addHeader("Tenant-Id", String.valueOf(authProperties.getTenantId()));
             StringEntity stringEntity = new StringEntity(json);
             stringEntity.setContentEncoding("UTF-8");
             stringEntity.setContentType("application/json");
@@ -103,4 +114,13 @@
         return result;
     }
 
+    private static void checkToken() throws Exception {
+        //第一次请求或者token过期,需要重新获取token
+        if(ObjectUtils.isEmpty(IailabAuthClient.accessToken)) {
+            IailabAuthClient.authenticate();
+        } else if (IailabAuthClient.expireTime < System.currentTimeMillis() / 1000) {
+            IailabAuthClient.refreshToken();
+        }
+    }
+
 }
diff --git a/sdk/src/main/resources/META-INF/spring.factories b/sdk/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..4ac7d96
--- /dev/null
+++ b/sdk/src/main/resources/META-INF/spring.factories
@@ -0,0 +1 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.iailab.sdk.auth.config.SdkAutoConfiguration
\ No newline at end of file
diff --git a/sdk/src/main/resources/application-dev.yaml b/sdk/src/main/resources/application-dev.yaml
deleted file mode 100644
index 0a38e15..0000000
--- a/sdk/src/main/resources/application-dev.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
---- #################### SDK相关配置 ####################
-
-iailab:
-  auth:
-    base-url: http://172.16.8.100:48080/admin-api
-    tenant-id: 1
\ No newline at end of file
diff --git a/sdk/src/main/resources/application.yaml b/sdk/src/main/resources/application.yaml
index 946abfd..6adf8aa 100644
--- a/sdk/src/main/resources/application.yaml
+++ b/sdk/src/main/resources/application.yaml
@@ -1,13 +1,4 @@
-spring:
-  application:
-    name: sdk
-
-  profiles:
-    active: dev
-
-iailab:
-  auth:
-    base-url: http://172.16.8.100:48080/admin-api
-    tenant-id: 1
-
-
+#spring:
+#  application:
+#    name: sdk
+#
diff --git a/sdk/src/test/java/com/iailab/sdk/IailabClientTest.java b/sdk/src/test/java/com/iailab/sdk/IailabClientTest.java
index 7f15846..6ca996c 100644
--- a/sdk/src/test/java/com/iailab/sdk/IailabClientTest.java
+++ b/sdk/src/test/java/com/iailab/sdk/IailabClientTest.java
@@ -18,21 +18,13 @@
 
     @Test
     public void testLogin() throws Exception {
-        // 准备参数
-        AuthLoginReqVO authLoginReqVO = new AuthLoginReqVO();
-        authLoginReqVO.setUsername("sysadmin");
-        authLoginReqVO.setPassword("iailab2019");
-        TokenDTO login = IailabAuthClient.login(authLoginReqVO);
-        System.out.println(login.getAccessToken());
-        System.out.println(login.getRefreshToken());
+        IailabAuthClient.authenticate();
     }
 
     @Test
-    public void testRefreshToken() {
+    public void testRefreshToken() throws Exception {
         // 准备参数
-        String refreshToken = "1d62031562364ed29d6d414fea97e2dd";
-        TokenDTO tokenDTO = IailabAuthClient.refreshToken(refreshToken);
-        System.out.println(tokenDTO);
+        IailabAuthClient.refreshToken();
     }
 
 

--
Gitblit v1.9.3