refactor: 重构API请求处理架构和用户管理

- 新增JHApiRequestHandler接口支持自定义请求头处理
- 重构JHApiExecutionAbstract为依赖注入提供更好支持
- 优化JHRequestExecution支持用户上下文管理
- 增强Spring Boot自动配置支持请求处理器注入
- 更新版本号至2.0.6
- 删除废弃的JHApiExecution类
- 修正测试类中的命名错误

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
yanlongqi
2025-11-22 15:07:43 +08:00
parent 360e30c4ff
commit 1c00f7eaee
20 changed files with 209 additions and 76 deletions

View File

@@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>jhinno-openapi-java-sdk</artifactId>
<version>2.0.5</version>
<version>2.0.6</version>
<packaging>jar</packaging>
<name>Jhinno OpenAPI SDK for Java</name>
<description>The Jhinno OpenAPI SDK for Java used for accessing Jhinno OpenApi Service</description>

View File

@@ -1,12 +0,0 @@
package com.jhinno.sdk.openapi;
import com.jhinno.sdk.openapi.api.JHRequestExecution;
public interface JHApiExecution {
/**
* 初始化API执行器
*/
void init(JHRequestExecution execution);
}

View File

@@ -1,13 +1,20 @@
package com.jhinno.sdk.openapi;
import com.jhinno.sdk.openapi.api.JHRequestExecution;
import lombok.Setter;
@Setter
public abstract class JHApiExecutionAbstract {
// 提供setter方法支持依赖注入
protected JHRequestExecution execution;
public void init(JHRequestExecution execution) {
// 默认构造函数,允许子类不实现构造方法
public JHApiExecutionAbstract() {
}
// 带参数的构造函数,允许直接注入
public JHApiExecutionAbstract(JHRequestExecution execution) {
this.execution = execution;
}

View File

@@ -23,13 +23,18 @@ public class JHApiExecutionManage {
*
* @param appformBaseUrl 景行API的URL
*/
public JHApiExecutionManage(String appformBaseUrl) {
public JHApiExecutionManage(String appformBaseUrl, JHApiRequestHandler requestHandler) {
JHApiClient client = new JHApiClient(appformBaseUrl);
client.initDefaultApiClient();
EXECUTION = new JHRequestExecution(client);
EXECUTION = new JHRequestExecution(client, requestHandler);
initApiExecution();
}
public JHApiExecutionManage(String appformBaseUrl) {
this(appformBaseUrl, new JHApiRequestHandler() {
});
}
/**
* 创建一个带有自定义HTTP客户端的API执行管理器
*
@@ -71,7 +76,7 @@ public class JHApiExecutionManage {
* @param execution 自定义的执行器实例
*/
public void registerApiExecution(JHApiExecutionAbstract execution) {
execution.init(EXECUTION);
execution.setExecution(EXECUTION);
API_CLIENT_MAP.put(execution.getClass(), execution);
}

View File

@@ -0,0 +1,26 @@
package com.jhinno.sdk.openapi;
import java.util.Map;
public interface JHApiRequestHandler {
/**
* 获取当前登录的用户名
*
* @return 用户名
*/
default String getCurrentUserName() {
return null;
}
/**
* 构建一个带token的请求头
*
* @param headers 处理器的请求头
* @return 请求头
*/
default Map<String, Object> getHeaders(Map<String, Object> headers) {
return headers;
}
}

View File

@@ -43,6 +43,11 @@ import java.util.concurrent.ConcurrentHashMap;
@NoArgsConstructor
public class JHRequestExecution {
/**
* 用户令牌的缓存
*/
private static final Map<String, TokenInfo> TOKEN_INFO_MAP = new ConcurrentHashMap<>(20);
/**
* JHApiClient实例
*/
@@ -84,29 +89,49 @@ public class JHRequestExecution {
*/
private String accessKeySecret;
private JHApiRequestHandler requestHandler;
/**
* 获取一个执行器的实例
*
* @param jhApiClient 请求的客户端
* @param requestHandler 请求头处理器
*/
public JHRequestExecution(JHApiClient jhApiClient, JHApiRequestHandler requestHandler) {
this.jhApiClient = jhApiClient;
this.requestHandler = requestHandler;
}
/**
* 创建一个默认的请求处理器
*
* @param jhApiClient 请求的客户端
*/
public JHRequestExecution(JHApiClient jhApiClient) {
this.jhApiClient = jhApiClient;
this.requestHandler = new JHApiRequestHandler() {
};
}
public String getUserName(String userName) {
if (StringUtils.isNotBlank(userName)) {
return userName;
}
return requestHandler.getCurrentUserName();
}
/**
* 用户令牌的缓存
*/
private static final Map<String, TokenInfo> TOKEN_INFO_MAP = new ConcurrentHashMap<>(20);
/**
* 设置是否使用服务器时间
* 获取当前用户的token
*
* @param usedServerTime 是否使用服务器时间
* @return 用户token
*/
public void setUsedServerTime(boolean usedServerTime) {
isUsedServerTime = usedServerTime;
public String getToken() {
return getToken(requestHandler.getCurrentUserName());
}
/**
* 获取用户的Token
*
@@ -122,9 +147,9 @@ public class JHRequestExecution {
// 防止因为服务器时间的问题二导致token不可用可以通过此配置提前获取token
int tokenEffectiveTime = (tokenTimeout - tokenResidueTime) * 60 * 1000;
// 如果是强制获取用户令牌为空、用户令牌过期等,则获取令牌
if (isForceGetToken || tokenInfo == null
|| System.currentTimeMillis() - tokenInfo.getCurrentTimestamp() > tokenEffectiveTime) {
// 如果是强制获取用户令牌为空、用户令牌不存在、用户令牌过期等,则获取令牌
boolean isGetToken = isForceGetToken || tokenInfo == null || System.currentTimeMillis() - tokenInfo.getCurrentTimestamp() > tokenEffectiveTime;
if (isGetToken) {
Map<String, Object> params = new HashMap<>(2);
params.put("timeout", tokenTimeout);
String currentTimeMillis = getCurrentTimeMillis();
@@ -139,13 +164,9 @@ public class JHRequestExecution {
} catch (Exception e) {
throw new ClientException("AES加密失败失败原因" + e.getMessage(), e);
}
String url = JHApiClient.getUrl(AuthPathConstant.AUTH_TOKEN_PATH, params);
Map<String, String> token = get(url, new TypeReference<ResponseResult<Map<String, String>>>() {
});
tokenInfo = new TokenInfo();
tokenInfo.setUserName(username);
tokenInfo.setToken(token.get("token"));
tokenInfo.setToken(requestToken(params));
tokenInfo.setCurrentTimestamp(System.currentTimeMillis());
TOKEN_INFO_MAP.put(username, tokenInfo);
}
@@ -153,7 +174,9 @@ public class JHRequestExecution {
}
/**
* @return
* 获得当前的时间
*
* @return 当前时间
*/
public String getCurrentTimeMillis() {
if (authType == AuthType.ACCESS_SECRET_MODE || !isUsedServerTime) {
@@ -162,14 +185,37 @@ public class JHRequestExecution {
return jhApiClient.getAppformServerCurrentTimeMillis();
}
/**
* 构建一个带token的请求头
* 获得最终的请求头
*
* @param username 用户名
* @param isContentType 是否携带默认的Content-type默认为{@link ContentType#APPLICATION_JSON}
* @return 请求头
*/
public Map<String, Object> getHeaders(String username, boolean isContentType) {
Map<String, Object> defaultHeaders = getDefaultHeaders(getUserName(username), isContentType);
return requestHandler.getHeaders(defaultHeaders);
}
/**
* 获得当前用户的请求头
*
* @param isContentType 是否携带默认的Content-type默认为{@link ContentType#APPLICATION_JSON}
* @return 请求头
*/
public Map<String, Object> getHeaders(boolean isContentType) {
return getHeaders(null, isContentType);
}
/**
* 构建一个默认参数的请求头
*
* @param username 用户名
* @param isContentType 是否携带默认的Content-type默认为{@link ContentType#APPLICATION_JSON}
* @return 请求头
*/
private Map<String, Object> getDefaultHeaders(String username, boolean isContentType) {
Map<String, Object> headers = new HashMap<>();
// 默认请求json数据
if (isContentType) {
@@ -198,6 +244,16 @@ public class JHRequestExecution {
return headers;
}
/**
* 获得一个签名
*
* @param currentTimeMillis 时间戳
* @return 签名
*/
public String getsSignature(String currentTimeMillis) {
return getsSignature(requestHandler.getCurrentUserName(), currentTimeMillis);
}
/**
* 获取签名
*
@@ -259,6 +315,17 @@ public class JHRequestExecution {
return result.getData();
}
private String requestToken(Map<String, Object> params) {
String url = JHApiClient.getUrl(AuthPathConstant.AUTH_TOKEN_PATH, params);
ResponseResult<Map<String, String>> result = jhApiClient.get(url, new TypeReference<ResponseResult<Map<String, String>>>() {
});
if (StringUtils.equals(result.getResult(), CommonConstant.FAILED)) {
throw new ServiceException(url, result.getCode(), result.getMessage());
}
Map<String, String> token = result.getData();
return token.get("token");
}
/**
* 发起一个有返回值的POST请求
*
@@ -412,7 +479,7 @@ public class JHRequestExecution {
}
/**
* 退出用户的登录,释放许可当用户退出登录后建议清除用户的token信息
* 退出用户的登录,释放许可
*
* @param username 用户名
*/
@@ -420,4 +487,11 @@ public class JHRequestExecution {
delete(AuthPathConstant.AUTH_LOGOUT, username);
TOKEN_INFO_MAP.remove(username);
}
/**
* 退出当前用户的登录
*/
public void logout() {
logout(requestHandler.getCurrentUserName());
}
}

View File

@@ -2,16 +2,7 @@ package com.jhinno.sdk.openapi.test;
import com.jhinno.sdk.openapi.AuthType;
import com.jhinno.sdk.openapi.JHApiExecutionManage;
import com.jhinno.sdk.openapi.api.app.JHAppApiExecution;
import com.jhinno.sdk.openapi.api.data.JHDataApiExecution;
import com.jhinno.sdk.openapi.api.file.JHFileApiExecution;
import com.jhinno.sdk.openapi.api.job.JHJobApiExecution;
import com.jhinno.sdk.openapi.api.organization.JHDepartmentApiExecution;
import com.jhinno.sdk.openapi.api.organization.JHUserApiExecution;
import com.jhinno.sdk.openapi.client.JHApiClient;
import java.util.HashMap;
import java.util.Map;
import com.jhinno.sdk.openapi.JHApiRequestHandler;
/**
* SDK Client 的配置
@@ -21,18 +12,26 @@ import java.util.Map;
*/
public class JHClientConfig {
public static final JHApiRequestHandler REQUEST_HANDLER = new JHApiRequestHandler() {
@Override
public String getCurrentUserName() {
return "yanlongqi";
}
};
public static final String APPFORM_SERVER_URL = "https://172.20.0.200";
public static final String ACCESS_KEY = "8147c7470bfd4a27952fe750c6bc7cef";
public static final String ACCESS_KEY_SECRET = "899b13f590394c3daafc6468fed4b1df";
/**
* 创建一个API执行器管理器
*/
public static final JHApiExecutionManage API_EXECUTRON_MANAGE = new JHApiExecutionManage(
"https://172.20.0.200");
public static final JHApiExecutionManage API_EXECUTION_MANAGE = new JHApiExecutionManage(APPFORM_SERVER_URL, REQUEST_HANDLER);
public static final String ACCESS_KEY = "8147c7470bfd4a27952fe750c6bc7cef";
public static final String ACCESS_KEY_SECRET = "899b13f590394c3daafc6468fed4b1df";
static {
API_EXECUTRON_MANAGE.configureApiExecution(t -> {
API_EXECUTION_MANAGE.configureApiExecution(t -> {
// 默认为使用Token模式如何使用的Token模式则不需要配置ACCESS_KEY和ACCESS_KEY SECRET
t.setAuthType(AuthType.ACCESS_SECRET_MODE);
t.setAccessKey(ACCESS_KEY);

View File

@@ -19,7 +19,7 @@ public class AppApiTest {
/**
* 获得一个调用应用接口的执行器
*/
public static final JHAppApiExecution jhAppApiExecution = JHClientConfig.API_EXECUTRON_MANAGE
public static final JHAppApiExecution jhAppApiExecution = JHClientConfig.API_EXECUTION_MANAGE
.getApiExecution(JHAppApiExecution.class);
/**
@@ -29,7 +29,7 @@ public class AppApiTest {
public void testStartApp() {
AppStartRequest appStartRequest = new AppStartRequest();
appStartRequest.setStartNew(true);
AppStartedInfo appStartedInfo = jhAppApiExecution.desktopStart("jhadmin", "linux_desktop", appStartRequest);
AppStartedInfo appStartedInfo = jhAppApiExecution.desktopStart(null, "linux_desktop", appStartRequest);
System.out.println("会话ID" + appStartedInfo.getDesktopId());
System.out.println("JhAppURL" + appStartedInfo.getJhappUrl());
System.out.println("WebURL:" + appStartedInfo.getWebSessionUrl());

View File

@@ -14,7 +14,7 @@ import java.util.Arrays;
*/
public class DataApiTest {
public static final JHDataApiExecution execution =JHClientConfig.API_EXECUTRON_MANAGE
public static final JHDataApiExecution execution =JHClientConfig.API_EXECUTION_MANAGE
.getApiExecution(JHDataApiExecution.class);
/**

View File

@@ -20,7 +20,7 @@ import java.util.List;
*/
public class FileApiTest {
private static final JHFileApiExecution execution = JHClientConfig.API_EXECUTRON_MANAGE
private static final JHFileApiExecution execution = JHClientConfig.API_EXECUTION_MANAGE
.getApiExecution(JHFileApiExecution.class);
/**

View File

@@ -17,7 +17,7 @@ import java.util.Map;
*/
public class JobApiTest {
private static final JHJobApiExecution execution = JHClientConfig.API_EXECUTRON_MANAGE
private static final JHJobApiExecution execution = JHClientConfig.API_EXECUTION_MANAGE
.getApiExecution(JHJobApiExecution.class);
/**

View File

@@ -13,7 +13,7 @@ import org.junit.Test;
*/
public class DepartmentApiTest {
private static final JHDepartmentApiExecution execution = JHClientConfig.API_EXECUTRON_MANAGE
private static final JHDepartmentApiExecution execution = JHClientConfig.API_EXECUTION_MANAGE
.getApiExecution(JHDepartmentApiExecution.class);

View File

@@ -15,7 +15,7 @@ import org.junit.Test;
*/
public class UserApiTest {
private static final JHUserApiExecution execution = JHClientConfig.API_EXECUTRON_MANAGE
private static final JHUserApiExecution execution = JHClientConfig.API_EXECUTION_MANAGE
.getApiExecution(JHUserApiExecution.class);

View File

@@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>cim.jhinno</groupId>
<artifactId>jhinno-openapi-sdk-spring-boot-example</artifactId>
<version>2.0.5</version>
<version>2.0.6</version>
<packaging>jar</packaging>
<name>Jhinno OpenAPI SDK for Java SpringBoot Example</name>
<description>The Jhinno OpenAPI SDK for Java used for accessing Jhinno OpenApi Service</description>
@@ -22,7 +22,7 @@
<dependency>
<groupId>com.jhinno</groupId>
<artifactId>jhinno-openapi-sdk-spring-boot-starter</artifactId>
<version>2.0.5</version>
<version>2.0.6</version>
</dependency>
<dependency>

View File

@@ -0,0 +1,13 @@
package com.jhinno.sdk.openapi.example;
import com.jhinno.sdk.openapi.JHApiRequestHandler;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ApiConfig implements JHApiRequestHandler {
@Override
public String getCurrentUserName() {
return "yanlongqi";
}
}

View File

@@ -1,6 +1,6 @@
jhinno:
openapi:
server-url: https://192.168.0.22
auth-type: token_mode
access-key: 3f03747f147942bd8debd81b6c9c6a80
access-key-secret: e0681859b91c499eb1d2c8e09cea3242
server-url: https://172.20.0.200
auth-type: access_secret_mode
access-key: 8147c7470bfd4a27952fe750c6bc7cef
access-key-secret: 899b13f590394c3daafc6468fed4b1df

View File

@@ -1,7 +1,9 @@
package com.jhinno.sdk.openapi.example.test.extend;
import com.jhinno.sdk.openapi.api.app.AppStartedInfo;
import com.jhinno.sdk.openapi.example.api.extend.FileSystemType;
import com.jhinno.sdk.openapi.example.api.extend.JHFileApiExtendExecution;
import com.jhinno.sdk.openapi.utils.JsonUtil;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@@ -24,6 +26,7 @@ public class JHFileApiExtendTest {
@Test
void testStartApp() {
jhAppApiExecution.desktopStart("jhadmin","linux_desktop");
AppStartedInfo linuxDesktop = jhAppApiExecution.desktopStart(null, "linux_desktop");
System.out.println(JsonUtil.objectToString(linuxDesktop));
}
}

View File

@@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>jhinno-openapi-sdk-spring-boot-starter</artifactId>
<version>2.0.5</version>
<version>2.0.6</version>
<packaging>jar</packaging>
<name>Jhinno OpenAPI SDK for Java SpringBoot Starter</name>
<description>The Jhinno OpenAPI SDK for Java used for accessing Jhinno OpenApi Service</description>
@@ -21,7 +21,7 @@
<dependency>
<groupId>com.jhinno</groupId>
<artifactId>jhinno-openapi-java-sdk</artifactId>
<version>2.0.5</version>
<version>2.0.6</version>
</dependency>
<dependency>

View File

@@ -1,5 +1,6 @@
package com.jhinno.sdk.openapi.autoconfigure;
import com.jhinno.sdk.openapi.JHApiRequestHandler;
import com.jhinno.sdk.openapi.api.JHRequestExecution;
import com.jhinno.sdk.openapi.client.JHApiClient;
import com.jhinno.sdk.openapi.client.JHApiHttpClientImpl;
@@ -30,8 +31,15 @@ public class JHOpenapiClientAutoConfigure {
}
@Bean
public JHRequestExecution requestExecution(JHApiClient jhApiClient, JHOpenapiProperties properties) {
JHRequestExecution requestExecution = new JHRequestExecution(jhApiClient);
@ConditionalOnMissingBean
public JHApiRequestHandler defaultRequestHandler() {
return new JHApiRequestHandler() {
};
}
@Bean
public JHRequestExecution requestExecution(JHApiClient jhApiClient, JHOpenapiProperties properties, JHApiRequestHandler requestHandler) {
JHRequestExecution requestExecution = new JHRequestExecution(jhApiClient, requestHandler);
requestExecution.setForceGetToken(properties.isForceGetToken());
requestExecution.setAuthType(properties.getAuthType());
requestExecution.setAccessKey(properties.getAccessKey());

View File

@@ -1,6 +1,6 @@
package com.jhinno.sdk.openapi.autoconfigure;
import com.jhinno.sdk.openapi.JHApiExecution;
import com.jhinno.sdk.openapi.JHApiExecutionAbstract;
import com.jhinno.sdk.openapi.api.JHRequestExecution;
import com.jhinno.sdk.openapi.api.app.JHAppApiExecution;
import com.jhinno.sdk.openapi.api.data.JHDataApiExecution;
@@ -9,11 +9,14 @@ import com.jhinno.sdk.openapi.api.job.JHJobApiExecution;
import com.jhinno.sdk.openapi.api.organization.JHDepartmentApiExecution;
import com.jhinno.sdk.openapi.api.organization.JHUserApiExecution;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
import java.util.List;
/**
* openapi执行器自动配置
*
@@ -26,39 +29,46 @@ public class JHOpenapiExecutionAutoconfigure implements BeanPostProcessor {
private final JHRequestExecution jhRequestExecution;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (bean instanceof JHApiExecution) {
((JHApiExecution) bean).init(jhRequestExecution);
if (bean instanceof JHApiExecutionAbstract) {
((JHApiExecutionAbstract) bean).setExecution(jhRequestExecution);
}
return bean;
}
@Bean
@ConditionalOnMissingBean
public JHAppApiExecution appApiExecution() {
return new JHAppApiExecution();
}
@Bean
@ConditionalOnMissingBean
public JHDataApiExecution dataApiExecution() {
return new JHDataApiExecution();
}
@Bean
@ConditionalOnMissingBean
public JHFileApiExecution fileApiExecution() {
return new JHFileApiExecution();
}
@Bean
@ConditionalOnMissingBean
public JHJobApiExecution jobApiExecution() {
return new JHJobApiExecution();
}
@Bean
@ConditionalOnMissingBean
public JHDepartmentApiExecution departmentApiExecution() {
return new JHDepartmentApiExecution();
}
@Bean
@ConditionalOnMissingBean
public JHUserApiExecution userApiExecution() {
return new JHUserApiExecution();
}