diff --git a/API-INTERFACE-DOCUMENTATION.md b/API-INTERFACE-DOCUMENTATION.md new file mode 100644 index 0000000..21bb716 --- /dev/null +++ b/API-INTERFACE-DOCUMENTATION.md @@ -0,0 +1,895 @@ +# Terrabase Enterprise API 接口文档 + +## 概述 + +本文档详细描述了 Terrabase Enterprise API 模块中所有服务接口的定义、参数、返回值和使用方法。该模块提供了企业级应用的核心功能,包括证书管理、加解密服务、日志管理、菜单管理、监控告警和用户管理等功能。 + +## 目录 + +- [通用响应格式](#通用响应格式) +- [证书管理服务 (CertificateService)](#证书管理服务-certificateservice) +- [加解密服务 (CryptoService)](#加解密服务-cryptoservice) +- [日志服务 (LogService)](#日志服务-logservice) +- [菜单管理服务 (MenuService)](#菜单管理服务-menuservice) +- [监控告警服务 (MonitoringService)](#监控告警服务-monitoringservice) +- [用户管理服务 (UserManagementService)](#用户管理服务-usermanagementservice) +- [数据模型](#数据模型) + +--- + +## 通用响应格式 + +所有 API 接口都使用统一的响应格式 `ResultVo`: + +```java +public class ResultVo { + private String code; // 响应状态码 + private String msg; // 响应消息 + private T data; // 响应数据 +} +``` + +**状态码说明:** +- `200`: 操作成功 +- 其他: 具体错误码,详见各接口说明 + +--- + +## 证书管理服务 (CertificateService) + +### 接口概述 +提供证书注册、导入、查询等功能。 + +### 接口方法 + +#### 1. 获取证书列表 +```java +ResultVo> listCertificateServiceList() +``` + +**功能描述:** 获取 OMS 管理的所有证书信息 + +**返回值:** `ResultVo>` +- 成功时返回证书信息列表 +- 失败时返回错误信息 + +**CertCollectInfo 字段说明:** + +| 字段名 | 类型 | 说明 | +|--------|------|------| +| productName | String | 产品名称 | +| issueTime | long | 签发时间 | +| expirationTime | long | 过期时间 | +| issuer | String | 颁发者 | +| subject | String | 主题 | +| serialNumber | String | 序列号 | +| certType | String | 证书类型 | +| status | String | 状态 | +| alertBeforeExpirationDays | Integer | 过期前提醒天数 | +| certName | String | 证书名称 | +| productVersion | String | 产品版本 | +| patchVersion | String | 补丁版本 | +| deviceEsn | String | 设备序列号 | + +#### 2. 获取许可证信息 +```java +ResultVo getLicenseInfo() +``` + +**功能描述:** 查询 License 信息 + +**返回值:** `ResultVo` +- 成功时返回许可证信息 +- 失败时返回错误信息 + +**LicenseInfo 字段说明:** + +| 字段名 | 类型 | 说明 | +|--------|------|------| +| status | String | 许可证状态 (0:未导入, 1:已上传未激活, 2:已激活, 3:已注销) | +| sboms | List | SBOM 信息列表 | + +--- + +## 加解密服务 (CryptoService) + +### 接口概述 +提供数据加密和解密功能,支持多种加密算法。 + +### 支持的加密算法 + +```java +public enum CryptoAlgorithm { + AES, // AES加密算法 (默认) + RSA, // RSA非对称加密 + DES, // DES加密算法 + TRIPLE_DES, // 3DES加密算法 + BLOWFISH, // Blowfish加密算法 + CHACHA20 // ChaCha20流密码算法 +} +``` + +### 接口方法 + +#### 1. 数据加密 +```java +String encrypt(String plaintext, CryptoAlgorithm algorithm, String username) +``` + +**功能描述:** 对明文数据进行加密 + +**参数说明:** + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| plaintext | String | 是 | 待加密的明文数据 | +| algorithm | CryptoAlgorithm | 否 | 加密算法,为 null 时使用默认 AES 算法 | +| username | String | 是 | 用户名 | + +**返回值:** `String` - 加密后的密文数据 + +**使用示例:** +```java +// 使用默认 AES 算法加密 +String ciphertext = cryptoService.encrypt("Hello World", null, "admin"); + +// 使用 RSA 算法加密 +String ciphertext = cryptoService.encrypt("Hello World", CryptoAlgorithm.RSA, "admin"); +``` + +#### 2. 数据解密 +```java +String decrypt(String ciphertext, CryptoAlgorithm algorithm, String username) +``` + +**功能描述:** 对密文数据进行解密 + +**参数说明:** + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| ciphertext | String | 是 | 待解密的密文数据 | +| algorithm | CryptoAlgorithm | 否 | 解密算法,为 null 时使用默认 AES 算法 | +| username | String | 是 | 用户名 | + +**返回值:** `String` - 解密后的明文数据 + +**使用示例:** +```java +// 使用默认 AES 算法解密 +String plaintext = cryptoService.decrypt(ciphertext, null, "admin"); + +// 使用 RSA 算法解密 +String plaintext = cryptoService.decrypt(ciphertext, CryptoAlgorithm.RSA, "admin"); +``` + +--- + +## 日志服务 (LogService) + +### 接口概述 +提供审计日志上报和国际化功能。 + +### 接口方法 + +#### 1. 上报审计日志 +```java +ResultVo registerLogs(List logs) +``` + +**功能描述:** 批量上报审计日志 + +**参数说明:** + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| logs | List | 是 | 日志对象列表 | + +**LogAttributeVo 字段说明:** + +| 字段名 | 类型 | 说明 | +|--------|------|------| +| sn | long | 序列号 | +| logType | String | 日志类型 | +| username | String | 用户名 | +| operation | String | 操作 | +| source | String | 来源 | +| terminal | String | 终端 | +| result | String | 结果 | +| flag | String | 标志 | +| paramType | String | 参数类型 | +| detail | String | 详细信息 | + +**返回值:** `ResultVo` - 返回成功处理的日志条数 + +#### 2. 注册审计日志国际化 +```java +ResultVo registryInternational(List logI18ns) +``` + +**功能描述:** 批量注册审计日志的国际化信息 + +**参数说明:** + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| logI18ns | List | 是 | 国际化对象列表 | + +**返回值:** `ResultVo` - 返回注册是否成功 + +--- + +## 菜单管理服务 (MenuService) + +### 接口概述 +提供菜单注册功能。 + +### 接口方法 + +#### 1. 注册菜单信息 +```java +void registerMenuInfo(MenuRegisterInfo menuRegisterInfo) +``` + +**功能描述:** 注册菜单信息到系统 + +**参数说明:** + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| menuRegisterInfo | MenuRegisterInfo | 是 | 菜单注册信息对象 | + +**MenuRegisterInfo 字段说明:** + +| 字段名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| menuId | String | 是 | 菜单ID | +| parentMenuId | String | 否 | 父菜单ID | +| menuNameCode | String | 否 | 菜单名称代码 | +| url | String | 否 | 菜单URL | +| iconUrl | String | 否 | 图标URL | +| en | String | 否 | 英文名称 | +| zh | String | 否 | 中文名称 | +| enable | boolean | 否 | 是否启用 | +| roleScneMap | List | 否 | 角色场景映射 | +| weight | int | 否 | 权重 | + +**返回值:** `void` - 无返回值 + +--- + +## 监控告警服务 (MonitoringService) + +### 接口概述 +提供告警定义注册、告警上报、告警查询等功能。 + +### 接口方法 + +#### 1. 批量注册告警定义 +```java +ResultVo registerEventDefine(RegisterEventDefineReq req) +``` + +**功能描述:** 批量注册告警定义到系统 + +**参数说明:** + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| req | RegisterEventDefineReq | 是 | 注册请求对象 | + +**返回值:** `ResultVo` - 注册结果 + +#### 2. 上报告警 +```java +ResultVo sendEvents(List alarmInfos) +``` + +**功能描述:** 批量上报告警信息 + +**参数说明:** + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| alarmInfos | List | 是 | 告警信息列表 | + +**EventInfo 字段说明:** + +| 字段名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| id | String | 否 | 事件ID (1-32位数字) | +| serialNumber | String | 否 | 序列号 (UUID格式) | +| syncNo | Integer | 否 | 同步号 | +| eventName | String | 否 | 事件名称 (最大255字符) | +| eventType | String | 是 | 事件类型 (alter/event) | +| eventSubject | String | 是 | 事件主题 (最大255字符) | +| eventSubjectType | String | 是 | 事件主题类型 (最大255字符) | +| eventDescription | String | 否 | 事件描述 (最大8000字符) | +| eventDescriptionArgs | List | 否 | 事件描述参数 (最大64个) | +| severity | String | 是 | 严重程度 (warning/minor/major/critical) | +| effect | String | 否 | 影响 (最大8000字符) | +| eventCategory | String | 是 | 事件分类 (最大128字符) | +| possibleCause | String | 否 | 可能原因 (最大8000字符) | +| suggestion | String | 否 | 建议 (最大8000字符) | +| status | String | 是 | 状态 (Uncleared/Cleared) | +| firstOccurTime | String | 否 | 首次发生时间 (最大32字符) | +| clearTime | String | 否 | 清除时间 (最大32字符) | +| evenSource | String | 否 | 事件源 (最大255字符) | +| deviceSn | String | 否 | 设备序列号 (最大255字符) | +| deviceType | String | 否 | 设备类型 (最大255字符) | +| devURL | String | 否 | 设备URL (最大255字符) | +| parts | String | 是 | 部件 (最大64字符) | +| language | String | 否 | 语言 (zh/zh-cn/en/en-us) | +| computerCategory | int | 否 | 计算机分类 | +| shouldDeleted | boolean | 否 | 是否应删除 | +| clearType | String | 否 | 清除类型 | +| defineMatchKey | String | 否 | 定义匹配键 | +| lastEventMatchKey | String | 否 | 最后事件匹配键 | +| shouldSaveDefine | Boolean | 否 | 是否应保存定义 (默认true) | +| deviceId | String | 否 | 设备ID | + +**返回值:** `ResultVo` - 返回是否上报成功 + +#### 3. 批量查询告警信息 +```java +ResultVo getEventsByPage(GetEventsParams getEventsParams) +``` + +**功能描述:** 分页查询告警信息 + +**参数说明:** + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| getEventsParams | GetEventsParams | 是 | 查询参数对象 | + +**返回值:** `ResultVo` - 返回分页查询结果 + +--- + +## 用户管理服务 (UserManagementService) + +### 接口概述 +提供角色、权限、菜单等用户管理功能。 + +### 接口方法 + +#### 1. 批量角色注册 +```java +void batchRegisterRole(RoleRegisterVo roleRegister) +``` + +**功能描述:** 批量注册角色信息 + +**参数说明:** + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| roleRegister | RoleRegisterVo | 是 | 角色注册对象 | + +**RoleRegisterVo 字段说明:** + +| 字段名 | 类型 | 说明 | +|--------|------|------| +| roleRegisterInfos | List | 角色注册信息列表 | +| roleI18nInfos | List | 角色国际化信息列表 | + +**返回值:** `void` - 无返回值 + +#### 2. 批量权限注册 +```java +void registerPermission(List authorityInfos) +``` + +**功能描述:** 批量注册权限信息 + +**参数说明:** + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| authorityInfos | List | 是 | 权限信息列表 | + +**返回值:** `void` - 无返回值 + +#### 3. 获取用户资源组列表 +```java +List getUserGroups(String userName) +``` + +**功能描述:** 根据用户名获取用户所属的资源组列表 + +**参数说明:** + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| userName | String | 是 | 用户名 | + +**返回值:** `List` - 用户资源组列表 + +#### 4. 根据Token查询角色名 +```java +ResultVo> queryRolesByToken() +``` + +**功能描述:** 根据当前用户的Token查询角色名列表 + +**返回值:** `ResultVo>` - 返回角色名列表 + +#### 5. 获取当前用户信息 +```java +ResultVo> getCurrentUserInfo() +``` + +**功能描述:** 获取当前登录用户的详细信息 + +**返回值:** `ResultVo>` - 返回当前用户信息列表 + +--- + +## 数据模型 + +### 通用数据模型 + +#### ResultVo +通用响应包装类,用于统一API响应格式。 + +#### CryptoAlgorithm +加密算法枚举,定义了系统支持的各种加密算法。 + +### 业务数据模型 + +#### CertCollectInfo +证书收集信息,包含证书的详细信息。 + +#### LicenseInfo +许可证信息,包含许可证状态和SBOM信息。 + +#### EventInfo +事件信息,用于告警和监控。 + +#### MenuRegisterInfo +菜单注册信息,用于菜单管理。 + +#### LogAttributeVo +日志属性对象,用于审计日志。 + +#### RoleRegisterVo +角色注册对象,用于角色管理。 + +#### AuthorityInfo +权限信息,用于权限管理。 + +#### ResourceGroup +资源组信息,用于资源管理。 + +#### LoginUserDto +登录用户信息,用于用户认证。 + +--- + +## 使用示例 + +### 1. SDK 初始化方式 + +```java +import com.terrabase.enterprise.api.sdk.TerrabaseSDK; +import com.terrabase.enterprise.api.sdk.TerrabaseSDKConfig; +import com.terrabase.enterprise.api.CryptoAlgorithm; + +public class TerrabaseSDKExample { + + public static void main(String[] args) { + try { + // 方式1:使用默认配置初始化(推荐) + TerrabaseSDK sdk = TerrabaseSDK.init(); + + // 方式2:使用自定义配置初始化 + TerrabaseSDKConfig config = new TerrabaseSDKConfig(); + config.setNacosDiscoveryEnabled(false); // 开源版不启用Nacos + TerrabaseSDK sdk2 = TerrabaseSDK.init(config); + + // 方式3:使用指定证书路径初始化(推荐用于HTTPS环境) + TerrabaseSDK sdkWithCert = TerrabaseSDK.initWithCertPath("/path/to/trust/cert.pem"); + + // 方式4:使用证书路径和Nacos配置初始化(商业版) + TerrabaseSDK commercialSdk = TerrabaseSDK.initWithCertPathAndNacos( + "/path/to/trust/cert.pem", + "https://nacos-server:8848", + "nacos", + "password" + ); + + // 方式5:直接使用静态方法(最简洁) + String encrypted = TerrabaseSDK.cryptoService().encrypt("hello", CryptoAlgorithm.AES, "user1"); + + } catch (Exception e) { + e.printStackTrace(); + } + } +} +``` + +### 2. 证书路径配置说明 + +#### 2.1 为什么需要配置证书路径? + +在HTTPS环境中,SDK需要验证服务器证书的有效性。默认情况下,SDK使用系统默认的证书路径,但在某些企业环境中,可能需要使用特定的证书文件来建立信任关系。 + +#### 2.2 证书路径配置方式 + +**方式1:使用便捷方法(推荐)** +```java +// 仅设置证书路径,使用默认配置 +TerrabaseSDK sdk = TerrabaseSDK.initWithCertPath("/path/to/trust/cert.pem"); + +// 设置证书路径和Nacos配置(商业版) +TerrabaseSDK sdk = TerrabaseSDK.initWithCertPathAndNacos( + "/path/to/trust/cert.pem", + "https://nacos-server:8848", + "nacos", + "password" +); +``` + +**方式2:使用配置对象** +```java +// 创建配置对象 +TerrabaseSDKConfig config = new TerrabaseSDKConfig(); +config.setTrustCertPath("/path/to/trust/cert.pem"); +config.setNacosDiscoveryEnabled(false); // 开源版 + +// 使用配置初始化 +TerrabaseSDK sdk = TerrabaseSDK.init(config); +``` + +**方式3:商业版完整配置** +```java +// 创建商业版配置 +TerrabaseSDKConfig commercialConfig = TerrabaseSDKConfig.createCommercial( + "https://nacos-server:8848", + "nacos", + "password" +); +commercialConfig.setTrustCertPath("/path/to/trust/cert.pem"); + +// 使用配置初始化 +TerrabaseSDK sdk = TerrabaseSDK.init(commercialConfig); +``` + +#### 2.3 证书文件格式要求 + +- **支持格式**:PEM格式(.pem, .crt, .cer) +- **文件内容**:包含完整的证书链 +- **文件权限**:确保应用有读取权限 + +#### 2.4 常见证书路径示例 + +```java +// Linux/Unix 环境 +TerrabaseSDK.initWithCertPath("/etc/ssl/certs/ca-certificates.crt"); +TerrabaseSDK.initWithCertPath("/opt/huawei/fce/runtime/security/server_cert/nacos/nacos.crt"); + +// Windows 环境 +TerrabaseSDK.initWithCertPath("C:\\certs\\trust-cert.pem"); +TerrabaseSDK.initWithCertPath("C:\\Program Files\\certificates\\ca-bundle.crt"); + +// 相对路径 +TerrabaseSDK.initWithCertPath("./certs/trust-cert.pem"); +TerrabaseSDK.initWithCertPath("conf/ssl/trust-store.pem"); +``` + +#### 2.5 证书验证失败处理 + +如果证书路径无效或证书验证失败,SDK会记录错误日志并可能影响HTTPS连接: + +```java +try { + TerrabaseSDK sdk = TerrabaseSDK.initWithCertPath("/invalid/path/cert.pem"); + // 如果证书路径无效,后续HTTPS请求可能会失败 +} catch (Exception e) { + System.err.println("证书配置失败: " + e.getMessage()); +} +``` + +#### 2.6 动态证书更新 + +如果需要动态更新证书路径,可以重新初始化SDK: + +```java +// 清理现有实例 +TerrabaseSDK.getInstance().clearCache(); + +// 重新初始化(注意:SDK是单例,需要重启应用才能生效) +TerrabaseSDK sdk = TerrabaseSDK.initWithCertPath("/new/path/cert.pem"); +``` + +### 3. 加解密服务使用示例 + +```java +// 方式1:使用静态方法(最简洁) +String plaintext = "敏感数据"; +String ciphertext = TerrabaseSDK.cryptoService().encrypt(plaintext, CryptoAlgorithm.AES, "admin"); +System.out.println("加密结果: " + ciphertext); + +String decryptedText = TerrabaseSDK.cryptoService().decrypt(ciphertext, CryptoAlgorithm.AES, "admin"); +System.out.println("解密结果: " + decryptedText); + +// 使用 RSA 算法加密 +String rsaCiphertext = TerrabaseSDK.cryptoService().encrypt(plaintext, CryptoAlgorithm.RSA, "admin"); +System.out.println("RSA 加密结果: " + rsaCiphertext); + +// 方式2:通过实例调用 +TerrabaseSDK sdk = TerrabaseSDK.getInstance(); +String encrypted = sdk.crypto().encrypt(plaintext, CryptoAlgorithm.AES, "admin"); +``` + +### 4. 日志服务使用示例 + +```java +// 使用静态方法调用 +LogService logService = TerrabaseSDK.logService(); + +// 创建日志对象 +LogAttributeVo log = LogAttributeVo.builder() + .sn(System.currentTimeMillis()) + .logType("AUDIT") + .username("admin") + .operation("LOGIN") + .source("WEB") + .terminal("192.168.1.100") + .result("SUCCESS") + .flag("NORMAL") + .paramType("STRING") + .detail("用户登录成功") + .build(); + +// 上报日志 +ResultVo result = logService.registerLogs(Arrays.asList(log)); +if ("200".equals(result.getCode())) { + System.out.println("日志上报成功,处理条数: " + result.getData()); +} else { + System.out.println("日志上报失败: " + result.getMsg()); +} + +// 或者直接使用静态方法 +ResultVo result2 = TerrabaseSDK.logService().registerLogs(Arrays.asList(log)); +``` + +### 5. 菜单服务使用示例 + +```java +// 使用静态方法获取服务 +MenuService menuService = TerrabaseSDK.menuService(); + +// 创建菜单信息 +MenuRegisterInfo menuInfo = MenuRegisterInfo.builder() + .menuId("MENU_001") + .parentMenuId("ROOT") + .menuNameCode("USER_MANAGEMENT") + .url("/user/management") + .iconUrl("/icons/user.png") + .en("User Management") + .zh("用户管理") + .enable(true) + .weight(1) + .build(); + +// 注册菜单 +try { + menuService.registerMenuInfo(menuInfo); + System.out.println("菜单注册成功"); +} catch (Exception e) { + System.out.println("菜单注册失败: " + e.getMessage()); +} + +// 或者直接使用静态方法 +TerrabaseSDK.menuService().registerMenuInfo(menuInfo); +``` + +### 6. 证书服务使用示例 + +```java +// 使用静态方法获取证书服务 +CertificateService certificateService = TerrabaseSDK.certificateService(); + +// 获取证书列表 +ResultVo> certResult = certificateService.listCertificateServiceList(); +if ("200".equals(certResult.getCode())) { + List certs = certResult.getData(); + System.out.println("证书数量: " + certs.size()); + for (CertCollectInfo cert : certs) { + System.out.println("证书名称: " + cert.getCertName() + + ", 状态: " + cert.getStatus() + + ", 过期时间: " + new Date(cert.getExpirationTime())); + } +} else { + System.out.println("获取证书列表失败: " + certResult.getMsg()); +} + +// 获取许可证信息 +ResultVo licenseResult = certificateService.getLicenseInfo(); +if ("200".equals(licenseResult.getCode())) { + LicenseInfo license = licenseResult.getData(); + System.out.println("许可证状态: " + license.getStatus()); +} else { + System.out.println("获取许可证信息失败: " + licenseResult.getMsg()); +} + +// 或者直接使用静态方法 +ResultVo> certs = TerrabaseSDK.certificateService().listCertificateServiceList(); +``` + +### 7. 监控告警服务使用示例 + +```java +// 使用静态方法获取监控服务 +MonitoringService monitoringService = TerrabaseSDK.monitoringService(); + +// 创建告警信息 +EventInfo event = EventInfo.builder() + .eventName("系统异常") + .eventType("alter") + .eventSubject("服务器") + .eventSubjectType("硬件") + .eventDescription("服务器CPU使用率过高") + .severity("major") + .eventCategory("性能") + .status("Uncleared") + .parts("CPU") + .build(); + +// 上报告警 +ResultVo alarmResult = monitoringService.sendEvents(Arrays.asList(event)); +if ("200".equals(alarmResult.getCode()) && alarmResult.getData()) { + System.out.println("告警上报成功"); +} else { + System.out.println("告警上报失败: " + alarmResult.getMsg()); +} + +// 或者直接使用静态方法 +ResultVo result = TerrabaseSDK.monitoringService().sendEvents(Arrays.asList(event)); +``` + +### 8. 用户管理服务使用示例 + +```java +// 使用静态方法获取用户管理服务 +UserManagementService userService = TerrabaseSDK.userManagementService(); + +// 获取用户资源组 +List groups = userService.getUserGroups("admin"); +System.out.println("用户资源组数量: " + groups.size()); +for (ResourceGroup group : groups) { + System.out.println("资源组: " + group.getGroupName()); +} + +// 根据Token查询角色 +ResultVo> rolesResult = userService.queryRolesByToken(); +if ("200".equals(rolesResult.getCode())) { + List roles = rolesResult.getData(); + System.out.println("用户角色: " + String.join(", ", roles)); +} else { + System.out.println("查询角色失败: " + rolesResult.getMsg()); +} + +// 获取当前用户信息 +ResultVo> userResult = userService.getCurrentUserInfo(); +if ("200".equals(userResult.getCode())) { + List users = userResult.getData(); + if (!users.isEmpty()) { + LoginUserDto user = users.get(0); + System.out.println("当前用户: " + user.getUsername()); + } +} else { + System.out.println("获取用户信息失败: " + userResult.getMsg()); +} + +// 或者直接使用静态方法 +ResultVo> users = TerrabaseSDK.userManagementService().getCurrentUserInfo(); +``` + +### 9. 完整使用示例 + +```java +import com.terrabase.enterprise.api.sdk.TerrabaseSDK; +import com.terrabase.enterprise.api.CryptoAlgorithm; +import com.terrabase.enterprise.api.dto.LogAttributeVo; +import com.terrabase.enterprise.api.dto.MenuRegisterInfo; +import com.terrabase.enterprise.api.dto.EventInfo; +import com.terrabase.enterprise.api.response.ResultVo; + +public class CompleteExample { + + public static void main(String[] args) { + try { + // 初始化 SDK(使用证书路径) + TerrabaseSDK.initWithCertPath("/path/to/trust/cert.pem"); + + // 1. 加解密操作 + String encrypted = TerrabaseSDK.cryptoService().encrypt("Hello World", CryptoAlgorithm.AES, "admin"); + String decrypted = TerrabaseSDK.cryptoService().decrypt(encrypted, CryptoAlgorithm.AES, "admin"); + System.out.println("解密结果: " + decrypted); + + // 2. 日志上报 + LogAttributeVo log = LogAttributeVo.builder() + .sn(System.currentTimeMillis()) + .logType("AUDIT") + .username("admin") + .operation("LOGIN") + .result("SUCCESS") + .build(); + ResultVo logResult = TerrabaseSDK.logService().registerLogs(Arrays.asList(log)); + + // 3. 菜单注册 + MenuRegisterInfo menu = MenuRegisterInfo.builder() + .menuId("MENU_001") + .menuNameCode("TEST_MENU") + .zh("测试菜单") + .enable(true) + .build(); + TerrabaseSDK.menuService().registerMenuInfo(menu); + + // 4. 告警上报 + EventInfo event = EventInfo.builder() + .eventName("测试告警") + .eventType("alter") + .eventSubject("系统") + .severity("minor") + .status("Uncleared") + .parts("TEST") + .build(); + ResultVo alarmResult = TerrabaseSDK.monitoringService().sendEvents(Arrays.asList(event)); + + // 5. 用户信息查询 + ResultVo> users = TerrabaseSDK.userManagementService().getCurrentUserInfo(); + + // 6. 证书信息查询 + ResultVo> certs = TerrabaseSDK.certificateService().listCertificateServiceList(); + + System.out.println("所有操作执行完成!"); + + } catch (Exception e) { + System.err.println("操作失败: " + e.getMessage()); + e.printStackTrace(); + } + } +} +``` + +--- + +## 注意事项 + +1. **加密算法选择**:建议根据数据敏感程度选择合适的加密算法,AES适用于一般数据加密,RSA适用于密钥交换。 + +2. **证书路径配置**: + - 确保证书文件存在且可读 + - 使用PEM格式的证书文件 + - 证书路径应使用绝对路径,避免相对路径问题 + - 在HTTPS环境中,证书配置错误可能导致连接失败 + +3. **日志上报**:审计日志应包含完整的操作信息,便于后续审计和追踪。 + +4. **菜单注册**:菜单注册时需确保菜单ID唯一,避免冲突。 + +5. **告警信息**:告警信息应包含完整的上下文信息,便于问题定位和处理。 + +6. **权限管理**:权限注册时应遵循最小权限原则,避免权限过度授予。 + +7. **错误处理**:所有接口调用都应进行适当的错误处理,根据返回的状态码进行相应处理。 + +8. **SDK初始化**:SDK采用单例模式,初始化后无法更改配置,如需更改配置需要重启应用。 + +--- + +## 版本信息 + +- **文档版本**: 1.0.0 +- **API版本**: 1.0.0 +- **最后更新**: 2024年12月 + +--- + +## 联系方式 + +如有问题或建议,请联系 Terrabase 开发团队。 diff --git a/business-app/pom.xml b/business-app/pom.xml index 13bb97e..fbec3de 100644 --- a/business-app/pom.xml +++ b/business-app/pom.xml @@ -30,18 +30,6 @@ spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-data-jpa - - - - - org.springframework.boot - spring-boot-starter-security - - org.springframework.boot @@ -55,11 +43,11 @@ ${project.version} - + - mysql - mysql-connector-java - runtime + com.terrabase + enterprise-impl-open + ${project.version} @@ -70,20 +58,104 @@ - org.springframework.security - spring-security-test - test + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + 2023.0.3.2 + + org.springframework.boot spring-boot-maven-plugin com.terrabase.business.BusinessApplication + + + repackage + + repackage + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.4.1 + + + create-sdk + package + + shade + + + terrabase-sdk-${project.version} + false + + + com.terrabase.sdk.TerrabaseSDK + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + true + sdk + + + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + attach-sources + + jar + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.6.0 + + + attach-javadocs + + jar + + + UTF-8 + UTF-8 + UTF-8 + none + true + + + diff --git a/business-app/src/main/java/com/terrabase/business/BusinessApplication.java b/business-app/src/main/java/com/terrabase/business/BusinessApplication.java new file mode 100644 index 0000000..3aa2e6b --- /dev/null +++ b/business-app/src/main/java/com/terrabase/business/BusinessApplication.java @@ -0,0 +1,78 @@ +package com.terrabase.business; + +import com.alibaba.nacos.common.tls.TlsSystemConfig; +import com.terrabase.business.util.JarLoadUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; + +/** + * Terrabase 业务应用主启动类 + * 基于Spring Boot框架,支持动态加载企业服务实现 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@SpringBootApplication +public class BusinessApplication { +// static { +// // configure nacos SSL +// System.setProperty(TlsSystemConfig.TLS_ENABLE, "true"); +// System.setProperty(TlsSystemConfig.CLIENT_AUTH, "true"); +// System.setProperty(TlsSystemConfig.CLIENT_TRUST_CERT, +// "/opt/huawei/fce/runtime/security/server_cert/nacos/nacos.crt"); +// } + + private static final Logger logger = LoggerFactory.getLogger(BusinessApplication.class); + + @Autowired + private JarLoadUtil jarLoadUtil; + + + public static void main(String[] args) { + logger.info("正在启动 Terrabase 业务应用..."); + SpringApplication.run(BusinessApplication.class, args); + } + + /** + * 配置RestTemplate Bean + */ + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } + + /** + * 应用启动后执行初始化逻辑 + */ + @Bean + public CommandLineRunner init() { + return args -> { + logger.info("=== Terrabase 业务应用初始化开始 ==="); + + try { + // 检测企业模式 + String enterpriseMode = jarLoadUtil.getEnterpriseMode(); + logger.info("企业模式: {}", enterpriseMode); + + if ("commercial".equals(enterpriseMode)) { + logger.info("商业版模式已启用"); + } else { + logger.info("开源版模式已启用"); + } + + } catch (Exception e) { + logger.error("企业模式检测失败", e); + // 不抛出异常,让应用继续启动 + logger.warn("应用将在默认模式下继续运行"); + } + + logger.info("=== Terrabase 业务应用初始化完成 ==="); + }; + } +} diff --git a/business-app/src/main/java/com/terrabase/business/controller/CertificateController.java b/business-app/src/main/java/com/terrabase/business/controller/CertificateController.java new file mode 100644 index 0000000..1ceafbb --- /dev/null +++ b/business-app/src/main/java/com/terrabase/business/controller/CertificateController.java @@ -0,0 +1,98 @@ +package com.terrabase.business.controller; + +import com.terrabase.business.util.JarLoadUtil; +import com.terrabase.enterprise.api.CertificateService; +import com.terrabase.enterprise.api.dto.CertCollectInfo; +import com.terrabase.enterprise.api.dto.LicenseInfo; +import com.terrabase.enterprise.api.response.ResultVo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 证书管理服务控制器 + * 负责提供证书管理相关的REST API接口 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@RestController +@RequestMapping("/api/enterprise/certificate") +@CrossOrigin(origins = "*") +public class CertificateController { + + private static final Logger logger = LoggerFactory.getLogger(CertificateController.class); + + @Autowired + private JarLoadUtil jarLoadUtil; + + /** + * 获取证书列表 + */ + @GetMapping("/list") + public ResponseEntity> getCertificateList() { + try { + CertificateService certService = jarLoadUtil.loadCertificateService(); + ResultVo> result = certService.listCertificateServiceList(); + + Map response = new HashMap<>(); + if ("200".equals(result.getCode())) { + response.put("status", "success"); + response.put("certificates", result.getData()); + response.put("total", result.getData() != null ? result.getData().size() : 0); + response.put("timestamp", System.currentTimeMillis()); + } else { + response.put("status", "failed"); + response.put("error", result.getMsg()); + response.put("code", result.getCode()); + } + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("获取证书列表失败", e); + Map error = new HashMap<>(); + error.put("status", "failed"); + error.put("error", "获取证书列表失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(error); + } + } + + /** + * 获取License信息 + */ + @GetMapping("/license") + public ResponseEntity> getLicenseInfo() { + try { + CertificateService certService = jarLoadUtil.loadCertificateService(); + ResultVo result = certService.getLicenseInfo(); + + Map response = new HashMap<>(); + if ("200".equals(result.getCode())) { + response.put("status", "success"); + response.put("license", result.getData()); + response.put("timestamp", System.currentTimeMillis()); + } else { + response.put("status", "failed"); + response.put("error", result.getMsg()); + response.put("code", result.getCode()); + } + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("获取License信息失败", e); + Map error = new HashMap<>(); + error.put("status", "failed"); + error.put("error", "获取License信息失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(error); + } + } + +} diff --git a/business-app/src/main/java/com/terrabase/business/controller/CryptoController.java b/business-app/src/main/java/com/terrabase/business/controller/CryptoController.java new file mode 100644 index 0000000..e5ea8cf --- /dev/null +++ b/business-app/src/main/java/com/terrabase/business/controller/CryptoController.java @@ -0,0 +1,135 @@ +package com.terrabase.business.controller; + +import com.terrabase.business.util.JarLoadUtil; +import com.terrabase.enterprise.api.CryptoService; +import com.terrabase.enterprise.api.CryptoAlgorithm; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; + +/** + * 加解密服务控制器 + * 负责提供数据加解密相关的REST API接口 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@RestController +@RequestMapping("/api/enterprise/crypto") +@CrossOrigin(origins = "*") +public class CryptoController { + + private static final Logger logger = LoggerFactory.getLogger(CryptoController.class); + + @Autowired + private JarLoadUtil jarLoadUtil; + + /** + * 数据加密接口 + */ + @PostMapping("/encrypt") + public ResponseEntity> encrypt(@RequestBody Map request) { + try { + String plaintext = request.get("plaintext"); + String algorithmName = request.get("algorithm"); + String username = request.get("username"); + + if (plaintext == null || plaintext.trim().isEmpty()) { + Map error = new HashMap<>(); + error.put("error", "明文数据不能为空"); + return ResponseEntity.badRequest().body(error); + } + + CryptoService cryptoService = jarLoadUtil.loadCryptoService(); + + // 解析算法参数,如果未提供则使用默认AES + CryptoAlgorithm algorithm = CryptoAlgorithm.fromString(algorithmName); + String result = cryptoService.encrypt(plaintext, algorithm, username); + + Map response = new HashMap<>(); + response.put("status", "success"); + response.put("ciphertext", result); + // 不再返回服务类型 + response.put("algorithm", algorithm != null ? algorithm.getAlgorithm() : null); + response.put("timestamp", System.currentTimeMillis()); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("数据加密失败", e); + Map error = new HashMap<>(); + error.put("status", "failed"); + error.put("error", "数据加密失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(error); + } + } + + /** + * 数据解密接口 + */ + @PostMapping("/decrypt") + public ResponseEntity> decrypt(@RequestBody Map request) { + try { + String ciphertext = request.get("ciphertext"); + String algorithmName = request.get("algorithm"); + String username = request.get("username"); + + if (ciphertext == null || ciphertext.trim().isEmpty()) { + Map error = new HashMap<>(); + error.put("error", "密文数据不能为空"); + return ResponseEntity.badRequest().body(error); + } + + CryptoService cryptoService = jarLoadUtil.loadCryptoService(); + + // 解析算法参数,如果未提供则使用默认AES + CryptoAlgorithm algorithm = CryptoAlgorithm.fromString(algorithmName); + String result = cryptoService.decrypt(ciphertext, algorithm, username); + + Map response = new HashMap<>(); + response.put("status", "success"); + response.put("result", result); + // 不再返回服务类型 + response.put("algorithm", algorithm != null ? algorithm.getAlgorithm() : null); + response.put("timestamp", System.currentTimeMillis()); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("数据解密失败", e); + Map error = new HashMap<>(); + error.put("status", "failed"); + error.put("error", "数据解密失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(error); + } + } + + /** + * 获取支持的加密算法列表 + */ + @GetMapping("/algorithms") + public ResponseEntity> getSupportedAlgorithms() { + try { + Map response = new HashMap<>(); + response.put("success", true); + response.put("algorithms", CryptoAlgorithm.values()); + response.put("timestamp", System.currentTimeMillis()); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("获取支持的加密算法列表失败", e); + Map error = new HashMap<>(); + error.put("success", false); + error.put("error", "获取支持的加密算法列表失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(error); + } + } + + +} diff --git a/business-app/src/main/java/com/terrabase/business/controller/EnterpriseController.java b/business-app/src/main/java/com/terrabase/business/controller/EnterpriseController.java new file mode 100644 index 0000000..e57c40e --- /dev/null +++ b/business-app/src/main/java/com/terrabase/business/controller/EnterpriseController.java @@ -0,0 +1,146 @@ +package com.terrabase.business.controller; + +import com.terrabase.business.util.JarLoadUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; + +/** + * 企业服务主控制器 + * 负责企业服务的基础功能:服务信息、健康状态、配置管理、服务重载等 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@RestController +@RequestMapping("/api/enterprise") +@CrossOrigin(origins = "*") +public class EnterpriseController { + + private static final Logger logger = LoggerFactory.getLogger(EnterpriseController.class); + + @Autowired + private JarLoadUtil jarLoadUtil; + + /** + * 获取企业服务信息 + */ + @GetMapping("/info") + public ResponseEntity> getServiceInfo() { + try { + Map info = new HashMap<>(); + info.put("enterpriseMode", jarLoadUtil.getEnterpriseMode()); + info.put("status", "running"); + info.put("message", "企业服务运行正常"); + + return ResponseEntity.ok(info); + + } catch (Exception e) { + logger.error("获取企业服务信息失败", e); + Map error = new HashMap<>(); + error.put("error", "获取企业服务信息失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(error); + } + } + + /** + * 获取健康状态 + */ + @GetMapping("/health") + public ResponseEntity> getHealthStatus() { + try { + Map health = new HashMap<>(); + health.put("status", "UP"); + health.put("enterpriseMode", jarLoadUtil.getEnterpriseMode()); + health.put("details", "企业服务运行正常"); + health.put("timestamp", System.currentTimeMillis()); + + return ResponseEntity.ok(health); + + } catch (Exception e) { + logger.error("获取健康状态失败", e); + Map error = new HashMap<>(); + error.put("status", "DOWN"); + error.put("error", "获取健康状态失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(error); + } + } + + /** + * 重新加载企业服务 + */ + @PostMapping("/reload") + public ResponseEntity> reloadService() { + try { + logger.info("开始重新加载企业服务..."); + + // 清理缓存 + jarLoadUtil.clearCache(); + + // 重新检测企业模式 + String enterpriseMode = jarLoadUtil.getEnterpriseMode(); + + Map response = new HashMap<>(); + response.put("message", "企业服务重新加载成功"); + response.put("enterpriseMode", enterpriseMode); + response.put("timestamp", System.currentTimeMillis()); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("重新加载企业服务失败", e); + Map error = new HashMap<>(); + error.put("error", "重新加载企业服务失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(error); + } + } + + /** + * 获取当前配置 + */ + @GetMapping("/config") + public ResponseEntity> getConfig() { + try { + Map config = new HashMap<>(); + config.put("enterpriseMode", jarLoadUtil.getEnterpriseMode()); + config.put("timestamp", System.currentTimeMillis()); + + return ResponseEntity.ok(config); + + } catch (Exception e) { + logger.error("获取配置信息失败", e); + Map error = new HashMap<>(); + error.put("error", "获取配置信息失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(error); + } + } + + /** + * 获取所有子服务信息概览 + */ + @GetMapping("/services/overview") + public ResponseEntity> getServicesOverview() { + try { + Map overview = new HashMap<>(); + overview.put("enterpriseMode", jarLoadUtil.getEnterpriseMode()); + overview.put("availableServices", new String[]{ + "crypto", "userManagement", "logManagement", + "certificate", "monitoring" + }); + overview.put("timestamp", System.currentTimeMillis()); + + return ResponseEntity.ok(overview); + + } catch (Exception e) { + logger.error("获取服务概览失败", e); + Map error = new HashMap<>(); + error.put("error", "获取服务概览失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(error); + } + } +} \ No newline at end of file diff --git a/business-app/src/main/java/com/terrabase/business/controller/LogManagementController.java b/business-app/src/main/java/com/terrabase/business/controller/LogManagementController.java new file mode 100644 index 0000000..82d0bd5 --- /dev/null +++ b/business-app/src/main/java/com/terrabase/business/controller/LogManagementController.java @@ -0,0 +1,111 @@ +package com.terrabase.business.controller; + +import com.terrabase.business.util.JarLoadUtil; +import com.terrabase.enterprise.api.LogService; +import com.terrabase.enterprise.api.dto.LogI18n; +import com.terrabase.enterprise.api.request.LogAttributeVo; +import com.terrabase.enterprise.api.response.ResultVo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 日志管理服务控制器 + * 负责提供日志管理相关的REST API接口 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@RestController +@RequestMapping("/api/enterprise/log") +@CrossOrigin(origins = "*") +public class LogManagementController { + + private static final Logger logger = LoggerFactory.getLogger(LogManagementController.class); + + @Autowired + private JarLoadUtil jarLoadUtil; + + /** + * 上报审计日志 + */ + @PostMapping("/register") + public ResponseEntity> registerLogs(@RequestBody List logs) { + try { + if (logs == null || logs.isEmpty()) { + Map error = new HashMap<>(); + error.put("error", "日志列表不能为空"); + return ResponseEntity.badRequest().body(error); + } + + LogService logService = jarLoadUtil.loadLogService(); + ResultVo result = logService.registerLogs(logs); + + Map response = new HashMap<>(); + if ("200".equals(result.getCode())) { + response.put("status", "success"); + response.put("message", "日志上报成功"); + response.put("logCount", result.getData()); + response.put("timestamp", System.currentTimeMillis()); + } else { + response.put("status", "failed"); + response.put("error", result.getMsg()); + response.put("code", result.getCode()); + } + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("上报审计日志失败", e); + Map error = new HashMap<>(); + error.put("status", "failed"); + error.put("error", "上报审计日志失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(error); + } + } + + /** + * 注册审计日志国际化 + */ + @PostMapping("/i18n") + public ResponseEntity> registryInternational(@RequestBody List logI18ns) { + try { + if (logI18ns == null || logI18ns.isEmpty()) { + Map error = new HashMap<>(); + error.put("error", "国际化信息列表不能为空"); + return ResponseEntity.badRequest().body(error); + } + + LogService logService = jarLoadUtil.loadLogService(); + ResultVo result = logService.registryInternational(logI18ns); + + Map response = new HashMap<>(); + if ("200".equals(result.getCode())) { + response.put("status", "success"); + response.put("message", "审计日志国际化注册成功"); + response.put("registered", result.getData()); + response.put("timestamp", System.currentTimeMillis()); + } else { + response.put("status", "failed"); + response.put("error", result.getMsg()); + response.put("code", result.getCode()); + } + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("注册审计日志国际化失败", e); + Map error = new HashMap<>(); + error.put("status", "failed"); + error.put("error", "注册审计日志国际化失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(error); + } + } + +} diff --git a/business-app/src/main/java/com/terrabase/business/controller/MonitoringController.java b/business-app/src/main/java/com/terrabase/business/controller/MonitoringController.java new file mode 100644 index 0000000..7bdba71 --- /dev/null +++ b/business-app/src/main/java/com/terrabase/business/controller/MonitoringController.java @@ -0,0 +1,149 @@ +package com.terrabase.business.controller; + +import com.terrabase.business.util.JarLoadUtil; +import com.terrabase.enterprise.api.MonitoringService; +import com.terrabase.enterprise.api.dto.EventInfo; +import com.terrabase.enterprise.api.dto.EventsCollection; +import com.terrabase.enterprise.api.request.GetEventsParams; +import com.terrabase.enterprise.api.request.RegisterEventDefineReq; +import com.terrabase.enterprise.api.response.ResultVo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 监控告警服务控制器 + * 负责提供监控告警相关的REST API接口 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@RestController +@RequestMapping("/api/enterprise/monitoring") +@CrossOrigin(origins = "*") +public class MonitoringController { + + private static final Logger logger = LoggerFactory.getLogger(MonitoringController.class); + + @Autowired + private JarLoadUtil jarLoadUtil; + + /** + * 注册事件定义 + */ + @PostMapping("/event-define") + public ResponseEntity> registerEventDefine(@RequestBody RegisterEventDefineReq request) { + try { + if (request == null) { + Map error = new HashMap<>(); + error.put("error", "事件定义请求不能为空"); + return ResponseEntity.badRequest().body(error); + } + + MonitoringService monitoringService = jarLoadUtil.loadMonitoringService(); + ResultVo result = monitoringService.registerEventDefine(request); + + Map response = new HashMap<>(); + if ("200".equals(result.getCode())) { + response.put("status", "success"); + response.put("message", "事件定义注册成功"); + response.put("timestamp", System.currentTimeMillis()); + } else { + response.put("status", "failed"); + response.put("error", result.getMsg()); + response.put("code", result.getCode()); + } + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("注册事件定义失败", e); + Map error = new HashMap<>(); + error.put("status", "failed"); + error.put("error", "注册事件定义失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(error); + } + } + + /** + * 上报事件 + */ + @PostMapping("/events") + public ResponseEntity> sendEvents(@RequestBody List eventInfos) { + try { + if (eventInfos == null || eventInfos.isEmpty()) { + Map error = new HashMap<>(); + error.put("error", "事件信息列表不能为空"); + return ResponseEntity.badRequest().body(error); + } + + MonitoringService monitoringService = jarLoadUtil.loadMonitoringService(); + ResultVo result = monitoringService.sendEvents(eventInfos); + + Map response = new HashMap<>(); + if ("200".equals(result.getCode())) { + response.put("status", "success"); + response.put("message", "事件上报成功"); + response.put("success", result.getData()); + response.put("timestamp", System.currentTimeMillis()); + } else { + response.put("status", "failed"); + response.put("error", result.getMsg()); + response.put("code", result.getCode()); + } + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("上报事件失败", e); + Map error = new HashMap<>(); + error.put("status", "failed"); + error.put("error", "上报事件失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(error); + } + } + + /** + * 分页查询事件 + */ + @PostMapping("/events/query") + public ResponseEntity> getEventsByPage(@RequestBody GetEventsParams getEventsParams) { + try { + if (getEventsParams == null) { + Map error = new HashMap<>(); + error.put("error", "查询参数不能为空"); + return ResponseEntity.badRequest().body(error); + } + + MonitoringService monitoringService = jarLoadUtil.loadMonitoringService(); + ResultVo result = monitoringService.getEventsByPage(getEventsParams); + + Map response = new HashMap<>(); + if ("200".equals(result.getCode())) { + response.put("status", "success"); + response.put("events", result.getData()); + response.put("timestamp", System.currentTimeMillis()); + } else { + response.put("status", "failed"); + response.put("error", result.getMsg()); + response.put("code", result.getCode()); + } + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("查询事件失败", e); + Map error = new HashMap<>(); + error.put("status", "failed"); + error.put("error", "查询事件失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(error); + } + } + +} diff --git a/business-app/src/main/java/com/terrabase/business/controller/UserManagementController.java b/business-app/src/main/java/com/terrabase/business/controller/UserManagementController.java new file mode 100644 index 0000000..1e03aa6 --- /dev/null +++ b/business-app/src/main/java/com/terrabase/business/controller/UserManagementController.java @@ -0,0 +1,197 @@ +package com.terrabase.business.controller; + +import com.terrabase.business.util.JarLoadUtil; +import com.terrabase.enterprise.api.UserManagementService; +import com.terrabase.enterprise.api.dto.AuthorityInfo; +import com.terrabase.enterprise.api.dto.LoginUserDto; +import com.terrabase.enterprise.api.dto.ResourceGroup; +import com.terrabase.enterprise.api.request.RoleRegisterVo; +import com.terrabase.enterprise.api.response.ResultVo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 用户管理服务控制器 + * 负责提供用户管理相关的REST API接口 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@RestController +@RequestMapping("/api/enterprise/user") +@CrossOrigin(origins = "*") +public class UserManagementController { + + private static final Logger logger = LoggerFactory.getLogger(UserManagementController.class); + + @Autowired + private JarLoadUtil jarLoadUtil; + + /** + * 批量角色注册 + */ + @PostMapping("/roles/register") + public ResponseEntity> batchRegisterRole(@RequestBody RoleRegisterVo roleRegister) { + try { + if (roleRegister == null) { + Map error = new HashMap<>(); + error.put("error", "角色注册信息不能为空"); + return ResponseEntity.badRequest().body(error); + } + + UserManagementService userService = jarLoadUtil.loadUserManagementService(); + userService.batchRegisterRole(roleRegister); + + Map response = new HashMap<>(); + response.put("status", "success"); + response.put("message", "角色批量注册成功"); + response.put("timestamp", System.currentTimeMillis()); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("批量角色注册失败", e); + Map error = new HashMap<>(); + error.put("status", "failed"); + error.put("error", "批量角色注册失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(error); + } + } + + /** + * 批量权限注册 + */ + @PostMapping("/permissions/register") + public ResponseEntity> registerPermission(@RequestBody List authorityInfos) { + try { + if (authorityInfos == null || authorityInfos.isEmpty()) { + Map error = new HashMap<>(); + error.put("error", "权限信息列表不能为空"); + return ResponseEntity.badRequest().body(error); + } + + UserManagementService userService = jarLoadUtil.loadUserManagementService(); + userService.registerPermission(authorityInfos); + + Map response = new HashMap<>(); + response.put("status", "success"); + response.put("message", "权限批量注册成功"); + response.put("permissionCount", authorityInfos.size()); + response.put("timestamp", System.currentTimeMillis()); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("批量权限注册失败", e); + Map error = new HashMap<>(); + error.put("status", "failed"); + error.put("error", "批量权限注册失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(error); + } + } + + /** + * 获取用户资源组列表 + */ + @GetMapping("/{username}/groups") + public ResponseEntity> getUserGroups(@PathVariable String username) { + try { + if (username == null || username.trim().isEmpty()) { + Map error = new HashMap<>(); + error.put("error", "用户名不能为空"); + return ResponseEntity.badRequest().body(error); + } + + UserManagementService userService = jarLoadUtil.loadUserManagementService(); + List resourceGroups = userService.getUserGroups(username); + + Map response = new HashMap<>(); + response.put("status", "success"); + response.put("username", username); + response.put("resourceGroups", resourceGroups); + response.put("groupCount", resourceGroups != null ? resourceGroups.size() : 0); + response.put("timestamp", System.currentTimeMillis()); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("获取用户资源组列表失败", e); + Map error = new HashMap<>(); + error.put("status", "failed"); + error.put("error", "获取用户资源组列表失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(error); + } + } + + /** + * 根据token查询角色名 + */ + @GetMapping("/roles/by-token") + public ResponseEntity> queryRolesByToken() { + try { + UserManagementService userService = jarLoadUtil.loadUserManagementService(); + ResultVo> result = userService.queryRolesByToken(); + + Map response = new HashMap<>(); + if ("200".equals(result.getCode())) { + response.put("status", "success"); + response.put("roles", result.getData()); + response.put("roleCount", result.getData() != null ? result.getData().size() : 0); + response.put("timestamp", System.currentTimeMillis()); + } else { + response.put("status", "failed"); + response.put("error", result.getMsg()); + response.put("code", result.getCode()); + } + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("根据token查询角色名失败", e); + Map error = new HashMap<>(); + error.put("status", "failed"); + error.put("error", "根据token查询角色名失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(error); + } + } + + /** + * 获取当前用户信息 + */ + @GetMapping("/current-user") + public ResponseEntity> getCurrentUserInfo() { + try { + UserManagementService userService = jarLoadUtil.loadUserManagementService(); + ResultVo> result = userService.getCurrentUserInfo(); + + Map response = new HashMap<>(); + if ("200".equals(result.getCode())) { + response.put("status", "success"); + response.put("users", result.getData()); + response.put("userCount", result.getData() != null ? result.getData().size() : 0); + response.put("timestamp", System.currentTimeMillis()); + } else { + response.put("status", "failed"); + response.put("error", result.getMsg()); + response.put("code", result.getCode()); + } + + return ResponseEntity.ok(response); + + } catch (Exception e) { + logger.error("获取当前用户信息失败", e); + Map error = new HashMap<>(); + error.put("status", "failed"); + error.put("error", "获取当前用户信息失败: " + e.getMessage()); + return ResponseEntity.internalServerError().body(error); + } + } + +} diff --git a/business-app/src/main/java/com/terrabase/business/util/JarLoadUtil.java b/business-app/src/main/java/com/terrabase/business/util/JarLoadUtil.java new file mode 100644 index 0000000..f116ee5 --- /dev/null +++ b/business-app/src/main/java/com/terrabase/business/util/JarLoadUtil.java @@ -0,0 +1,285 @@ +package com.terrabase.business.util; + +import com.terrabase.enterprise.api.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 企业服务加载工具类 + * 用于根据配置加载商业版(JAR包)或开源版(直接依赖)的企业服务实现 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@Component +public class JarLoadUtil { + + private static final Logger logger = LoggerFactory.getLogger(JarLoadUtil.class); + + @Value("${enterprise.jar.path:./lib}") + private String jarPath; + + @Autowired + private ApplicationContext applicationContext; + + // 缓存已加载的实例 + private final ConcurrentHashMap serviceInstances = new ConcurrentHashMap<>(); + + /** + * 加载加解密服务 + * @return 加解密服务实例 + */ + public CryptoService loadCryptoService() { + return (CryptoService) loadService("crypto_service", + "com.terrabase.enterprise.impl.commercial.CommercialCryptoServiceImpl", + "com.terrabase.enterprise.impl.open.OpenCryptoServiceImpl"); + } + + /** + * 加载用户管理服务 + * @return 用户管理服务实例 + */ + public UserManagementService loadUserManagementService() { + return (UserManagementService) loadService("user_management_service", + "com.terrabase.enterprise.impl.commercial.CommercialUserManagementServiceImpl", + "com.terrabase.enterprise.impl.open.OpenUserManagementServiceImpl"); + } + + + /** + * 加载证书管理服务 + * @return 证书管理服务实例 + */ + public CertificateService loadCertificateService() { + return (CertificateService) loadService("certificate_service", + "com.terrabase.enterprise.impl.commercial.CommercialCertificateServiceImpl", + "com.terrabase.enterprise.impl.open.OpenCertificateServiceImpl"); + } + + /** + * 加载日志服务 + * @return 日志服务实例 + */ + public LogService loadLogService() { + return (LogService) loadService("log_service", + "com.terrabase.enterprise.impl.commercial.CommercialLogServiceImpl", + "com.terrabase.enterprise.impl.open.OpenLogServiceImpl"); + } + + /** + * 加载监控告警服务 + * @return 监控告警服务实例 + */ + public MonitoringService loadMonitoringService() { + return (MonitoringService) loadService("monitoring_service", + "com.terrabase.enterprise.impl.commercial.CommercialMonitoringServiceImpl", + "com.terrabase.enterprise.impl.open.OpenMonitoringServiceImpl"); + } + + /** + * 通用服务加载方法 + * @param cacheKey 缓存键 + * @param commercialClassName 商业版类名 + * @param openClassName 开源版类名 + * @return 服务实例 + */ + private Object loadService(String cacheKey, String commercialClassName, String openClassName) { + try { + // 先检查缓存 + Object cachedService = serviceInstances.get(cacheKey); + if (cachedService != null) { + logger.info("从缓存中获取服务实例: {}", cacheKey); + return cachedService; + } + + logger.info("开始检测并加载服务: {}", cacheKey); + + Object service; + if (isCommercialJarAvailable()) { + logger.info("检测到商业版JAR包,加载商业版服务: {}", commercialClassName); + service = loadServiceFromJar(commercialClassName); + } else { + logger.info("未检测到商业版JAR包,使用开源版服务: {}", openClassName); + service = loadServiceFromClasspath(openClassName); + } + + // 如果服务加载失败,使用开源版作为降级方案 + if (service == null) { + logger.warn("服务加载失败,使用开源版作为降级方案: {}", openClassName); + service = loadServiceFromClasspath(openClassName); + } + + // 如果开源版也加载失败,抛出异常 + if (service == null) { + throw new RuntimeException("无法加载任何服务实现: " + cacheKey); + } + + // 缓存服务实例 + serviceInstances.put(cacheKey, service); + + // 服务已加载完成,无需手动启动 + + logger.info("服务加载成功: {}", cacheKey); + + return service; + + } catch (Exception e) { + logger.error("加载服务失败: {}, 尝试使用开源版作为降级方案", cacheKey, e); + try { + // 最后的降级方案:直接实例化开源版服务 + Object fallbackService = loadServiceFromClasspath(openClassName); + if (fallbackService != null) { + serviceInstances.put(cacheKey, fallbackService); + + // 降级服务已加载完成,无需手动启动 + + logger.warn("使用开源版服务作为降级方案: {}", cacheKey); + return fallbackService; + } + } catch (Exception fallbackException) { + logger.error("降级方案也失败了: {}", cacheKey, fallbackException); + } + throw new RuntimeException("无法加载任何服务实现: " + cacheKey, e); + } + } + + + /** + * 检测商业版JAR包是否可用 + * @return true如果JAR包存在且可加载 + */ + private boolean isCommercialJarAvailable() { + try { + String jarFileName = "enterprise-impl-commercial-1.0.0.jar"; + File jarFile = new File(jarPath, jarFileName); + + if (!jarFile.exists()) { + logger.debug("商业版JAR文件不存在: {}", jarFile.getAbsolutePath()); + return false; + } + + // 尝试加载JAR包中的类来验证JAR包是否有效 + URL jarUrl = jarFile.toURI().toURL(); + URLClassLoader classLoader = new URLClassLoader(new URL[]{jarUrl}, this.getClass().getClassLoader()); + + // 尝试加载商业版实现类 + Class clazz = classLoader.loadClass("com.terrabase.enterprise.impl.commercial.CommercialCryptoServiceImpl"); + if (clazz != null) { + logger.debug("商业版JAR包验证成功: {}", jarFile.getAbsolutePath()); + return true; + } + + } catch (Exception e) { + logger.debug("商业版JAR包检测失败: {}", e.getMessage()); + } + + return false; + } + + + + /** + * 从JAR包加载服务(通用方法) + * @param className 类名 + * @return 服务实例 + */ + private Object loadServiceFromJar(String className) { + try { + String jarFileName = getJarFileName(className); + File jarFile = new File(jarPath, jarFileName); + + if (!jarFile.exists()) { + logger.warn("JAR文件不存在: {}", jarFile.getAbsolutePath()); + return null; + } + + logger.info("从JAR文件加载: {}", jarFile.getAbsolutePath()); + + URL jarUrl = jarFile.toURI().toURL(); + URLClassLoader classLoader = new URLClassLoader(new URL[]{jarUrl}, this.getClass().getClassLoader()); + + Class clazz = classLoader.loadClass(className); + + // 仅使用默认构造函数创建实例 + Constructor defaultConstructor = clazz.getDeclaredConstructor(); + defaultConstructor.setAccessible(true); + Object instance = defaultConstructor.newInstance(); + logger.info("使用默认构造函数创建商业版服务实例"); + + return instance; + + } catch (Exception e) { + logger.warn("从JAR包加载失败: {}", e.getMessage()); + return null; + } + } + + /** + * 从类路径加载服务(通用方法) + * @param className 类名 + * @return 服务实例 + */ + private Object loadServiceFromClasspath(String className) { + try { + logger.info("从类路径加载: {}", className); + + Class clazz = Class.forName(className); + + // 仅使用默认构造函数创建实例 + Constructor defaultConstructor = clazz.getDeclaredConstructor(); + defaultConstructor.setAccessible(true); + Object instance = defaultConstructor.newInstance(); + logger.info("使用默认构造函数创建服务实例"); + + + return instance; + + } catch (Exception e) { + logger.warn("从类路径加载失败: {}", e.getMessage()); + return null; + } + } + + + + /** + * 根据类名获取JAR文件名 + * @param className 类名 + * @return JAR文件名 + */ + private String getJarFileName(String className) { + if (className.contains("commercial")) { + return "enterprise-impl-commercial-1.0.0.jar"; + } else if (className.contains("open")) { + return "enterprise-impl-open-1.0.0.jar"; + } + return "unknown.jar"; + } + + + /** + * 清理缓存 + */ + public void clearCache() { + logger.info("清理服务缓存"); + serviceInstances.clear(); + } + + /** + * 获取当前企业模式 + * @return 企业模式 + */ + public String getEnterpriseMode() { + return isCommercialJarAvailable() ? "commercial" : "open"; + } +} diff --git a/business-app/src/main/java/com/terrabase/sdk/example/SDKUsageExample.java b/business-app/src/main/java/com/terrabase/sdk/example/SDKUsageExample.java new file mode 100644 index 0000000..6e2230d --- /dev/null +++ b/business-app/src/main/java/com/terrabase/sdk/example/SDKUsageExample.java @@ -0,0 +1,337 @@ +package com.terrabase.sdk.example; + +import com.terrabase.enterprise.api.CryptoAlgorithm; +import com.terrabase.enterprise.api.dto.*; +import com.terrabase.enterprise.api.request.LogAttributeVo; +import com.terrabase.enterprise.api.request.RegisterEventDefineReq; +import com.terrabase.enterprise.api.request.GetEventsParams; +import com.terrabase.enterprise.api.dto.EventDefine; +import com.terrabase.enterprise.api.response.ResultVo; +import com.terrabase.enterprise.api.sdk.TerrabaseSDK; + +import java.util.List; + +/** + * Terrabase SDK 使用示例 + * 展示如何在不同场景下使用SDK + * + * @author Yehong Pan + * @version 1.0.0 + */ +public class SDKUsageExample { + + public static void main(String[] args) { + // 示例1:基本使用 + basicUsageExample(); + + // 示例2:加解密操作 + cryptoExample(); + + // 示例3:用户管理 + userManagementExample(); + + // 示例4:企业服务信息 + enterpriseServiceExample(); + + // 示例5:日志服务 + logServiceExample(); + + // 示例6:证书管理 + certificateServiceExample(); + + // 示例7:监控告警 + monitoringServiceExample(); + } + + /** + * 基本使用示例 + */ + public static void basicUsageExample() { + System.out.println("=== 基本使用示例 ==="); + + // 方式1:通过getInstance()调用(推荐) + String mode = TerrabaseSDK.getInstance().getEnterpriseMode(); + String serviceInfo = TerrabaseSDK.getServiceInfo(); + System.out.println("企业模式: " + mode); + System.out.println("服务信息: " + serviceInfo); + + // 方式2:传统实例化方式 + TerrabaseSDK sdk = TerrabaseSDK.init(); + String sdkMode = sdk.getEnterpriseMode(); + System.out.println("当前企业模式: " + sdkMode); + + // 方式3:获取详细信息 + System.out.println("企业服务详细信息: " + serviceInfo); + } + + /** + * 加解密操作示例 + */ + public static void cryptoExample() { + System.out.println("\n=== 加解密操作示例 ==="); + + try { + String plaintext = "Hello, Terrabase!"; + String username = "user1"; + + // 方式1:直接使用静态方法(最简洁) + String encrypted = TerrabaseSDK.cryptoService().encrypt(plaintext, CryptoAlgorithm.AES, username); + System.out.println("AES加密结果: " + encrypted); + + String decrypted = TerrabaseSDK.cryptoService().decrypt(encrypted, CryptoAlgorithm.AES, username); + System.out.println("AES解密结果: " + decrypted); + + // 方式2:RSA加密 + String rsaEncrypted = TerrabaseSDK.cryptoService().encrypt("Another message", CryptoAlgorithm.RSA, username); + System.out.println("RSA加密结果: " + rsaEncrypted); + + // 方式3:传统实例化方式 + TerrabaseSDK sdk = TerrabaseSDK.init(); + String encrypted3 = sdk.crypto().encrypt("Instance method", CryptoAlgorithm.AES, username); + System.out.println("实例方法加密结果: " + encrypted3); + + } catch (Exception e) { + System.err.println("加解密操作失败: " + e.getMessage()); + } + } + + /** + * 用户管理示例 + */ + public static void userManagementExample() { + System.out.println("\n=== 用户管理示例 ==="); + + try { + // 方式1:直接使用静态方法(最简洁) + ResultVo> result = TerrabaseSDK.userManagementService().getCurrentUserInfo(); + + if ("200".equals(result.getCode())) { + System.out.println("用户信息获取成功:"); + List users = result.getData(); + if (users != null) { + for (LoginUserDto user : users) { + System.out.println(" 用户: " + user.getUserName() + " (ID: " + user.getUserId() + ")"); + } + } + } else { + System.out.println("用户信息获取失败: " + result.getMsg()); + } + + // 方式2:传统实例化方式 + TerrabaseSDK sdk = TerrabaseSDK.init(); + ResultVo> result2 = sdk.userManagement().getCurrentUserInfo(); + System.out.println("实例方式获取用户信息: " + (result2.getData() != null ? result2.getData().size() + "个用户" : "无数据")); + + } catch (Exception e) { + System.err.println("用户管理操作失败: " + e.getMessage()); + } + } + + /** + * 企业服务信息示例 + */ + public static void enterpriseServiceExample() { + System.out.println("\n=== 企业服务信息示例 ==="); + + try { + // 方式1:直接使用静态方法(最简洁) + System.out.println("企业服务信息:"); + System.out.println(TerrabaseSDK.getServiceInfo()); + + // 方式2:传统实例化方式 + TerrabaseSDK sdk = TerrabaseSDK.init(); + String mode = sdk.getEnterpriseMode(); + System.out.println("实例方式获取企业模式: " + mode); + + // 方式3:展示所有服务的调用 + System.out.println("\n所有服务调用演示:"); + System.out.println(" 加解密服务: " + TerrabaseSDK.cryptoService().getClass().getSimpleName()); + System.out.println(" 用户管理服务: " + TerrabaseSDK.userManagementService().getClass().getSimpleName()); + System.out.println(" 日志服务: " + TerrabaseSDK.logService().getClass().getSimpleName()); + System.out.println(" 证书服务: " + TerrabaseSDK.certificateService().getClass().getSimpleName()); + System.out.println(" 监控服务: " + TerrabaseSDK.monitoringService().getClass().getSimpleName()); + + } catch (Exception e) { + System.err.println("企业服务操作失败: " + e.getMessage()); + } + } + + /** + * 日志服务示例 + */ + public static void logServiceExample() { + System.out.println("\n=== 日志服务示例 ==="); + + try { + // 创建日志属性对象 + LogAttributeVo log1 = new LogAttributeVo(); + log1.setSn(1L); + log1.setLogType("INFO"); + log1.setUsername("user1"); + log1.setOperation("登录"); + log1.setSource("web"); + log1.setTerminal("browser"); + log1.setResult("成功"); + log1.setFlag("normal"); + log1.setParamType("string"); + log1.setDetail("用户登录成功"); + + LogAttributeVo log2 = new LogAttributeVo(); + log2.setSn(2L); + log2.setLogType("ERROR"); + log2.setUsername("user1"); + log2.setOperation("数据库连接"); + log2.setSource("system"); + log2.setTerminal("server"); + log2.setResult("失败"); + log2.setFlag("error"); + log2.setParamType("string"); + log2.setDetail("数据库连接失败"); + + // 批量上报日志 + List logs = List.of(log1, log2); + ResultVo result = TerrabaseSDK.logService().registerLogs(logs); + if ("200".equals(result.getCode())) { + System.out.println("日志上报成功,上报了 " + result.getData() + " 条日志"); + } + + // 注册日志国际化信息 + LogI18n logI18n = new LogI18n(); + logI18n.setCode("user.login.success"); + logI18n.setLanguage("zh-CN"); + logI18n.setContent("用户登录成功"); + + ResultVo i18nResult = TerrabaseSDK.logService().registryInternational(List.of(logI18n)); + if ("200".equals(i18nResult.getCode())) { + System.out.println("日志国际化注册成功: " + i18nResult.getData()); + } + + } catch (Exception e) { + System.err.println("日志服务操作失败: " + e.getMessage()); + } + } + + /** + * 证书管理服务示例 + */ + public static void certificateServiceExample() { + System.out.println("\n=== 证书管理服务示例 ==="); + + try { + // 获取证书服务列表 + ResultVo> certs = TerrabaseSDK.certificateService().listCertificateServiceList(); + if ("200".equals(certs.getCode()) && certs.getData() != null) { + System.out.println("获取到 " + certs.getData().size() + " 个证书服务"); + for (CertCollectInfo cert : certs.getData()) { + System.out.println(" 证书服务: " + cert.getProductName() + " (状态: " + cert.getStatus() + ")"); + } + } + + // 获取License信息 + ResultVo license = TerrabaseSDK.certificateService().getLicenseInfo(); + if ("200".equals(license.getCode()) && license.getData() != null) { + LicenseInfo licenseInfo = license.getData(); + System.out.println("License信息:"); + System.out.println(" 状态: " + licenseInfo.getStatus()); + System.out.println(" SBOM数量: " + (licenseInfo.getSboms() != null ? licenseInfo.getSboms().size() : 0)); + } + + } catch (Exception e) { + System.err.println("证书管理操作失败: " + e.getMessage()); + } + } + + /** + * 监控告警服务示例 + */ + public static void monitoringServiceExample() { + System.out.println("\n=== 监控告警服务示例 ==="); + + try { + // 注册告警定义 + RegisterEventDefineReq eventDefine = new RegisterEventDefineReq(); + eventDefine.setServiceName("system-service"); + eventDefine.setServiceEn("System Service"); + eventDefine.setServiceZh("系统服务"); + eventDefine.setDeleteAll(false); + + // 创建事件定义列表 + EventDefine eventDef = new EventDefine(); + eventDef.setEventId("system-error-001"); + eventDef.setName("系统异常"); + eventDef.setType("event"); + eventDef.setCategory("system"); + eventDef.setDescription("系统发生异常"); + eventDef.setSeverity("major"); + eventDef.setLanguage("zh-cn"); + + eventDefine.setEventDefines(List.of(eventDef)); + + ResultVo registerResult = TerrabaseSDK.monitoringService().registerEventDefine(eventDefine); + if ("200".equals(registerResult.getCode())) { + System.out.println("告警定义注册成功"); + } + + // 上报告警事件 + EventInfo eventInfo = new EventInfo(); + eventInfo.setEventName("系统异常"); + eventInfo.setEventType("event"); + eventInfo.setEventSubject("数据库连接超时"); + eventInfo.setEventSubjectType("system"); + eventInfo.setSeverity("major"); + eventInfo.setEventCategory("system"); + eventInfo.setStatus("Uncleared"); + eventInfo.setFirstOccurTime("2024-01-01T00:00:00Z"); + eventInfo.setParts("system"); + eventInfo.setLanguage("zh-cn"); + + ResultVo sendResult = TerrabaseSDK.monitoringService().sendEvents(List.of(eventInfo)); + if ("200".equals(sendResult.getCode())) { + System.out.println("告警事件上报成功: " + sendResult.getData()); + } + + // 查询告警事件 + GetEventsParams queryParams = new GetEventsParams(); + queryParams.setAlarmName("系统异常"); + queryParams.setStartTime(1704067200000L); // 2024-01-01 + queryParams.setEndTime(1706745600000L); // 2024-01-31 + queryParams.setSeverity("major"); + queryParams.setCategory("system"); + queryParams.setEventType("Uncleared"); + queryParams.setOrderBy("first_occur_time desc"); + queryParams.setLanguage("zh-cn"); + queryParams.setPageNum(1); + queryParams.setPageSize(10); + + ResultVo events = TerrabaseSDK.monitoringService().getEventsByPage(queryParams); + if ("200".equals(events.getCode()) && events.getData() != null) { + EventsCollection collection = events.getData(); + System.out.println("查询到 " + collection.getTotalCount() + " 条告警事件"); + if (collection.getEvents() != null) { + for (EventObject event : collection.getEvents()) { + System.out.println(" 事件: " + event.getEventName() + " (" + event.getEventType() + ")"); + } + } + } + + } catch (Exception e) { + System.err.println("监控告警操作失败: " + e.getMessage()); + } + } + + /** + * 高级使用示例:自定义JAR路径 + */ + public static void advancedUsageExample() { + System.out.println("\n=== 高级使用示例 ==="); + + try { + // 使用自定义JAR路径创建SDK实例 + // 注意:这需要修改TerrabaseSDK类以支持自定义路径 + System.out.println("高级使用示例需要扩展SDK类以支持自定义配置"); + + } catch (Exception e) { + System.err.println("高级使用示例失败: " + e.getMessage()); + } + } +} diff --git a/business-app/src/main/resources/application.properties b/business-app/src/main/resources/application.properties new file mode 100644 index 0000000..ecbd905 --- /dev/null +++ b/business-app/src/main/resources/application.properties @@ -0,0 +1,48 @@ +# Terrabase Business Application 配置文件 + +# 应用基本信息 +spring.application.name=terrabase-business-app +# +## nacos相关配置 +#spring.cloud.nacos.discovery.server-addr=https://consulservice:18302 +#spring.cloud.nacos.discovery.username=consul +#spring.cloud.nacos.discovery.password=${NACOS_PASSWORD} +#spring.cloud.nacos.discovery.port=8080 +#spring.cloud.nacos.discovery.ip=${SERVICE_NAME} +#spring.cloud.nacos.discovery.secure=true +#spring.cloud.nacos.discovery.cluster-name=DEFAULT +# +#loadbalancer.client.name=${SERVICE_NAME} +# 在未配置 Nacos 地址时,默认关闭注册与发现,避免启动失败 +spring.cloud.nacos.discovery.enabled=false +spring.cloud.nacos.discovery.register-enabled=false +# +server.port=8080 + +# JAR包路径配置 +# 企业服务实现JAR包的存放路径 +enterprise.jar.path=./lib + +# 日志配置 +logging.level.com.terrabase=INFO +logging.level.org.springframework.web=DEBUG +logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n + +# 企业服务缓存配置 +enterprise.service.cache.enabled=true +enterprise.service.cache.ttl=3600 + +# 监控配置 +management.endpoints.web.exposure.include=health,info,metrics +management.endpoint.health.show-details=always +management.info.env.enabled=true + +# 应用信息 +info.app.name=Terrabase Business Application +info.app.description=企业级数据使能平台 - 业务应用模块 +info.app.version=1.0.0 +info.app.encoding=UTF-8 +info.app.java.version=17 + +# 商业版加解密REST接口配置 +# 已移除不再使用的商业版REST配置项 diff --git a/enterprise-api/pom.xml b/enterprise-api/pom.xml index 4e09329..bf74517 100644 --- a/enterprise-api/pom.xml +++ b/enterprise-api/pom.xml @@ -60,6 +60,26 @@ slf4j-api + + + org.bouncycastle + bcprov-jdk15on + 1.70 + + + + org.bouncycastle + bcpkix-jdk15on + 1.70 + + + + + commons-codec + commons-codec + 1.15 + + org.springframework.boot @@ -67,6 +87,13 @@ test + + org.projectlombok + lombok + 1.18.32 + provided + + junit junit @@ -85,6 +112,14 @@ 17 + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + + com.terrabase.enterprise.api.sdk.example.TerrabaseSDKExample + + \ No newline at end of file diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/CertificateService.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/CertificateService.java new file mode 100644 index 0000000..48e8397 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/CertificateService.java @@ -0,0 +1,28 @@ +package com.terrabase.enterprise.api; + +import com.terrabase.enterprise.api.dto.*; +import com.terrabase.enterprise.api.response.ResultVo; +import java.util.List; + +/** + * 证书管理服务接口 + * 提供证书注册、导入、查询等功能 + * + * @author Terrabase Team + * @version 1.0.0 + */ +public interface CertificateService { + + // ========== 证书管理相关接口 ========== + /** + * 获取oms管理的所有证书信息接口 + * @return 证书收集信息结果对象 + */ + ResultVo> listCertificateServiceList(); + + /** + * License信息查询接口 + * @return License信息结果对象 + */ + ResultVo getLicenseInfo(); +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/CryptoAlgorithm.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/CryptoAlgorithm.java new file mode 100644 index 0000000..5221271 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/CryptoAlgorithm.java @@ -0,0 +1,139 @@ +package com.terrabase.enterprise.api; + +/** + * 加密算法枚举 + * 定义系统支持的各种加密算法 + * + * @author Terrabase Team + * @version 1.0.0 + */ +public enum CryptoAlgorithm { + + /** + * AES加密算法 - 高级加密标准 + * 支持128、192、256位密钥长度 + */ + AES("AES", "AES/GCM/NoPadding", 256), + + /** + * RSA加密算法 - 非对称加密 + * 支持1024、2048、4096位密钥长度 + */ + RSA("RSA", "RSA/ECB/PKCS1Padding", 2048), + + /** + * DES加密算法 - 数据加密标准 + * 支持56位密钥长度 + */ + DES("DES", "DES/CBC/PKCS5Padding", 56), + + /** + * 3DES加密算法 - 三重数据加密标准 + * 支持112、168位密钥长度 + */ + TRIPLE_DES("DESede", "DESede/CBC/PKCS5Padding", 168), + + /** + * Blowfish加密算法 + * 支持32-448位可变密钥长度 + */ + BLOWFISH("Blowfish", "Blowfish/CBC/PKCS5Padding", 128), + + /** + * ChaCha20加密算法 + * 流密码算法 + */ + CHACHA20("ChaCha20", "ChaCha20-Poly1305", 256); + + private final String algorithm; + private final String transformation; + private final int defaultKeyLength; + + /** + * 构造函数 + * @param algorithm 算法名称 + * @param transformation 转换模式 + * @param defaultKeyLength 默认密钥长度(位) + */ + CryptoAlgorithm(String algorithm, String transformation, int defaultKeyLength) { + this.algorithm = algorithm; + this.transformation = transformation; + this.defaultKeyLength = defaultKeyLength; + } + + /** + * 获取算法名称 + * @return 算法名称 + */ + public String getAlgorithm() { + return algorithm; + } + + /** + * 获取转换模式 + * @return 转换模式 + */ + public String getTransformation() { + return transformation; + } + + /** + * 获取默认密钥长度 + * @return 默认密钥长度(位) + */ + public int getDefaultKeyLength() { + return defaultKeyLength; + } + + /** + * 根据算法名称获取枚举值 + * @param algorithmName 算法名称 + * @return 对应的枚举值,如果未找到则返回AES + */ + public static CryptoAlgorithm fromString(String algorithmName) { + if (algorithmName == null || algorithmName.trim().isEmpty()) { + return AES; // 默认使用AES + } + + for (CryptoAlgorithm algorithm : values()) { + if (algorithm.getAlgorithm().equalsIgnoreCase(algorithmName.trim())) { + return algorithm; + } + } + + // 如果未找到匹配的算法,返回AES作为默认值 + return AES; + } + + /** + * 检查是否为对称加密算法 + * @return true如果是对称加密算法 + */ + public boolean isSymmetric() { + return this != RSA; + } + + /** + * 检查是否为非对称加密算法 + * @return true如果是非对称加密算法 + */ + public boolean isAsymmetric() { + return this == RSA; + } + + /** + * 检查是否为流密码算法 + * @return true如果是流密码算法 + */ + public boolean isStreamCipher() { + return this == CHACHA20; + } + + /** + * 检查是否为分组密码算法 + * @return true如果是分组密码算法 + */ + public boolean isBlockCipher() { + return this != CHACHA20; + } +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/CryptoService.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/CryptoService.java new file mode 100644 index 0000000..707869a --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/CryptoService.java @@ -0,0 +1,31 @@ +package com.terrabase.enterprise.api; + +import com.terrabase.enterprise.api.dto.*; + +/** + * 加解密服务接口 + * 提供数据加密和解密功能 + * + * @author Yehong Pan + * @version 1.0.0 + */ +public interface CryptoService { + + /** + * 加密接口 + * @param plaintext 明文数据 + * @param algorithm 加密算法,如果为null则使用默认AES算法 + * @param username 用户名 + * @return 密文数据 + */ + String encrypt(String plaintext, CryptoAlgorithm algorithm, String username); + + /** + * 解密接口 + * @param ciphertext 密文数据 + * @param algorithm 解密算法,如果为null则使用默认AES算法 + * @param username 用户名 + * @return 明文数据 + */ + String decrypt(String ciphertext, CryptoAlgorithm algorithm, String username); +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/LogService.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/LogService.java new file mode 100644 index 0000000..b3b9fc5 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/LogService.java @@ -0,0 +1,24 @@ +package com.terrabase.enterprise.api; + +import com.terrabase.enterprise.api.dto.*; +import com.terrabase.enterprise.api.request.LogAttributeVo; +import com.terrabase.enterprise.api.response.ResultVo; + +import java.util.List; + +public interface LogService { + + /** + * 上报审计日志接口 + * @param logs 日志对象列表 + * @return ResultVo<日志条数> + */ + ResultVo registerLogs(List logs); + + /** + * 注册审计日志国际化 + * @param logI18ns 国际化对象列表 + * @return ResultVo<注册结果> + */ + ResultVo registryInternational(List logI18ns); +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/MenuService.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/MenuService.java new file mode 100644 index 0000000..3d011ef --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/MenuService.java @@ -0,0 +1,18 @@ +package com.terrabase.enterprise.api; + +import com.terrabase.enterprise.api.dto.MenuRegisterInfo; + +/** + * 菜单管理服务接口 + * 提供菜单注册功能 + * + * @author Yehong Pan + * @version 1.0.0 + */ +public interface MenuService { + /** + * 注册菜单信息 + * @param menuRegisterInfo 菜单注册信息对象 + */ + void registerMenuInfo(MenuRegisterInfo menuRegisterInfo); +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/MonitoringService.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/MonitoringService.java new file mode 100644 index 0000000..c237e03 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/MonitoringService.java @@ -0,0 +1,38 @@ +package com.terrabase.enterprise.api; + +import com.terrabase.enterprise.api.dto.*; +import com.terrabase.enterprise.api.request.RegisterEventDefineReq; +import com.terrabase.enterprise.api.request.GetEventsParams; +import com.terrabase.enterprise.api.response.ResultVo; +import java.util.List; + +/** + * 监控告警服务接口 + * 提供告警定义注册、告警上报、告警查询等功能 + * + * @author Yehong Pan + * @version 1.0.0 + */ +public interface MonitoringService { + + /** + * 批量注册告警定义接口 + * @param req 注册请求 + * @return 注册结果 + */ + ResultVo registerEventDefine(RegisterEventDefineReq req); + + /** + * 上报告警接口 + * @param alarmInfos 告警信息列表 + * @return 是否上报成功 + */ + ResultVo sendEvents(List alarmInfos); + + /** + * 批量查询告警信息接口 + * @param getEventsParams 查询请求 + * @return 查询结果 + */ + ResultVo getEventsByPage(GetEventsParams getEventsParams); +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/UserManagementService.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/UserManagementService.java new file mode 100644 index 0000000..5f37d09 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/UserManagementService.java @@ -0,0 +1,54 @@ +package com.terrabase.enterprise.api; + +import com.terrabase.enterprise.api.dto.AuthorityInfo; +import com.terrabase.enterprise.api.dto.LoginUserDto; +import com.terrabase.enterprise.api.dto.ResourceGroup; +import com.terrabase.enterprise.api.request.RoleRegisterVo; +import com.terrabase.enterprise.api.response.ResultVo; + +import java.util.List; + +/** + * 用户管理服务接口 + * 提供角色、权限、菜单等用户管理功能 + * + * @author Yehong Pan + * @version 1.0.0 + */ +public interface UserManagementService { + + // ========== 用户注册相关接口 ========== + + /** + * 批量角色注册接口 + * @param roleRegister 角色注册对象 + */ + void batchRegisterRole(RoleRegisterVo roleRegister); + + /** + * 批量权限注册接口 + * @param authorityInfos 权限信息列表 + */ + void registerPermission(List authorityInfos); + + /** + * 获取用户资源组列表 + * @param userName 用户名 + * @return 用户资源组列表 + */ + List getUserGroups(String userName); + + // ========== 用户认证相关接口 ========== + + /** + * 根据token查询角色名接口 + * @return 角色名列表结果对象 + */ + ResultVo> queryRolesByToken(); + + /** + * 获取当前用户信息接口 + * @return 当前用户信息结果对象 + */ + ResultVo> getCurrentUserInfo(); +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/AuthorityInfo.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/AuthorityInfo.java new file mode 100644 index 0000000..0f907c3 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/AuthorityInfo.java @@ -0,0 +1,46 @@ +package com.terrabase.enterprise.api.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class AuthorityInfo { + /** + * 资源唯一标识 + */ + @JsonProperty("Description") + private String description; + + /** + * 资源唯一标识 + */ + @JsonProperty("ResourceKey") + private String resourceKey; + + /** + * 是否跳过权限检查 + */ + @JsonProperty("SkipCheck") + private boolean skipCheck; + + /** + * 所需角色 + */ + @JsonProperty("Roles") + private List roles = new ArrayList<>(10); +} + + + + diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/CertCollectInfo.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/CertCollectInfo.java new file mode 100644 index 0000000..23c06aa --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/CertCollectInfo.java @@ -0,0 +1,38 @@ +package com.terrabase.enterprise.api.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class CertCollectInfo { + private String productName; + + private long issueTime; + + private long expirationTime; + + private String issuer; + + private String subject; + + private String serialNumber; + + private String certType; + + private String status; + + private Integer alertBeforeExpirationDays; + + private String certName; + + private String productVersion; + + private String patchVersion; + + private String deviceEsn; +} + + + + diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/Ciphertext.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/Ciphertext.java new file mode 100644 index 0000000..392a50a --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/Ciphertext.java @@ -0,0 +1,14 @@ +package com.terrabase.enterprise.api.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Ciphertext { + @JsonProperty("keyId") + String keyId; +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/EventDefine.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/EventDefine.java new file mode 100644 index 0000000..2455324 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/EventDefine.java @@ -0,0 +1,53 @@ +package com.terrabase.enterprise.api.dto; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.*; + +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class EventDefine { + private @NotEmpty String eventId; + + private @NotEmpty String name; + + private String effect; + + private @NotEmpty String category; + + private String description; + + private String subjectType; + + private @NotEmpty @Pattern( + regexp = "^(alter|event)$" + ) String type; + + private @NotEmpty String parts; + + private String cause; + + private @Pattern( + regexp = "^(warning|minor|major|critical)$" + ) String severity; + + private String suggestion; + + private String version; + + private @Pattern( + regexp = "^(zh|zh-cn|en|en-us)$" + ) String language; + + private @Size( + max = 256 + ) String defineMatchKey; +} + + + + diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/EventInfo.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/EventInfo.java new file mode 100644 index 0000000..783c634 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/EventInfo.java @@ -0,0 +1,136 @@ +package com.terrabase.enterprise.api.dto; + +import com.fasterxml.jackson.annotation.JsonAlias; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Pattern; +import lombok.*; +import org.hibernate.validator.constraints.Length; + +import java.util.List; + +@Setter +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class EventInfo { + private @Pattern( + regexp = "^\\d{1,32}$", + message = "id is a number" + ) String id; + + private @Pattern( + regexp = "^[A-Za-z0-9:_\\-=]{1,128}$", + message = "serialNumber is an uuid" + ) String serialNumber; + + private Integer syncNo; + + private @Length( + max = 255, + message = "eventName must less than 255" + ) String eventName; + + private @NotEmpty @Pattern( + regexp = "^(alter|event)$" + ) String eventType; + + private @NotBlank @Length( + max = 255, + message = "evenSubject must less than 255" + ) String eventSubject; + + private @NotBlank @Length( + max = 255, + message = "evenSubjectType must less than 255" + ) String eventSubjectType; + + private @Length( + max = 8000 + ) String eventDescription; + + private @Length( + max = 64 + ) List eventDescriptionArgs; + + private @NotBlank @Pattern( + regexp = "warning|minor|major|critical" + ) String severity; + + private @Length( + max = 8000 + ) String effect; + + private @NotBlank @Length( + max = 128, + message = "evenCategory must less than 255" + ) String eventCategory; + + @JsonAlias({"possibleCause", "cause"}) + private @Length( + max = 8000 + ) String possibleCause; + + private @Length( + max = 8000 + ) String suggestion; + + private @NotBlank @Pattern( + regexp = "Uncleared|Cleared", + message = "status must be Uncleared or Cleared" + ) String status; + + private @Length( + max = 32 + ) String firstOccurTime; + + private @Length( + max = 32 + ) String clearTime; + + private @Length( + max = 255, + message = "eventSource must less than 255" + ) String evenSource; + + private @Length( + max = 255, + message = "deviceSn must less than 255" + ) String deviceSn; + + private @Length( + max = 255, + message = "deviceType must less than 255" + ) String deviceType; + + private @Length( + max = 255 + ) String devURL; + + private @NotBlank @Length( + max = 64 + ) String parts; + + private @Pattern( + regexp = "^(zh|zh-cn|en|en-us)$" + ) String language; + + private int computerCategory; + + private boolean shouldDeleted; + + private String clearType; + + private String defineMatchKey; + + private String lastEventMatchKey; + + private Boolean shouldSaveDefine = true; + + private String deviceId; +} + + + + diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/EventObject.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/EventObject.java new file mode 100644 index 0000000..3c4854f --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/EventObject.java @@ -0,0 +1,66 @@ +package com.terrabase.enterprise.api.dto; + +import lombok.*; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class EventObject { + protected String id; + + protected String eventId; + + protected String eventName; + + protected String eventType; + + protected String eventCategory; + + protected String deviceSn; + + protected String deviceId; + + protected String eventSubject; + + protected String severity; + + protected String eventSource; + + protected String parts; + + protected String clearType; + + protected String clearUser; + + protected String status; + + protected String confirmStatus; + + protected boolean blockingStatus; + + protected int occurCounts; + + protected Long clearTime; + + protected Long firstOccurTime; + + protected Long lastOccurTime; + + protected String readFlag; + + protected String eventDescription; + + protected String eventDescriptionArgs; + + protected String suggestion; + + protected String cause; + + protected Boolean sendToeService; + + protected String serialNumber; + + protected String devUrl; +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/EventsCollection.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/EventsCollection.java new file mode 100644 index 0000000..3841e28 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/EventsCollection.java @@ -0,0 +1,18 @@ +package com.terrabase.enterprise.api.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Setter +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class EventsCollection { + private List events; + + private int totalCount; +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/LicenseInfo.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/LicenseInfo.java new file mode 100644 index 0000000..8e40302 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/LicenseInfo.java @@ -0,0 +1,29 @@ +package com.terrabase.enterprise.api.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class LicenseInfo { + /** + * status许可证状态 + * 0没有导入 1已经正常上传但是未激活 2已经激活 3已经注销 + */ + private String status; + + /** + * sbom信息 + */ + private List sboms; +} + + + + diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/LicenseInfoEx.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/LicenseInfoEx.java new file mode 100644 index 0000000..5896087 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/LicenseInfoEx.java @@ -0,0 +1,18 @@ +package com.terrabase.enterprise.api.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Setter +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class LicenseInfoEx { + private String name; + + private String total; + + private String unit; +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/LogI18n.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/LogI18n.java new file mode 100644 index 0000000..98138b0 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/LogI18n.java @@ -0,0 +1,22 @@ +package com.terrabase.enterprise.api.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class LogI18n { + private String code; + + private String language; + + private String content; +} + + + + diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/LoginUserDto.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/LoginUserDto.java new file mode 100644 index 0000000..fe23988 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/LoginUserDto.java @@ -0,0 +1,20 @@ +package com.terrabase.enterprise.api.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class LoginUserDto { + private String userName; + + private String userId; + + private String role; + + private List resourceGroups; +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/MenuInfo.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/MenuInfo.java new file mode 100644 index 0000000..a52f330 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/MenuInfo.java @@ -0,0 +1,24 @@ +package com.terrabase.enterprise.api.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class MenuInfo { + private int id; + + private String menuId; + + private String parentMenuId; + + private String menuName; + + private String url; + + private String en; + + private String zh; + + private boolean enable; +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/MenuRegisterInfo.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/MenuRegisterInfo.java new file mode 100644 index 0000000..2197ae8 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/MenuRegisterInfo.java @@ -0,0 +1,40 @@ +package com.terrabase.enterprise.api.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MenuRegisterInfo { + @NotNull + private String menuId; + + private String parentMenuId; + + @JsonProperty("menuName") + private String menuNameCode; + + private String url; + + private String iconUrl; + + private String en; + + private String zh; + + private boolean enable; + + private List roleScneMap; + + private int weight; +} \ No newline at end of file diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/Plaintext.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/Plaintext.java new file mode 100644 index 0000000..638767b --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/Plaintext.java @@ -0,0 +1,14 @@ +package com.terrabase.enterprise.api.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class Plaintext { + @ToString.Exclude + @JsonProperty("plain") + private String plain; +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/ResourceGroup.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/ResourceGroup.java new file mode 100644 index 0000000..3adc860 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/ResourceGroup.java @@ -0,0 +1,9 @@ +package com.terrabase.enterprise.api.dto; + +public record ResourceGroup(String id, String parentId, String name, String description) { + private static final String PUBLIC_GROUP_ID = "000"; + + public static ResourceGroup buildPublicGroup() { + return new ResourceGroup(PUBLIC_GROUP_ID, null, "publicGroup", "systemPublicGroup"); + } +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/RoleI18nInfo.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/RoleI18nInfo.java new file mode 100644 index 0000000..fd025ee --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/RoleI18nInfo.java @@ -0,0 +1,34 @@ +package com.terrabase.enterprise.api.dto; + +import jakarta.validation.constraints.NotNull; +import lombok.*; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class RoleI18nInfo { + /** + * 角色名 + */ + private String name; + + /** + * 角色关键字 + */ + @NotNull + private String code; + + /** + * 语言 + */ + @NotNull + private String language; + + /** + * 角色的描述内容 + */ + @NotNull + private String content; +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/RoleRegisterInfo.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/RoleRegisterInfo.java new file mode 100644 index 0000000..af4a6c8 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/RoleRegisterInfo.java @@ -0,0 +1,41 @@ +package com.terrabase.enterprise.api.dto; + +import jakarta.validation.constraints.NotNull; +import lombok.*; +import org.hibernate.validator.constraints.Length; + +@Setter +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class RoleRegisterInfo { + /** + * 角色名 + */ + @Length(max = 64) + @NotNull + private String name; + + /** + * 角色名国际化code代码 + */ + @NotNull + private String nameCode; + + /** + * 角色描述国际化code + */ + private String description; + + /** + * 是否可以创建此类用户 0 不支持 1 支持 + */ + private boolean creatable; + + /** + * 角色支持创建的账户登录类型,0-未定义 1-人机 2-机机 3-人机和机机 4-内部登录 + * (以二进制具体为是否为1代表支持某类型,比如001表示人机,010表示机机,100表示内部,011表示人机和机机) + */ + private Integer supportLoginType; +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/RoleScenInfo.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/RoleScenInfo.java new file mode 100644 index 0000000..c3210cf --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/dto/RoleScenInfo.java @@ -0,0 +1,22 @@ +package com.terrabase.enterprise.api.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class RoleScenInfo { + @JsonProperty("menuId") + private String menuId; + + @JsonProperty("roleName") + private String roleName; + + @JsonProperty("scenId") + private String scenId; +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/request/GetEventsParams.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/request/GetEventsParams.java new file mode 100644 index 0000000..e502034 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/request/GetEventsParams.java @@ -0,0 +1,101 @@ +package com.terrabase.enterprise.api.request; + +import jakarta.validation.constraints.*; +import lombok.*; +import org.hibernate.validator.constraints.Length; + +import java.util.List; + +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class GetEventsParams { + private @Size( + max = 128 + ) String alarmName; + + private @Positive Long startTime; + + private @Positive Long endTime; + + private @NotBlank @Pattern( + regexp = "warning|minor|major|critical" + ) String severity; + + private @Pattern( + regexp = "^(?! )[_\\- .a-zA-Z0-9]+(? categorys; + + private String cabinetId; + + private @Size( + max = 512 + ) String devUrl; + + private @Min(1L) Integer pageNum; + + private @Max(2000L) Integer pageSize; + + private boolean specifyStartLine; +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/request/LogAttributeVo.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/request/LogAttributeVo.java new file mode 100644 index 0000000..54aff3a --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/request/LogAttributeVo.java @@ -0,0 +1,30 @@ +package com.terrabase.enterprise.api.request; + +import lombok.*; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class LogAttributeVo { + private long sn; + + private String logType; + + private String username; + + private String operation; + + private String source; + + private String terminal; + + private String result; + + private String flag; + + private String paramType; + + private String detail; +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/request/RegisterEventDefineReq.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/request/RegisterEventDefineReq.java new file mode 100644 index 0000000..ab0d1d1 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/request/RegisterEventDefineReq.java @@ -0,0 +1,33 @@ +package com.terrabase.enterprise.api.request; + +import com.terrabase.enterprise.api.dto.EventDefine; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Size; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Setter +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class RegisterEventDefineReq { + private static final int MAX_SIZE = 1000; + + private @NotBlank String serviceName; + + private String ServiceEn; + + private String ServiceZh; + + private boolean deleteAll = false; + + private @Valid @NotEmpty @Size( + max=1000 + ) List eventDefines; +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/request/RoleRegisterVo.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/request/RoleRegisterVo.java new file mode 100644 index 0000000..f20f4bc --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/request/RoleRegisterVo.java @@ -0,0 +1,20 @@ +package com.terrabase.enterprise.api.request; + +import com.terrabase.enterprise.api.dto.RoleI18nInfo; +import com.terrabase.enterprise.api.dto.RoleRegisterInfo; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Setter +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class RoleRegisterVo { + private List roleRegisterInfos; + + private List roleI18nInfos; +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/response/ResultVo.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/response/ResultVo.java new file mode 100644 index 0000000..8a49d72 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/response/ResultVo.java @@ -0,0 +1,109 @@ +package com.terrabase.enterprise.api.response; + +/** + * 通用响应结果包装类 + * 用于统一API响应格式 + * + * @param 响应数据类型 + * @author Yehong Pan + * @version 1.0.0 + */ +public class ResultVo { + + /** + * 响应状态码 + */ + private String code; + + /** + * 响应消息 + */ + private String msg; + + /** + * 响应数据 + */ + private T data; + + public ResultVo() {} + + public ResultVo(String code, String msg) { + this.code = code; + this.msg = msg; + } + + public ResultVo(String code, String msg, T data) { + this.code = code; + this.msg = msg; + this.data = data; + } + + /** + * 成功响应 + */ + public static ResultVo success() { + return new ResultVo<>("200", "操作成功"); + } + + /** + * 成功响应带数据 + */ + public static ResultVo success(T data) { + return new ResultVo<>("200", "操作成功", data); + } + + /** + * 成功响应带消息和数据 + */ + public static ResultVo success(String msg, T data) { + return new ResultVo<>("200", msg, data); + } + + /** + * 失败响应 + */ + public static ResultVo error(String code, String msg) { + return new ResultVo<>(code, msg); + } + + /** + * 失败响应带数据 + */ + public static ResultVo error(String code, String msg, T data) { + return new ResultVo<>(code, msg, data); + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + @Override + public String toString() { + return "ResultVo{" + + "code='" + code + '\'' + + ", msg='" + msg + '\'' + + ", data=" + data + + '}'; + } +} + diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/sdk/StandaloneJarLoadUtil.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/sdk/StandaloneJarLoadUtil.java new file mode 100644 index 0000000..20993b4 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/sdk/StandaloneJarLoadUtil.java @@ -0,0 +1,407 @@ +package com.terrabase.enterprise.api.sdk; + +import com.terrabase.enterprise.api.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 独立的企业服务加载工具类(不依赖Spring容器) + * 用于SDK中动态加载企业服务实现 + * + * @author Yehong Pan + * @version 1.0.0 + */ +public class StandaloneJarLoadUtil { + + private static final Logger logger = LoggerFactory.getLogger(StandaloneJarLoadUtil.class); + + private final String jarPath; + + // 缓存已加载的实例 + private final ConcurrentHashMap serviceInstances = new ConcurrentHashMap<>(); + + /** + * 构造函数 + * + * @param jarPath JAR包路径 + */ + public StandaloneJarLoadUtil(String jarPath) { + this.jarPath = jarPath != null ? jarPath : findJarPathStatic(); + } + + /** + * 默认构造函数,使用默认路径 + */ + public StandaloneJarLoadUtil() { + this(findJarPathStatic()); + } + + /** + * 加载加解密服务 + * @return 加解密服务实例 + */ + public CryptoService loadCryptoService() { + return (CryptoService) loadService("crypto_service", + "com.terrabase.enterprise.impl.commercial.CommercialCryptoServiceImpl", + "com.terrabase.enterprise.impl.open.OpenCryptoServiceImpl"); + } + + /** + * 加载用户管理服务 + * @return 用户管理服务实例 + */ + public UserManagementService loadUserManagementService() { + return (UserManagementService) loadService("user_management_service", + "com.terrabase.enterprise.impl.commercial.CommercialUserManagementServiceImpl", + "com.terrabase.enterprise.impl.open.OpenUserManagementServiceImpl"); + } + + /** + * 加载日志服务 + * @return 日志服务实例 + */ + public LogService loadLogService() { + return (LogService) loadService("log_service", + "com.terrabase.enterprise.impl.commercial.CommercialLogServiceImpl", + "com.terrabase.enterprise.impl.open.OpenLogServiceImpl"); + } + + /** + * 加载证书管理服务 + * @return 证书管理服务实例 + */ + public CertificateService loadCertificateService() { + return (CertificateService) loadService("certificate_service", + "com.terrabase.enterprise.impl.commercial.CommercialCertificateServiceImpl", + "com.terrabase.enterprise.impl.open.OpenCertificateServiceImpl"); + } + + /** + * 加载监控告警服务 + * @return 监控告警服务实例 + */ + public MonitoringService loadMonitoringService() { + return (MonitoringService) loadService("monitoring_service", + "com.terrabase.enterprise.impl.commercial.CommercialMonitoringServiceImpl", + "com.terrabase.enterprise.impl.open.OpenMonitoringServiceImpl"); + } + + /** + * 加载菜单服务 + * @return 菜单服务实例 + */ + public MenuService loadMenuService() { + return (MenuService) loadService("menu_service", + "com.terrabase.enterprise.impl.commercial.CommercialMenuServiceImpl", + "com.terrabase.enterprise.impl.open.OpenMenuServiceImpl"); + } + + /** + * 通用服务加载方法 + * @param cacheKey 缓存键 + * @param commercialClassName 商业版类名 + * @param openClassName 开源版类名 + * @return 服务实例 + */ + private Object loadService(String cacheKey, String commercialClassName, String openClassName) { + try { + // 先检查缓存 + Object cachedService = serviceInstances.get(cacheKey); + if (cachedService != null) { + logger.info("从缓存中获取服务实例: {}", cacheKey); + return cachedService; + } + + logger.info("开始检测并加载服务: {}", cacheKey); + + Object service; + if (isCommercialJarAvailable()) { + logger.info("检测到商业版JAR包,加载商业版服务: {}", commercialClassName); + service = loadServiceFromJar(commercialClassName); + } else if (isOpenJarAvailable()) { + logger.info("检测到开源版JAR包,加载开源版服务: {}", openClassName); + service = loadServiceFromJar(openClassName); + } else { + logger.info("未检测到JAR包,尝试从类路径加载开源版服务: {}", openClassName); + service = loadServiceFromClasspath(openClassName); + } + + // 如果服务加载失败,使用开源版作为降级方案 + if (service == null) { + logger.warn("服务加载失败,尝试从JAR包加载开源版作为降级方案: {}", openClassName); + if (isOpenJarAvailable()) { + service = loadServiceFromJar(openClassName); + } else { + service = loadServiceFromClasspath(openClassName); + } + } + + // 如果开源版也加载失败,抛出异常 + if (service == null) { + throw new RuntimeException("无法加载任何服务实现: " + cacheKey); + } + + // 缓存服务实例 + serviceInstances.put(cacheKey, service); + + logger.info("服务加载成功: {}", cacheKey); + + return service; + + } catch (Exception e) { + logger.error("加载服务失败: {}, 尝试使用开源版作为降级方案", cacheKey, e); + try { + // 最后的降级方案:尝试从JAR包或类路径加载开源版服务 + Object fallbackService = null; + if (isOpenJarAvailable()) { + fallbackService = loadServiceFromJar(openClassName); + } else { + fallbackService = loadServiceFromClasspath(openClassName); + } + + if (fallbackService != null) { + serviceInstances.put(cacheKey, fallbackService); + logger.warn("使用开源版服务作为降级方案: {}", cacheKey); + return fallbackService; + } + } catch (Exception fallbackException) { + logger.error("降级方案也失败了: {}", cacheKey, fallbackException); + } + throw new RuntimeException("无法加载任何服务实现: " + cacheKey, e); + } + } + + + /** + * 检测商业版JAR包是否可用 + * @return true如果JAR包存在且可加载 + */ + private boolean isCommercialJarAvailable() { + try { + String jarFileName = "enterprise-impl-commercial-1.0.0.jar"; + File jarFile = new File(jarPath, jarFileName); + + if (!jarFile.exists()) { + logger.debug("商业版JAR文件不存在: {}", jarFile.getAbsolutePath()); + return false; + } + + // 尝试加载JAR包中的类来验证JAR包是否有效 + URL jarUrl = jarFile.toURI().toURL(); + URLClassLoader classLoader = new URLClassLoader(new URL[]{jarUrl}, this.getClass().getClassLoader()); + + // 尝试加载商业版实现类 + Class clazz = classLoader.loadClass("com.terrabase.enterprise.impl.commercial.CommercialCryptoServiceImpl"); + if (clazz != null) { + logger.debug("商业版JAR包验证成功: {}", jarFile.getAbsolutePath()); + return true; + } + + } catch (Exception e) { + logger.debug("商业版JAR包检测失败: {}", e.getMessage()); + } + + return false; + } + + /** + * 检测开源版JAR包是否可用 + * @return true如果JAR包存在且可加载 + */ + private boolean isOpenJarAvailable() { + try { + String jarFileName = "enterprise-impl-open-1.0.0.jar"; + File jarFile = new File(jarPath, jarFileName); + + if (!jarFile.exists()) { + logger.debug("开源版JAR文件不存在: {}", jarFile.getAbsolutePath()); + return false; + } + + // 尝试加载JAR包中的类来验证JAR包是否有效 + URL jarUrl = jarFile.toURI().toURL(); + URLClassLoader classLoader = new URLClassLoader(new URL[]{jarUrl}, this.getClass().getClassLoader()); + + // 尝试加载开源版实现类 + Class clazz = classLoader.loadClass("com.terrabase.enterprise.impl.open.OpenCryptoServiceImpl"); + if (clazz != null) { + logger.debug("开源版JAR包验证成功: {}", jarFile.getAbsolutePath()); + return true; + } + + } catch (Exception e) { + logger.debug("开源版JAR包检测失败: {}", e.getMessage()); + } + + return false; + } + + + + + /** + * 从JAR包加载服务(通用方法) + * @param className 类名 + * @return 服务实例 + */ + private Object loadServiceFromJar(String className) { + try { + String jarFileName = getJarFileName(className); + File jarFile = new File(jarPath, jarFileName); + + if (!jarFile.exists()) { + logger.warn("JAR文件不存在: {}", jarFile.getAbsolutePath()); + return null; + } + + logger.info("从JAR文件加载: {}", jarFile.getAbsolutePath()); + + URL jarUrl = jarFile.toURI().toURL(); + URLClassLoader classLoader = new URLClassLoader(new URL[]{jarUrl}, this.getClass().getClassLoader()); + + Class clazz = classLoader.loadClass(className); + + // 仅使用默认构造函数创建实例 + Constructor defaultConstructor = clazz.getDeclaredConstructor(); + defaultConstructor.setAccessible(true); + Object instance = defaultConstructor.newInstance(); + logger.info("使用默认构造函数创建商业版服务实例"); + + return instance; + + } catch (Exception e) { + logger.warn("从JAR包加载失败: {}", e.getMessage()); + return null; + } + } + + /** + * 从类路径加载服务(通用方法) + * @param className 类名 + * @return 服务实例 + */ + private Object loadServiceFromClasspath(String className) { + try { + logger.info("从类路径加载: {}", className); + + Class clazz = Class.forName(className); + + // 仅使用默认构造函数创建实例 + Constructor defaultConstructor = clazz.getDeclaredConstructor(); + defaultConstructor.setAccessible(true); + Object instance = defaultConstructor.newInstance(); + logger.info("使用默认构造函数创建服务实例"); + + return instance; + + } catch (Exception e) { + logger.warn("从类路径加载失败: {}", e.getMessage()); + return null; + } + } + + + + /** + * 根据类名获取JAR文件名 + * @param className 类名 + * @return JAR文件名 + */ + private String getJarFileName(String className) { + if (className.contains("commercial")) { + return "enterprise-impl-commercial-1.0.0.jar"; + } else if (className.contains("open")) { + return "enterprise-impl-open-1.0.0.jar"; + } + return "unknown.jar"; + } + + /** + * 清理缓存 + */ + public void clearCache() { + logger.info("清理服务缓存"); + serviceInstances.clear(); + } + + /** + * 智能查找 JAR 路径(静态方法) + * 支持 IntelliJ、Maven、Eclipse 等多种开发环境 + * @return JAR 路径 + */ + private static String findJarPathStatic() { + logger.info("开始智能查找 JAR 路径..."); + + // 获取当前工作目录 + String currentDir = System.getProperty("user.dir"); + logger.debug("当前工作目录: {}", currentDir); + + // 可能的 JAR 路径(按优先级排序) + String[] possiblePaths = { + "lib", // 当前目录下的 lib(IntelliJ 常用) + "./lib", // 明确指定当前目录(Maven 常用) + "../lib", // 上级目录下的 lib + "../../lib", // 上两级目录 + currentDir + "/lib", // 基于工作目录的绝对路径 + "C:/Terrabase/lib", // Windows 绝对路径 + "/Terrabase/lib", // Linux 绝对路径 + currentDir + "/../lib", // 工作目录的上级 lib + currentDir + "/../../lib" // 工作目录的上两级 lib + }; + + // 遍历所有可能的路径 + for (String path : possiblePaths) { + logger.debug("检查路径: {}", path); + File libDir = new File(path); + + if (libDir.exists() && libDir.isDirectory()) { + File[] files = libDir.listFiles(); + if (files != null) { + for (File file : files) { + if (file.getName().contains("enterprise-impl") && file.getName().endsWith(".jar")) { + logger.info("✅ 找到 JAR 路径: {} (包含文件: {})", path, file.getName()); + return path; + } + } + } + } + } + + // 如果都没找到,返回默认路径 + logger.warn("⚠️ 未找到 JAR 文件,使用默认路径: ./lib"); + logger.info("请确保以下路径之一存在 JAR 文件:"); + for (String path : possiblePaths) { + logger.info(" - {}", path); + } + return "./lib"; + } + + /** + * 智能查找 JAR 路径(公共方法) + * 支持 IntelliJ、Maven、Eclipse 等多种开发环境 + * @return JAR 路径 + */ + public static String findJarPath() { + return findJarPathStatic(); + } + + /** + * 获取当前企业模式 + * @return 企业模式 + */ + public String getEnterpriseMode() { + if (isCommercialJarAvailable()) { + return "commercial"; + } else if (isOpenJarAvailable()) { + return "open"; + } else { + return "classpath"; + } + } +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/sdk/TerrabaseSDK.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/sdk/TerrabaseSDK.java new file mode 100644 index 0000000..98c9cd1 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/sdk/TerrabaseSDK.java @@ -0,0 +1,461 @@ +package com.terrabase.enterprise.api.sdk; + +import com.terrabase.enterprise.api.*; +import com.terrabase.enterprise.api.CryptoAlgorithm; +import com.terrabase.enterprise.api.dto.*; +import com.terrabase.enterprise.api.response.ResultVo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Terrabase SDK 主入口类 + * 提供统一的静态调用接口,支持所有企业服务功能 + * + *

使用方式

+ *
+ * // 方式1:直接使用静态方法(最简洁)
+ * String encrypted = TerrabaseSDK.cryptoService().encrypt("hello", CryptoAlgorithm.AES, "user1");
+ * String decrypted = TerrabaseSDK.cryptoService().decrypt(encrypted, CryptoAlgorithm.AES, "user1");
+ * 
+ * // 方式2:通过getInstance()获取实例后调用
+ * String encrypted = TerrabaseSDK.getInstance().crypto().encrypt("hello", CryptoAlgorithm.AES, "user1");
+ * String decrypted = TerrabaseSDK.getInstance().crypto().decrypt(encrypted, CryptoAlgorithm.AES, "user1");
+ * 
+ * // 方式3:先初始化再调用
+ * TerrabaseSDK sdk = TerrabaseSDK.init();
+ * String encrypted = sdk.crypto().encrypt("hello", CryptoAlgorithm.AES, "user1");
+ * ResultVo<List<LoginUserDto>> users = sdk.userManagement().getCurrentUserInfo();
+ * 
+ * // 方式4:使用指定证书路径初始化(推荐用于HTTPS环境)
+ * TerrabaseSDK sdkWithCert = TerrabaseSDK.initWithCertPath("/path/to/trust/cert.pem");
+ * String encrypted = sdkWithCert.crypto().encrypt("hello", CryptoAlgorithm.AES, "user1");
+ * 
+ * // 方式5:使用证书路径和Nacos配置初始化(商业版)
+ * TerrabaseSDK commercialSdk = TerrabaseSDK.initWithCertPathAndNacos(
+ *     "/path/to/trust/cert.pem", 
+ *     "https://nacos-server:8848", 
+ *     "nacos", 
+ *     "password"
+ * );
+ * 
+ * // 日志服务
+ * TerrabaseSDK.logService().log("INFO", "用户登录成功", "user1");
+ * TerrabaseSDK.logService().log("ERROR", "系统异常", "user1");
+ * 
+ * // 用户管理服务
+ * ResultVo<List<LoginUserDto>> users = TerrabaseSDK.userManagementService().getCurrentUserInfo();
+ * 
+ * // 证书管理服务
+ * ResultVo<CertificateDto> cert = TerrabaseSDK.certificateService().generateCertificate(request);
+ * ResultVo<Boolean> valid = TerrabaseSDK.certificateService().validateCertificate("cert123");
+ * 
+ * // 监控告警服务
+ * ResultVo<Boolean> alert = TerrabaseSDK.monitoringService().sendAlert(alertRequest);
+ * ResultVo<List<MetricDto>> metrics = TerrabaseSDK.monitoringService().getMetrics("cpu_usage", "2024-01-01", "2024-01-31");
+ * 
+ * // 获取企业模式信息
+ * String mode = TerrabaseSDK.getInstance().getEnterpriseMode();
+ * String info = TerrabaseSDK.getServiceInfo();
+ * 
+ * + *

配置初始化方式(高级用法)

+ *
+ * // 使用自定义配置初始化
+ * TerrabaseSDKConfig config = new TerrabaseSDKConfig();
+ * config.setJarPath("/path/to/enterprise/jars");
+ * config.setTrustCertPath("/path/to/trust/cert.pem");  // 设置证书路径
+ * TerrabaseSDK.init(config);
+ * 
+ * // 商业版配置(启用Nacos)
+ * TerrabaseSDKConfig commercialConfig = TerrabaseSDKConfig.createCommercial(
+ *     "https://nacos-server:8848", "nacos", "password");
+ * commercialConfig.setTrustCertPath("/path/to/trust/cert.pem");
+ * TerrabaseSDK.init(commercialConfig);
+ * 
+ * + * @author Yehong Pan + * @version 1.0.0 + */ +public class TerrabaseSDK { + + private static final Logger logger = LoggerFactory.getLogger(TerrabaseSDK.class); + + // SDK实例缓存 + private static volatile TerrabaseSDK instance; + private static final Object lock = new Object(); + + // 服务实例缓存 + private final Map serviceCache = new ConcurrentHashMap<>(); + + // 服务加载器 + private final StandaloneJarLoadUtil jarLoadUtil; + + // SDK配置 + private final TerrabaseSDKConfig config; + + /** + * 私有构造函数,防止外部实例化 + */ + private TerrabaseSDK() { + this.config = TerrabaseSDKConfig.createDefault(); + // 使用智能路径查找,如果配置中没有指定路径 + String jarPath = config.getJarPath(); + if (jarPath == null || jarPath.trim().isEmpty()) { + jarPath = StandaloneJarLoadUtil.findJarPath(); + config.setJarPath(jarPath); + logger.info("使用智能路径查找结果: {}", jarPath); + } + this.jarLoadUtil = new StandaloneJarLoadUtil(jarPath); + logger.info("TerrabaseSDK 实例已创建"); + } + + /** + * 带配置的私有构造函数 + * @param config SDK配置 + */ + private TerrabaseSDK(TerrabaseSDKConfig config) { + this.config = config != null ? config : TerrabaseSDKConfig.createDefault(); + // 使用智能路径查找,如果配置中没有指定路径 + String jarPath = this.config.getJarPath(); + if (jarPath == null || jarPath.trim().isEmpty()) { + jarPath = StandaloneJarLoadUtil.findJarPath(); + this.config.setJarPath(jarPath); + logger.info("使用智能路径查找结果: {}", jarPath); + } + this.jarLoadUtil = new StandaloneJarLoadUtil(jarPath); + + // 如果是商业版且启用了Nacos,则初始化Nacos配置 + initializeNacosIfNeeded(); + + logger.info("TerrabaseSDK 实例已创建,配置: {}", this.config); + } + + /** + * 初始化SDK + * 此方法会检测并加载企业服务实现 + * + * @return SDK实例 + */ + public static TerrabaseSDK init() { + if (instance == null) { + synchronized (lock) { + if (instance == null) { + instance = new TerrabaseSDK(); + logger.info("TerrabaseSDK 初始化完成"); + } + } + } + return instance; + } + + /** + * 使用配置初始化SDK + * 此方法会检测并加载企业服务实现 + * + * @param config SDK配置 + * @return SDK实例 + */ + public static TerrabaseSDK init(TerrabaseSDKConfig config) { + if (instance == null) { + synchronized (lock) { + if (instance == null) { + instance = new TerrabaseSDK(config); + logger.info("TerrabaseSDK 初始化完成,配置: {}", config); + } + } + } + return instance; + } + + /** + * 使用证书路径初始化SDK + * 此方法会检测并加载企业服务实现,并设置指定的证书路径 + * + * @param trustCertPath 信任证书路径 + * @return SDK实例 + */ + public static TerrabaseSDK initWithCertPath(String trustCertPath) { + if (instance == null) { + synchronized (lock) { + if (instance == null) { + TerrabaseSDKConfig config = TerrabaseSDKConfig.createDefault(); + config.setTrustCertPath(trustCertPath); + instance = new TerrabaseSDK(config); + logger.info("TerrabaseSDK 使用证书路径初始化完成: {}", trustCertPath); + } + } + } + return instance; + } + + /** + * 使用证书路径和Nacos配置初始化SDK(商业版) + * 此方法会检测并加载企业服务实现,并设置指定的证书路径和Nacos配置 + * + * @param trustCertPath 信任证书路径 + * @param nacosServerAddr Nacos服务器地址 + * @param nacosUsername Nacos用户名 + * @param nacosPassword Nacos密码 + * @return SDK实例 + */ + public static TerrabaseSDK initWithCertPathAndNacos(String trustCertPath, String nacosServerAddr, String nacosUsername, String nacosPassword) { + if (instance == null) { + synchronized (lock) { + if (instance == null) { + TerrabaseSDKConfig config = TerrabaseSDKConfig.createCommercial(nacosServerAddr, nacosUsername, nacosPassword); + config.setTrustCertPath(trustCertPath); + instance = new TerrabaseSDK(config); + logger.info("TerrabaseSDK 使用证书路径和Nacos配置初始化完成,证书路径: {}, Nacos地址: {}", trustCertPath, nacosServerAddr); + } + } + } + return instance; + } + + /** + * 获取SDK实例(如果未初始化会自动初始化) + * + * @return SDK实例 + */ + public static TerrabaseSDK getInstance() { + if (instance == null) { + return init(); + } + return instance; + } + + /** + * 获取加解密服务 + * + * @return 加解密服务实例 + */ + public CryptoService crypto() { + return getService("crypto_service", jarLoadUtil::loadCryptoService); + } + + /** + * 获取用户管理服务 + * + * @return 用户管理服务实例 + */ + public UserManagementService userManagement() { + return getService("user_management_service", jarLoadUtil::loadUserManagementService); + } + + /** + * 获取日志服务 + * + * @return 日志服务实例 + */ + public LogService log() { + return getService("log_service", jarLoadUtil::loadLogService); + } + + /** + * 获取证书管理服务 + * + * @return 证书管理服务实例 + */ + public CertificateService certificate() { + return getService("certificate_service", jarLoadUtil::loadCertificateService); + } + + /** + * 获取监控告警服务 + * + * @return 监控告警服务实例 + */ + public MonitoringService monitoring() { + return getService("monitoring_service", jarLoadUtil::loadMonitoringService); + } + + /** + * 获取菜单服务 + * + * @return 菜单服务实例 + */ + public MenuService menu() { + return getService("menu_service", jarLoadUtil::loadMenuService); + } + + + // ==================== 静态方法(便捷调用) ==================== + + /** + * 获取加解密服务(静态方法) + * + * @return 加解密服务实例 + */ + public static CryptoService cryptoService() { + return getInstance().crypto(); + } + + /** + * 获取用户管理服务(静态方法) + * + * @return 用户管理服务实例 + */ + public static UserManagementService userManagementService() { + return getInstance().userManagement(); + } + + /** + * 获取日志服务(静态方法) + * + * @return 日志服务实例 + */ + public static LogService logService() { + return getInstance().log(); + } + + /** + * 获取证书管理服务(静态方法) + * + * @return 证书管理服务实例 + */ + public static CertificateService certificateService() { + return getInstance().certificate(); + } + + /** + * 获取监控告警服务(静态方法) + * + * @return 监控告警服务实例 + */ + public static MonitoringService monitoringService() { + return getInstance().monitoring(); + } + + /** + * 获取菜单服务(静态方法) + * + * @return 菜单服务实例 + */ + public static MenuService menuService() { + return getInstance().menu(); + } + + + /** + * 获取服务信息(静态方法) + * 返回包含企业模式等信息的字符串 + * + * @return 服务信息字符串 + */ + public static String getServiceInfo() { + try { + StringBuilder info = new StringBuilder(); + info.append("企业模式: ").append(getInstance().getEnterpriseMode()).append("\n"); + info.append("JAR路径: ").append(getInstance().getConfig().getJarPath()).append("\n"); + info.append("Nacos发现: ").append(getInstance().getConfig().isNacosDiscoveryEnabled() ? "启用" : "禁用"); + return info.toString(); + } catch (Exception e) { + logger.error("获取服务信息失败", e); + return "获取服务信息失败: " + e.getMessage(); + } + } + + /** + * 获取当前企业模式 + * + * @return 企业模式(commercial 或 open) + */ + public String getEnterpriseMode() { + return jarLoadUtil.getEnterpriseMode(); + } + + /** + * 获取SDK配置 + * + * @return SDK配置 + */ + public TerrabaseSDKConfig getConfig() { + return config; + } + + /** + * 清理服务缓存 + */ + public void clearCache() { + serviceCache.clear(); + jarLoadUtil.clearCache(); + logger.info("SDK服务缓存已清理"); + } + + /** + * 如果需要,初始化Nacos配置 + * 仅在商业版且启用Nacos时执行 + */ + private void initializeNacosIfNeeded() { + try { + // 检查是否为商业版 + String enterpriseMode = jarLoadUtil.getEnterpriseMode(); + if (!"commercial".equals(enterpriseMode)) { + logger.debug("非商业版模式,跳过Nacos配置初始化"); + return; + } + + // 检查是否启用了Nacos + if (!config.isNacosDiscoveryEnabled()) { + logger.debug("Nacos服务发现未启用,跳过Nacos配置初始化"); + return; + } + + // 动态加载商业版Nacos配置类 + Class nacosConfigClass = Class.forName("com.terrabase.enterprise.impl.commercial.config.CommercialNacosConfig"); + java.lang.reflect.Method initializeMethod = nacosConfigClass.getMethod("initializeFromSDKConfig", TerrabaseSDKConfig.class); + initializeMethod.invoke(null, config); + + logger.info("商业版Nacos配置初始化完成"); + + } catch (ClassNotFoundException e) { + logger.debug("商业版Nacos配置类不存在,跳过Nacos配置初始化"); + } catch (Exception e) { + logger.warn("初始化Nacos配置时发生异常,但不影响SDK正常使用", e); + } + } + + /** + * 获取服务实例(带缓存) + * + * @param serviceName 服务名称 + * @param loader 服务加载器 + * @return 服务实例 + */ + @SuppressWarnings("unchecked") + private T getService(String serviceName, ServiceLoader loader) { + try { + // 先检查缓存 + T cachedService = (T) serviceCache.get(serviceName); + if (cachedService != null) { + return cachedService; + } + + // 加载服务 + T service = loader.load(); + if (service != null) { + serviceCache.put(serviceName, service); + logger.debug("服务已加载并缓存: {}", serviceName); + } + + return service; + + } catch (Exception e) { + logger.error("加载服务失败: {}", serviceName, e); + throw new RuntimeException("无法加载服务: " + serviceName, e); + } + } + + /** + * 服务加载器接口 + */ + @FunctionalInterface + private interface ServiceLoader { + T load() throws Exception; + } + +} diff --git a/enterprise-api/src/main/java/com/terrabase/enterprise/api/sdk/TerrabaseSDKConfig.java b/enterprise-api/src/main/java/com/terrabase/enterprise/api/sdk/TerrabaseSDKConfig.java new file mode 100644 index 0000000..9f16df4 --- /dev/null +++ b/enterprise-api/src/main/java/com/terrabase/enterprise/api/sdk/TerrabaseSDKConfig.java @@ -0,0 +1,360 @@ +package com.terrabase.enterprise.api.sdk; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Terrabase SDK 配置类 + * 支持外部配置注入,解决SDK以JAR包形式使用时配置隔离问题 + * + * @author Yehong Pan + * @version 1.0.0 + */ +public class TerrabaseSDKConfig { + + private static final Logger logger = LoggerFactory.getLogger(TerrabaseSDKConfig.class); + + // ==================== Nacos 配置(仅商业版使用) ==================== + private String nacosServerAddr = "https://consulservice:18302"; // 默认Nacos服务器地址 + private String nacosUsername = "consul"; // 默认用户名 + private String nacosPassword; // 密码通过脚本自动获取 + private String nacosPort = "8080"; + private String nacosIp = "terrabase"; // 默认服务注册IP为terrabase + private boolean nacosSecure = true; + private String nacosClusterName = "DEFAULT"; + private boolean nacosDiscoveryEnabled = false; // 默认关闭,仅商业版启用 + private boolean nacosRegisterEnabled = false; // 默认关闭,仅商业版启用 + + // ==================== SSL 配置 ==================== + private boolean tlsEnabled = true; // 默认启用TLS + private boolean clientAuth = true; // 默认启用客户端认证 + private String trustCertPath = "/opt/huawei/fce/runtime/security/server_cert/nacos/nacos.crt"; // 默认证书路径 + + // ==================== 服务配置 ==================== + private String serviceName; + private String servicePort = "8008"; // 默认服务端口为8008 + + // ==================== JAR包路径配置 ==================== + private String jarPath = "./lib"; + + // ==================== 认证配置 ==================== + private String machineToken; + private String authToken; + + // ==================== 其他配置 ==================== + private Map customProperties = new HashMap<>(); + + /** + * 默认构造函数 + */ + public TerrabaseSDKConfig() { + } + + /** + * 创建默认配置(开源版) + * 开源版不启用Nacos服务注册和发现 + * @return 默认配置实例 + */ + public static TerrabaseSDKConfig createDefault() { + TerrabaseSDKConfig config = new TerrabaseSDKConfig(); + // 开源版默认不启用Nacos + config.setNacosDiscoveryEnabled(false); + config.setNacosRegisterEnabled(false); + return config; + } + + /** + * 创建商业版配置 + * 商业版启用Nacos服务注册和发现 + * @param nacosServerAddr Nacos服务器地址 + * @param nacosUsername Nacos用户名 + * @param nacosPassword Nacos密码 + * @return 商业版配置实例 + */ + public static TerrabaseSDKConfig createCommercial(String nacosServerAddr, String nacosUsername, String nacosPassword) { + TerrabaseSDKConfig config = new TerrabaseSDKConfig(); + config.setNacosServerAddr(nacosServerAddr); + config.setNacosUsername(nacosUsername); + config.setNacosPassword(nacosPassword); + // 商业版启用Nacos + config.setNacosDiscoveryEnabled(true); + config.setNacosRegisterEnabled(true); + return config; + } + + /** + * 创建带默认值的商业版配置 + * 使用默认的Nacos服务器地址和用户名,密码通过脚本自动获取 + * @return 商业版配置实例 + */ + public static TerrabaseSDKConfig createCommercialWithDefaults() { + TerrabaseSDKConfig config = new TerrabaseSDKConfig(); + // 使用默认值,密码通过脚本自动获取(带重试机制) + config.setNacosPassword(getPasswordWithFallback()); + // 商业版启用Nacos + config.setNacosDiscoveryEnabled(true); + config.setNacosRegisterEnabled(true); + return config; + } + + // ==================== Getter/Setter 方法 ==================== + + public String getNacosServerAddr() { + return nacosServerAddr; + } + + public void setNacosServerAddr(String nacosServerAddr) { + this.nacosServerAddr = nacosServerAddr; + } + + public String getNacosUsername() { + return nacosUsername; + } + + public void setNacosUsername(String nacosUsername) { + this.nacosUsername = nacosUsername; + } + + public String getNacosPassword() { + return nacosPassword; + } + + public void setNacosPassword(String nacosPassword) { + this.nacosPassword = nacosPassword; + } + + public String getNacosPort() { + return nacosPort; + } + + public void setNacosPort(String nacosPort) { + this.nacosPort = nacosPort; + } + + public String getNacosIp() { + return nacosIp; + } + + public void setNacosIp(String nacosIp) { + this.nacosIp = nacosIp; + } + + public boolean isNacosSecure() { + return nacosSecure; + } + + public void setNacosSecure(boolean nacosSecure) { + this.nacosSecure = nacosSecure; + } + + public String getNacosClusterName() { + return nacosClusterName; + } + + public void setNacosClusterName(String nacosClusterName) { + this.nacosClusterName = nacosClusterName; + } + + public boolean isNacosDiscoveryEnabled() { + return nacosDiscoveryEnabled; + } + + public void setNacosDiscoveryEnabled(boolean nacosDiscoveryEnabled) { + this.nacosDiscoveryEnabled = nacosDiscoveryEnabled; + } + + public boolean isNacosRegisterEnabled() { + return nacosRegisterEnabled; + } + + public void setNacosRegisterEnabled(boolean nacosRegisterEnabled) { + this.nacosRegisterEnabled = nacosRegisterEnabled; + } + + public boolean isTlsEnabled() { + return tlsEnabled; + } + + public void setTlsEnabled(boolean tlsEnabled) { + this.tlsEnabled = tlsEnabled; + } + + public boolean isClientAuth() { + return clientAuth; + } + + public void setClientAuth(boolean clientAuth) { + this.clientAuth = clientAuth; + } + + public String getTrustCertPath() { + return trustCertPath; + } + + public void setTrustCertPath(String trustCertPath) { + this.trustCertPath = trustCertPath; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getServicePort() { + return servicePort; + } + + public void setServicePort(String servicePort) { + this.servicePort = servicePort; + } + + public String getJarPath() { + return jarPath; + } + + public void setJarPath(String jarPath) { + this.jarPath = jarPath; + } + + public String getMachineToken() { + return machineToken; + } + + public void setMachineToken(String machineToken) { + this.machineToken = machineToken; + } + + public String getAuthToken() { + return authToken; + } + + public void setAuthToken(String authToken) { + this.authToken = authToken; + } + + public Map getCustomProperties() { + return customProperties; + } + + public void setCustomProperties(Map customProperties) { + this.customProperties = customProperties; + } + + /** + * 添加自定义属性 + * @param key 属性键 + * @param value 属性值 + */ + public void addCustomProperty(String key, String value) { + this.customProperties.put(key, value); + } + + /** + * 获取自定义属性 + * @param key 属性键 + * @return 属性值 + */ + public String getCustomProperty(String key) { + return this.customProperties.get(key); + } + + /** + * 通过脚本获取Nacos密码 + * 执行脚本获取加密密码并解密 + * @return 解密后的密码 + */ + public static String getPasswordFromScript() { + try { + logger.info("开始执行密码获取脚本..."); + + // 执行脚本获取加密密码 + ProcessBuilder pb = new ProcessBuilder("bash", "-c", + "grep keypass_tomcat /opt/huawei/fce/runtime/security/server_cert/nacos/nacos.conf | awk -F= '{print $2}'"); + Process process = pb.start(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String encryptedPassword = reader.readLine(); + reader.close(); + + if (encryptedPassword == null || encryptedPassword.trim().isEmpty()) { + logger.warn("无法获取加密密码,使用默认密码"); + return "default-password"; + } + + logger.info("成功获取加密密码,开始解密..."); + + // 执行Python脚本解密 + ProcessBuilder pythonPb = new ProcessBuilder("python", "-c", + "import kmc.kmc as K; import os; os.environ['KMC_DATA_USER']='tomcat'; nacos_pass='" + + encryptedPassword.trim() + "'; plain_nacos_pass=K.API().decrypt(0,nacos_pass); print(plain_nacos_pass)"); + Process pythonProcess = pythonPb.start(); + + BufferedReader pythonReader = new BufferedReader(new InputStreamReader(pythonProcess.getInputStream())); + String decryptedPassword = pythonReader.readLine(); + pythonReader.close(); + + if (decryptedPassword == null || decryptedPassword.trim().isEmpty()) { + logger.warn("密码解密失败,使用默认密码"); + return "default-password"; + } + + logger.info("密码获取成功"); + return decryptedPassword.trim(); + + } catch (Exception e) { + logger.error("密码获取脚本执行失败,使用默认密码", e); + return "default-password"; + } + } + + /** + * 获取密码(带重试机制) + * 如果脚本获取失败,会尝试从环境变量获取 + * @return 密码 + */ + public static String getPasswordWithFallback() { + // 首先尝试从脚本获取 + String password = getPasswordFromScript(); + + // 如果脚本获取失败,尝试从环境变量获取 + if ("default-password".equals(password)) { + String envPassword = System.getenv("NACOS_PASSWORD"); + if (envPassword != null && !envPassword.trim().isEmpty()) { + logger.info("从环境变量获取密码"); + return envPassword; + } + } + + return password; + } + + /** + * 验证配置是否有效 + * @return 是否有效 + */ + public boolean isValid() { + if (nacosDiscoveryEnabled) { + return nacosServerAddr != null && !nacosServerAddr.trim().isEmpty(); + } + return true; + } + + @Override + public String toString() { + return "TerrabaseSDKConfig{" + + "nacosServerAddr='" + nacosServerAddr + '\'' + + ", nacosUsername='" + nacosUsername + '\'' + + ", nacosDiscoveryEnabled=" + nacosDiscoveryEnabled + + ", nacosRegisterEnabled=" + nacosRegisterEnabled + + ", serviceName='" + serviceName + '\'' + + ", jarPath='" + jarPath + '\'' + + '}'; + } +} diff --git a/enterprise-impl-commercial/pom.xml b/enterprise-impl-commercial/pom.xml index 41d62ef..1d24194 100644 --- a/enterprise-impl-commercial/pom.xml +++ b/enterprise-impl-commercial/pom.xml @@ -43,12 +43,6 @@ spring-boot-starter-web
- - - org.springframework.boot - spring-boot-starter-security - - org.springframework.boot @@ -61,6 +55,23 @@ spring-boot-starter-webflux + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + com.fasterxml.jackson.core @@ -94,9 +105,10 @@ - org.springframework.security - spring-security-test - test + org.projectlombok + lombok + 1.18.32 + provided diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/CommercialCertificateServiceImpl.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/CommercialCertificateServiceImpl.java new file mode 100644 index 0000000..7810121 --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/CommercialCertificateServiceImpl.java @@ -0,0 +1,81 @@ +package com.terrabase.enterprise.impl.commercial; + +import com.terrabase.enterprise.api.CertificateService; +import com.terrabase.enterprise.api.dto.*; +import com.terrabase.enterprise.api.response.ResultVo; +import com.terrabase.enterprise.impl.commercial.client.CertFeignClient; +import com.terrabase.enterprise.impl.commercial.client.LicenseClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 商业版证书管理服务实现 + * 集成商业组件实现证书管理功能 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@Service +public class CommercialCertificateServiceImpl implements CertificateService { + + private static final Logger logger = LoggerFactory.getLogger(CommercialCertificateServiceImpl.class); + + @Autowired + private LicenseClient licenseClient; + + @Autowired + private CertFeignClient certFeignClient; + + // ========== 证书管理相关接口实现 ========== + + @Override + public ResultVo> listCertificateServiceList() { + try { + logger.info("商业版执行证书信息查询"); + + // 使用 Feign 客户端调用远程服务 + ResultVo> result = certFeignClient.listCertificateServiceList(); + + if ("200".equals(result.getCode())) { + logger.info("商业版Feign客户端证书信息查询成功,查询到证书数量: {}", + result.getData() != null ? result.getData().size() : 0); + } else { + logger.error("商业版Feign客户端证书信息查询失败: {}", result.getMsg()); + } + + return result; + + } catch (Exception e) { + logger.error("商业版证书信息查询失败", e); + return ResultVo.error("500", "证书信息查询失败: " + e.getMessage()); + } + } + + @Override + public ResultVo getLicenseInfo() { + try { + logger.info("商业版执行License信息查询"); + + // 使用LicenseClient调用远程服务 + ResultVo result = licenseClient.getLicenseInfo(); + + if ("200".equals(result.getCode())) { + logger.info("商业版License信息查询成功 - 状态: {}, SBOM模块数量: {}", + result.getData().getStatus(), + result.getData().getSboms() != null ? result.getData().getSboms().size() : 0); + } else { + logger.error("商业版License信息查询失败: {}", result.getMsg()); + } + + return result; + + } catch (Exception e) { + logger.error("商业版License信息查询失败", e); + return ResultVo.error("500", "License信息查询失败: " + e.getMessage()); + } + } +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/CommercialCryptoServiceImpl.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/CommercialCryptoServiceImpl.java new file mode 100644 index 0000000..1adf914 --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/CommercialCryptoServiceImpl.java @@ -0,0 +1,95 @@ +package com.terrabase.enterprise.impl.commercial; + +import com.terrabase.enterprise.api.CryptoService; +import com.terrabase.enterprise.api.CryptoAlgorithm; +import com.terrabase.enterprise.api.dto.Ciphertext; +import com.terrabase.enterprise.api.dto.Plaintext; +import com.terrabase.enterprise.api.response.ResultVo; +import com.terrabase.enterprise.impl.commercial.client.CryptoFeighClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import java.util.UUID; + +/** + * 商业版加解密服务实现 + * 集成商业组件实现加解密功能 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@Service +public class CommercialCryptoServiceImpl implements CryptoService { + + private static final Logger logger = LoggerFactory.getLogger(CommercialCryptoServiceImpl.class); + + private final CryptoFeighClient cryptoFeighClient; + + public CommercialCryptoServiceImpl(CryptoFeighClient cryptoFeighClient) { + this.cryptoFeighClient = cryptoFeighClient; + } + + + @Override + public String encrypt(String plaintext, CryptoAlgorithm algorithm, String username) { + + if (plaintext == null || plaintext.trim().isEmpty()) { + return "明文数据不能为空"; + } + + try { + logger.info("商业版执行数据加密,原文长度: {}, 用户: {}", plaintext.length(), username); + + // 生成UUID作为keyId + String keyId = UUID.randomUUID().toString(); + + // 调用Feign客户端进行加密 + ResultVo result = cryptoFeighClient.encrypt(keyId, new Plaintext(plaintext), username); + + if (result != null && "200".equals(result.getCode()) && result.getData() != null) { + String returnedKeyId = result.getData().getKeyId(); + logger.info("商业版Feign加密成功,返回keyId: {}", returnedKeyId); + return returnedKeyId; + } + + logger.error("商业版Feign加密失败: {}", (result != null ? result.getMsg() : "结果为空")); + return "加密失败: " + (result != null ? result.getMsg() : "结果为空"); + + } catch (Exception e) { + logger.error("商业版加密失败", e); + return "加密失败: " + e.getMessage(); + } + } + + @Override + public String decrypt(String ciphertext, CryptoAlgorithm algorithm, String username) { + + if (ciphertext == null || ciphertext.trim().isEmpty()) { + return "密文数据不能为空"; + } + + try { + logger.info("商业版执行数据解密,密文长度: {}, 用户: {}", ciphertext.length(), username); + + // ciphertext在新语义下为keyId + String keyId = ciphertext; + + // 调用Feign客户端进行解密 + ResultVo result = cryptoFeighClient.decrypt(keyId, username); + + if (result != null && "200".equals(result.getCode()) && result.getData() != null) { + String plain = result.getData().getPlain(); + logger.info("商业版Feign解密成功,返回明文长度: {}", (plain != null ? plain.length() : 0)); + return plain; + } + + logger.error("商业版Feign解密失败: {}", (result != null ? result.getMsg() : "结果为空")); + return "解密失败: " + (result != null ? result.getMsg() : "结果为空"); + + } catch (Exception e) { + logger.error("商业版解密失败", e); + return "解密失败: " + e.getMessage(); + } + } +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/CommercialLogServiceImpl.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/CommercialLogServiceImpl.java new file mode 100644 index 0000000..b82420c --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/CommercialLogServiceImpl.java @@ -0,0 +1,86 @@ +package com.terrabase.enterprise.impl.commercial; + +import com.terrabase.enterprise.api.LogService; +import com.terrabase.enterprise.api.dto.*; +import com.terrabase.enterprise.api.request.LogAttributeVo; +import com.terrabase.enterprise.api.response.ResultVo; +import com.terrabase.enterprise.impl.commercial.client.OperateLogFeignClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 商业版日志管理服务实现 + * 集成商业组件实现日志管理功能 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@Service +public class CommercialLogServiceImpl implements LogService { + + private static final Logger logger = LoggerFactory.getLogger(CommercialLogServiceImpl.class); + + @Autowired + private OperateLogFeignClient operateLogFeignClient; + + @Override + public ResultVo<Integer> registerLogs(List<LogAttributeVo> logs) { + if (logs == null || logs.isEmpty()) { + logger.warn("审计日志对象不能为空"); + return ResultVo.error("400", "审计日志对象不能为空"); + } + + try { + logger.info("商业版执行审计日志上报,日志数量: {}", logs.size()); + + // 使用 Feign 客户端调用远程服务 + ResultVo<Integer> result = operateLogFeignClient.registerLogs(logs); + + if ("200".equals(result.getCode())) { + logger.info("商业版审计日志上报成功,日志数量: {}", result.getData()); + return result; + } else { + logger.error("商业版审计日志上报失败: {}", result.getMsg()); + return ResultVo.error(result.getCode(), result.getMsg()); + } + + } catch (Exception e) { + logger.error("商业版审计日志上报失败", e); + return ResultVo.error("500", "审计日志上报失败: " + e.getMessage()); + } + } + + @Override + public ResultVo<Boolean> registryInternational(List<LogI18n> logI18ns) { + if (logI18ns == null || logI18ns.isEmpty()) { + logger.warn("审计日志国际化对象不能为空"); + return ResultVo.error("400", "审计日志国际化对象不能为空"); + } + + try { + logger.info("商业版执行审计日志国际化注册,国际化信息数量: {}", logI18ns.size()); + + // 使用 Feign 客户端调用远程服务 + ResultVo<Boolean> result = operateLogFeignClient.registryInternational(logI18ns); + + if ("200".equals(result.getCode())) { + logger.info("商业版审计日志国际化注册成功: {}", result.getData()); + return result; + } else { + logger.error("商业版审计日志国际化注册失败: {}", result.getMsg()); + return ResultVo.error(result.getCode(), result.getMsg()); + } + + } catch (Exception e) { + logger.error("商业版审计日志国际化注册失败", e); + return ResultVo.error("500", "审计日志国际化注册失败: " + e.getMessage()); + } + } +} + + + diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/CommercialMenuServiceImpl.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/CommercialMenuServiceImpl.java new file mode 100644 index 0000000..3b7d62c --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/CommercialMenuServiceImpl.java @@ -0,0 +1,57 @@ +package com.terrabase.enterprise.impl.commercial; + +import com.terrabase.enterprise.api.MenuService; +import com.terrabase.enterprise.api.dto.MenuRegisterInfo; +import com.terrabase.enterprise.api.dto.MenuInfo; +import com.terrabase.enterprise.api.response.ResultVo; +import com.terrabase.enterprise.impl.commercial.client.MenuFeignClient; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + + +/** + * 商业版菜单管理服务实现 + * 提供菜单注册功能 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@Service +public class CommercialMenuServiceImpl implements MenuService { + + private static final Logger logger = LoggerFactory.getLogger(CommercialMenuServiceImpl.class); + + @Autowired + private MenuFeignClient menuFeignClient; + + @Override + public void registerMenuInfo(MenuRegisterInfo menuRegisterInfo) { + if (menuRegisterInfo == null) { + logger.warn("菜单注册对象不能为空"); + return; + } + + try { + logger.info("商业版执行菜单注册 - 菜单ID: {}, 菜单名称: {}, URL: {}", + menuRegisterInfo.getMenuId(), menuRegisterInfo.getMenuNameCode(), menuRegisterInfo.getUrl()); + + // 使用Feign客户端进行菜单注册 + ResultVo<MenuInfo> result = menuFeignClient.registerMenuInfo(menuRegisterInfo); + + if (result != null && "200".equals(result.getCode())) { + logger.info("商业版Feign客户端菜单注册成功 - 菜单ID: {}, 菜单名称: {}", + menuRegisterInfo.getMenuId(), menuRegisterInfo.getMenuNameCode()); + } else { + logger.error("商业版Feign客户端菜单注册失败 - 响应码: {}, 消息: {}", + result != null ? result.getCode() : "null", + result != null ? result.getMsg() : "null"); + } + + } catch (Exception e) { + logger.error("商业版菜单注册失败: {}", menuRegisterInfo, e); + } + } +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/CommercialMonitoringServiceImpl.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/CommercialMonitoringServiceImpl.java new file mode 100644 index 0000000..5822add --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/CommercialMonitoringServiceImpl.java @@ -0,0 +1,115 @@ +package com.terrabase.enterprise.impl.commercial; + +import com.terrabase.enterprise.api.MonitoringService; +import com.terrabase.enterprise.api.dto.*; +import com.terrabase.enterprise.api.request.RegisterEventDefineReq; +import com.terrabase.enterprise.api.request.GetEventsParams; +import com.terrabase.enterprise.api.response.ResultVo; +import com.terrabase.enterprise.impl.commercial.client.AlarmFeignClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 商业版监控告警服务实现 + * 集成商业组件实现监控告警功能 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@Service +public class CommercialMonitoringServiceImpl implements MonitoringService { + + private static final Logger logger = LoggerFactory.getLogger(CommercialMonitoringServiceImpl.class); + + @Autowired + private AlarmFeignClient alarmFeignClient; + + @Override + public ResultVo registerEventDefine(RegisterEventDefineReq req) { + if (req == null) { + logger.warn("告警定义注册请求对象不能为空"); + return ResultVo.error("400", "告警定义注册请求对象不能为空"); + } + + try { + logger.info("商业版执行告警定义注册,服务名称: {}, 告警定义数量: {}", + req.getServiceName(), req.getEventDefines().size()); + + // 调用Feign客户端进行告警定义注册 + ResultVo result = alarmFeignClient.registerEventDefine(req); + + if ("200".equals(result.getCode())) { + logger.info("商业版告警定义注册成功,服务名称: {}", req.getServiceName()); + } else { + logger.error("商业版告警定义注册失败: {}", result.getMsg()); + } + + return result; + + } catch (Exception e) { + logger.error("商业版告警定义注册失败", e); + return ResultVo.error("500", "告警定义注册失败: " + e.getMessage()); + } + } + + @Override + public ResultVo<Boolean> sendEvents(List<EventInfo> alarmInfos) { + if (alarmInfos == null || alarmInfos.isEmpty()) { + logger.warn("告警信息列表不能为空"); + return ResultVo.error("400", "告警信息列表不能为空"); + } + + try { + logger.info("商业版执行告警上报,告警数量: {}", alarmInfos.size()); + + // 调用Feign客户端进行告警上报 + ResultVo<Boolean> result = alarmFeignClient.sendEvents(alarmInfos); + + if ("200".equals(result.getCode())) { + logger.info("商业版告警上报成功,告警数量: {}", alarmInfos.size()); + } else { + logger.error("商业版告警上报失败: {}", result.getMsg()); + } + + return result; + + } catch (Exception e) { + logger.error("商业版告警上报失败", e); + return ResultVo.error("500", "告警上报失败: " + e.getMessage()); + } + } + + @Override + public ResultVo<EventsCollection> getEventsByPage(GetEventsParams getEventsParams) { + if (getEventsParams == null) { + logger.warn("告警查询参数对象不能为空"); + return ResultVo.error("400", "告警查询参数对象不能为空"); + } + + try { + logger.info("商业版执行告警分页查询,页码: {}, 每页大小: {}", + getEventsParams.getPageNum(), getEventsParams.getPageSize()); + + // 调用Feign客户端进行告警分页查询 + ResultVo<EventsCollection> result = alarmFeignClient.getEventsByPage(getEventsParams); + + if ("200".equals(result.getCode())) { + EventsCollection eventsCollection = result.getData(); + logger.info("商业版告警分页查询成功,查询到告警数量: {}, 总记录数: {}", + eventsCollection.getEvents().size(), eventsCollection.getTotalCount()); + } else { + logger.error("商业版告警分页查询失败: {}", result.getMsg()); + } + + return result; + + } catch (Exception e) { + logger.error("商业版告警分页查询失败", e); + return ResultVo.error("500", "告警分页查询失败: " + e.getMessage()); + } + } +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/CommercialUserManagementServiceImpl.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/CommercialUserManagementServiceImpl.java new file mode 100644 index 0000000..674e3c1 --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/CommercialUserManagementServiceImpl.java @@ -0,0 +1,200 @@ +package com.terrabase.enterprise.impl.commercial; + +import com.terrabase.enterprise.api.UserManagementService; +import com.terrabase.enterprise.api.dto.AuthorityInfo; +import com.terrabase.enterprise.api.dto.LoginUserDto; +import com.terrabase.enterprise.api.dto.ResourceGroup; +import com.terrabase.enterprise.api.request.RoleRegisterVo; +import com.terrabase.enterprise.api.response.ResultVo; +import com.terrabase.enterprise.impl.commercial.client.ManualAuthenticationClient; +import com.terrabase.enterprise.impl.commercial.client.OmsExtensionClient; +import com.terrabase.enterprise.impl.commercial.client.PermissionFeignClient; +import com.terrabase.enterprise.impl.commercial.client.RoleFeignClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +/** + * 商业版用户管理服务实现 + * 集成商业组件实现用户管理功能 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@Service +public class CommercialUserManagementServiceImpl implements UserManagementService { + + private static final Logger logger = LoggerFactory.getLogger(CommercialUserManagementServiceImpl.class); + + @Autowired + private OmsExtensionClient omsExtensionClient; + + @Autowired + private ManualAuthenticationClient manualAuthenticationClient; + + @Autowired + private RoleFeignClient roleFeignClient; + + @Autowired + private PermissionFeignClient permissionFeignClient; + + // ========== 用户注册相关接口实现 ========== + + @Override + public void batchRegisterRole(RoleRegisterVo roleRegister) { + if (roleRegister == null) { + logger.warn("角色注册对象不能为空"); + return; + } + + try { + logger.info("商业版执行批量角色注册"); + + // 使用 RoleFeignClient 调用远程服务进行批量角色注册 + ResultVo<String> result = roleFeignClient.batchRegisterRole(roleRegister); + + if ("200".equals(result.getCode())) { + logger.info("商业版批量角色注册成功"); + + // 记录角色注册信息 + if (roleRegister.getRoleRegisterInfos() != null && !roleRegister.getRoleRegisterInfos().isEmpty()) { + logger.info("商业版处理角色注册信息,数量: {}", roleRegister.getRoleRegisterInfos().size()); + + for (com.terrabase.enterprise.api.dto.RoleRegisterInfo roleInfo : roleRegister.getRoleRegisterInfos()) { + logger.info("商业版角色注册成功 - 角色名: {}, 角色名代码: {}, 描述: {}, 可创建: {}, 支持登录类型: {}", + roleInfo.getName(), + roleInfo.getNameCode(), + roleInfo.getDescription(), + roleInfo.isCreatable(), + roleInfo.getSupportLoginType()); + } + } + + // 记录角色国际化信息 + if (roleRegister.getRoleI18nInfos() != null && !roleRegister.getRoleI18nInfos().isEmpty()) { + logger.info("商业版处理角色国际化信息,数量: {}", roleRegister.getRoleI18nInfos().size()); + + for (com.terrabase.enterprise.api.dto.RoleI18nInfo i18nInfo : roleRegister.getRoleI18nInfos()) { + logger.info("商业版角色国际化信息 - 角色名: {}, 代码: {}, 语言: {}, 内容: {}", + i18nInfo.getName(), + i18nInfo.getCode(), + i18nInfo.getLanguage(), + i18nInfo.getContent()); + } + } + } else { + logger.error("商业版批量角色注册失败: {}", result.getMsg()); + } + + } catch (Exception e) { + logger.error("商业版批量角色注册失败: {}", roleRegister, e); + } + } + + @Override + public void registerPermission(List<AuthorityInfo> authorityInfos) { + if (authorityInfos == null || authorityInfos.isEmpty()) { + logger.warn("权限注册列表不能为空"); + return; + } + + try { + logger.info("商业版执行批量权限注册,权限数量: {}", authorityInfos.size()); + + // 调用Feign客户端进行批量权限注册 + ResultVo<String> result = permissionFeignClient.registerPermission(authorityInfos); + + if ("200".equals(result.getCode())) { + logger.info("商业版批量权限注册成功,权限数量: {}", authorityInfos.size()); + for (AuthorityInfo authorityInfo : authorityInfos) { + logger.info("商业版权限注册成功 - 资源标识: {}, 描述: {}, 跳过检查: {}, 所需角色: {}", + authorityInfo.getResourceKey(), + authorityInfo.getDescription(), + authorityInfo.isSkipCheck(), + authorityInfo.getRoles()); + } + } else { + logger.error("商业版批量权限注册失败: {}", result.getMsg()); + } + + } catch (Exception e) { + logger.error("商业版批量权限注册失败: {}", authorityInfos, e); + } + } + + @Override + public List<ResourceGroup> getUserGroups(String userName) { + if (userName == null || userName.trim().isEmpty()) { + logger.warn("用户名不能为空"); + return new ArrayList<>(); + } + + try { + logger.info("商业版获取用户资源组: {}", userName); + + // 使用FeignClient调用OMS扩展服务获取用户资源组 + List<ResourceGroup> groups = omsExtensionClient.getUserGroups(userName); + + logger.info("商业版获取用户资源组成功 - 用户: {}, 资源组数量: {}", userName, groups.size()); + + return groups; + + } catch (Exception e) { + logger.error("商业版获取用户资源组失败 - 用户: {}", userName, e); + // 发生异常时返回默认的公共资源组 + List<ResourceGroup> defaultGroups = new ArrayList<>(); + defaultGroups.add(ResourceGroup.buildPublicGroup()); + return defaultGroups; + } + } + + // ========== 用户认证相关接口实现 ========== + + @Override + public ResultVo<List<String>> queryRolesByToken() { + try { + logger.info("商业版执行根据token查询角色名"); + + // 使用 Feign 客户端调用远程服务 + ResultVo<List<String>> result = manualAuthenticationClient.queryRolesByToken(); + + if ("200".equals(result.getCode())) { + logger.info("商业版根据token查询角色名成功,角色数量: {}", result.getData().size()); + return result; + } else { + logger.error("商业版根据token查询角色名失败: {}", result.getMsg()); + return ResultVo.error(result.getCode(), result.getMsg()); + } + + } catch (Exception e) { + logger.error("商业版根据token查询角色名失败", e); + return ResultVo.error("500", "角色查询失败: " + e.getMessage()); + } + } + + @Override + public ResultVo<List<LoginUserDto>> getCurrentUserInfo() { + try { + logger.info("商业版执行获取当前用户信息"); + + // 使用 Feign 客户端调用远程服务 + ResultVo<List<LoginUserDto>> result = manualAuthenticationClient.sessionCur(); + + if ("200".equals(result.getCode())) { + logger.info("商业版获取当前用户信息成功,用户数量: {}", result.getData().size()); + return result; + } else { + logger.error("商业版获取当前用户信息失败: {}", result.getMsg()); + return ResultVo.error(result.getCode(), result.getMsg()); + } + + } catch (Exception e) { + logger.error("商业版获取当前用户信息失败", e); + return ResultVo.error("500", "用户信息查询失败: " + e.getMessage()); + } + } +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/AlarmFeignClient.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/AlarmFeignClient.java new file mode 100644 index 0000000..fba595b --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/AlarmFeignClient.java @@ -0,0 +1,49 @@ +package com.terrabase.enterprise.impl.commercial.client; + +import com.terrabase.enterprise.api.dto.EventInfo; +import com.terrabase.enterprise.api.dto.EventsCollection; +import com.terrabase.enterprise.api.request.GetEventsParams; +import com.terrabase.enterprise.api.request.RegisterEventDefineReq; +import com.terrabase.enterprise.api.response.ResultVo; +import com.terrabase.enterprise.impl.commercial.config.FeignInnerRequestAuthInterceptor; +import com.terrabase.enterprise.impl.commercial.config.HttpsFeignClientConfig; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.cloud.openfeign.SpringQueryMap; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.List; + +@Component +@FeignClient(value = "Monitor", path = "/monitor/v1", contextId = "eDataMate-alarm", + configuration = {HttpsFeignClientConfig.class, FeignInnerRequestAuthInterceptor.class}) +public interface AlarmFeignClient { + /** + * 批量注册权限 + * + * @param req 注册请求 + * @return 注册结果 + */ + @PostMapping({"/events/defines"}) + ResultVo registerEventDefine(@RequestBody RegisterEventDefineReq req); + + /** + * 上报告警 + * + * @param alarmInfos 告警信息 + * @return 是否上报成功 + */ + @PostMapping({"/events/service/send-alarm/internal"}) + ResultVo<Boolean> sendEvents(@RequestBody List<EventInfo> alarmInfos); + + /** + * 批量查询告警信息 + * + * @param getEventsParams 查询请求 + * @return 查询结果 + */ + @GetMapping({"/events"}) + ResultVo<EventsCollection> getEventsByPage(@SpringQueryMap GetEventsParams getEventsParams); +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/CertFeignClient.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/CertFeignClient.java new file mode 100644 index 0000000..d3c14e0 --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/CertFeignClient.java @@ -0,0 +1,26 @@ +package com.terrabase.enterprise.impl.commercial.client; + +import com.terrabase.enterprise.api.dto.CertCollectInfo; +import com.terrabase.enterprise.api.response.ResultVo; +import com.terrabase.enterprise.impl.commercial.config.FeignInnerRequestAuthInterceptor; +import com.terrabase.enterprise.impl.commercial.config.HttpsFeignClientConfig; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.GetMapping; + +import java.util.List; + +@Component +@FeignClient(value = "Framework", path = "/framework/v1/certificate", contextId = "me-cert-collect", + configuration = {HttpsFeignClientConfig.class, FeignInnerRequestAuthInterceptor.class}) +public interface CertFeignClient { + /** + * 获取oms管理的所有证书信息 + * + * @return 证书信息 + */ + @GetMapping(value = "/action/cert/collect", consumes = MediaType.APPLICATION_JSON_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE) + ResultVo<List<CertCollectInfo>> listCertificateServiceList(); +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/CryptoFeighClient.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/CryptoFeighClient.java new file mode 100644 index 0000000..eba3dd3 --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/CryptoFeighClient.java @@ -0,0 +1,33 @@ +package com.terrabase.enterprise.impl.commercial.client; + +import com.terrabase.enterprise.api.dto.Ciphertext; +import com.terrabase.enterprise.api.dto.Plaintext; +import com.terrabase.enterprise.api.response.ResultVo; +import com.terrabase.enterprise.impl.commercial.config.FeignInnerRequestAuthInterceptor; +import com.terrabase.enterprise.impl.commercial.config.HttpsFeignClientConfig; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; + +@Component +@FeignClient(value = "Framework", path = "/framework/v1/crypto", contextId = "me-role", + configuration = {HttpsFeignClientConfig.class, FeignInnerRequestAuthInterceptor.class}) +public interface CryptoFeighClient { + /** + * 加密接口 数据加密,传入要加密的明文,返回加密后的id + * + * @param key 加密id + * @param plaintext 要加密的明文 + * @param username 用户名 + * @return 加密后的id + */ + @PostMapping({"/{key}/actions/encrypt/internal"}) + ResultVo<Ciphertext> encrypt(@PathVariable("key") String key, @RequestBody Plaintext plaintext, + @RequestHeader("username") String username); + + @PostMapping({"/{key}/actions/decrypt/internal"}) + ResultVo<Plaintext> decrypt(@PathVariable("key") String key, @RequestHeader("username") String username); +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/LicenseClient.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/LicenseClient.java new file mode 100644 index 0000000..d832e70 --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/LicenseClient.java @@ -0,0 +1,17 @@ +package com.terrabase.enterprise.impl.commercial.client; + +import com.terrabase.enterprise.api.dto.LicenseInfo; +import com.terrabase.enterprise.api.response.ResultVo; +import com.terrabase.enterprise.impl.commercial.config.FeignInnerRequestAuthInterceptor; +import com.terrabase.enterprise.impl.commercial.config.HttpsFeignClientConfig; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.GetMapping; + +@Component +@FeignClient(value = "Framework", path = "/framework/v1/license", contextId = "edm-license", + configuration = {HttpsFeignClientConfig.class, FeignInnerRequestAuthInterceptor.class}) +public interface LicenseClient { + @GetMapping(value = "/info") + ResultVo<LicenseInfo> getLicenseInfo(); +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/ManualAuthenticationClient.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/ManualAuthenticationClient.java new file mode 100644 index 0000000..b5f1292 --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/ManualAuthenticationClient.java @@ -0,0 +1,33 @@ +package com.terrabase.enterprise.impl.commercial.client; + +import com.terrabase.enterprise.api.dto.LoginUserDto; +import com.terrabase.enterprise.api.response.ResultVo; +import com.terrabase.enterprise.impl.commercial.config.FeignInnerRequestAuthInterceptor; +import com.terrabase.enterprise.impl.commercial.config.HttpsFeignClientConfig; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; + +import java.util.List; + +@Component +@FeignClient(value = "Framework", path = "/framework/v1", contextId = "frameworkRole", + configuration = {HttpsFeignClientConfig.class, FeignInnerRequestAuthInterceptor.class}) +public interface ManualAuthenticationClient { + /** + * 调用oms侧接口,如果鉴权成功会返回角色名 + * + * @return ResultVo<List<String>> + */ + @PostMapping(value = "/iam/roles/query-by-token") + ResultVo<List<String>> queryRolesByToken(); + + /** + * 调用oms侧接口,返回当前的用户信息 + * + * @return Result<List<LoginUserDto>> + */ + @GetMapping(value = "/sessions/current/internal") + ResultVo<List<LoginUserDto>> sessionCur(); +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/MenuFeignClient.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/MenuFeignClient.java new file mode 100644 index 0000000..93db0be --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/MenuFeignClient.java @@ -0,0 +1,23 @@ +package com.terrabase.enterprise.impl.commercial.client; + +import com.terrabase.enterprise.api.dto.MenuInfo; +import com.terrabase.enterprise.api.dto.MenuRegisterInfo; +import com.terrabase.enterprise.impl.commercial.config.FeignInnerRequestAuthInterceptor; +import com.terrabase.enterprise.impl.commercial.config.HttpsFeignClientConfig; +import com.terrabase.enterprise.api.response.ResultVo; + + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@Component +@FeignClient(value = "Framework", path = "/framework/v1/customize", contextId = "settings-menu", +configuration = {HttpsFeignClientConfig.class, FeignInnerRequestAuthInterceptor.class}) +public interface MenuFeignClient { + @PostMapping(value = "/menu/register/internal", consumes = MediaType.APPLICATION_JSON_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE) + ResultVo<MenuInfo> registerMenuInfo(@RequestBody MenuRegisterInfo menuRegisterInfo); +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/OmsExtensionClient.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/OmsExtensionClient.java new file mode 100644 index 0000000..7f2dac0 --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/OmsExtensionClient.java @@ -0,0 +1,19 @@ +package com.terrabase.enterprise.impl.commercial.client; + +import com.terrabase.enterprise.api.dto.ResourceGroup; +import com.terrabase.enterprise.impl.commercial.config.FeignInnerRequestAuthInterceptor; +import com.terrabase.enterprise.impl.commercial.config.HttpsFeignClientConfig; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +import java.util.List; + +@Component +@FeignClient(name = "oms", path = "/rpc/v1", url = "${oms-extension.api.url:https://oms-extension:8021}", + configuration = {FeignInnerRequestAuthInterceptor.class, HttpsFeignClientConfig.class}) +public interface OmsExtensionClient { + @GetMapping("/{userName}/user-resource-groups") + List<ResourceGroup> getUserGroups(@PathVariable String userName); +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/OperateLogFeignClient.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/OperateLogFeignClient.java new file mode 100644 index 0000000..6051473 --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/OperateLogFeignClient.java @@ -0,0 +1,36 @@ +package com.terrabase.enterprise.impl.commercial.client; + +import com.terrabase.enterprise.api.dto.LogI18n; +import com.terrabase.enterprise.api.request.LogAttributeVo; +import com.terrabase.enterprise.api.response.ResultVo; +import com.terrabase.enterprise.impl.commercial.config.FeignInnerRequestAuthInterceptor; +import com.terrabase.enterprise.impl.commercial.config.HttpsFeignClientConfig; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.PostMapping; + +import java.util.List; + +@Component +@FeignClient(value = "Framework", path = "/framework/v1/log/operateLogs", contextId = "eDataMate-operateLog", + configuration = {HttpsFeignClientConfig.class, FeignInnerRequestAuthInterceptor.class}) +public interface +OperateLogFeignClient { + /** + * 上报审计日志接口 + * + * @param logs log对象列表 + * @return ResultVo<日志条数> + */ + @PostMapping(value = "/actions/register/internal") + ResultVo<Integer> registerLogs(List<LogAttributeVo> logs); + + /** + * 注册审计日志国际化 + * + * @param LogI18ns 国际化对象list + * @return ResultVo<注册结果> + */ + @PostMapping(value = "/actions/register/internation/internal") + ResultVo<Boolean> registryInternational(List<LogI18n> LogI18ns); +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/PermissionFeignClient.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/PermissionFeignClient.java new file mode 100644 index 0000000..0dd82d8 --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/PermissionFeignClient.java @@ -0,0 +1,28 @@ +package com.terrabase.enterprise.impl.commercial.client; + +import com.terrabase.enterprise.api.dto.AuthorityInfo; +import com.terrabase.enterprise.api.response.ResultVo; +import com.terrabase.enterprise.impl.commercial.config.FeignInnerRequestAuthInterceptor; +import com.terrabase.enterprise.impl.commercial.config.HttpsFeignClientConfig; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.List; + +@Component +@FeignClient(value = "Framework", path = "/framework/v1/iam/permission", contextId = "eDataMate-permission", + configuration = {HttpsFeignClientConfig.class, FeignInnerRequestAuthInterceptor.class}) +public interface PermissionFeignClient { + /** + * 批量注册权限 + * + * @param authorityInfos 待注册权限实体列表 + * @return 注册结果 + */ + @PostMapping(value = "/batch/register/internal", consumes = MediaType.APPLICATION_JSON_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE) + ResultVo<String> registerPermission(@RequestBody List<AuthorityInfo> authorityInfos); +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/RoleFeignClient.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/RoleFeignClient.java new file mode 100644 index 0000000..b34e021 --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/client/RoleFeignClient.java @@ -0,0 +1,26 @@ +package com.terrabase.enterprise.impl.commercial.client; + +import com.terrabase.enterprise.api.request.RoleRegisterVo; +import com.terrabase.enterprise.api.response.ResultVo; +import com.terrabase.enterprise.impl.commercial.config.FeignInnerRequestAuthInterceptor; +import com.terrabase.enterprise.impl.commercial.config.HttpsFeignClientConfig; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@Component +@FeignClient(value = "Framework", path = "/framework/v1/iam/roles", contextId = "me-role", + configuration = {HttpsFeignClientConfig.class, FeignInnerRequestAuthInterceptor.class}) +public interface RoleFeignClient { + /** + * 创建新角色 + * + * @param roleRegisterVo 待创建新角色 + * @return 创建结果 + */ + @PostMapping(value = "/batch/register/internal", consumes = MediaType.APPLICATION_JSON_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE) + ResultVo<String> batchRegisterRole(@RequestBody RoleRegisterVo roleRegisterVo); +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/CommercialNacosConfig.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/CommercialNacosConfig.java new file mode 100644 index 0000000..7c22573 --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/CommercialNacosConfig.java @@ -0,0 +1,116 @@ +package com.terrabase.enterprise.impl.commercial.config; + +import com.alibaba.nacos.common.tls.TlsSystemConfig; +import com.terrabase.enterprise.api.sdk.TerrabaseSDKConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * 商业版Nacos配置管理类 + * 仅在商业版且启用Nacos时生效 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@Configuration +@ConditionalOnProperty( + name = "spring.cloud.nacos.discovery.enabled", + havingValue = "true", + matchIfMissing = false +) +@Import({NacosListener.class, LoadBalancerClientFactoryConfig.class}) +public class CommercialNacosConfig { + + private static final Logger logger = LoggerFactory.getLogger(CommercialNacosConfig.class); + + /** + * 根据SDK配置初始化Nacos配置 + * + * @param sdkConfig SDK配置 + */ + public static void initializeFromSDKConfig(TerrabaseSDKConfig sdkConfig) { + if (sdkConfig == null || !sdkConfig.isNacosDiscoveryEnabled()) { + logger.info("Nacos服务发现未启用,跳过Nacos配置初始化"); + return; + } + + logger.info("初始化商业版Nacos配置"); + logger.info("Nacos服务器地址: {}", sdkConfig.getNacosServerAddr()); + logger.info("Nacos用户名: {}", sdkConfig.getNacosUsername()); + logger.info("服务名称: {}", sdkConfig.getServiceName()); + logger.info("服务端口: {}", sdkConfig.getServicePort()); + + // 设置系统属性,供Spring Cloud Nacos使用 + if (sdkConfig.getNacosServerAddr() != null) { + System.setProperty("spring.cloud.nacos.discovery.server-addr", sdkConfig.getNacosServerAddr()); + } + if (sdkConfig.getNacosUsername() != null) { + System.setProperty("spring.cloud.nacos.discovery.username", sdkConfig.getNacosUsername()); + } + if (sdkConfig.getNacosPassword() != null) { + System.setProperty("spring.cloud.nacos.discovery.password", sdkConfig.getNacosPassword()); + } + if (sdkConfig.getNacosPort() != null) { + System.setProperty("spring.cloud.nacos.discovery.port", sdkConfig.getNacosPort()); + } + if (sdkConfig.getNacosIp() != null) { + System.setProperty("spring.cloud.nacos.discovery.ip", sdkConfig.getNacosIp()); + } + if (sdkConfig.getServiceName() != null) { + System.setProperty("spring.application.name", sdkConfig.getServiceName()); + } + if (sdkConfig.getServicePort() != null) { + System.setProperty("server.port", sdkConfig.getServicePort()); + } + + // 启用Nacos发现和注册 + System.setProperty("spring.cloud.nacos.discovery.enabled", "true"); + System.setProperty("spring.cloud.nacos.discovery.register-enabled", "true"); + + // 配置SSL设置 + configureSSL(sdkConfig); + + logger.info("商业版Nacos配置初始化完成"); + } + + /** + * 配置SSL设置 + * + * @param sdkConfig SDK配置 + */ + private static void configureSSL(TerrabaseSDKConfig sdkConfig) { + if (sdkConfig.isTlsEnabled()) { + logger.info("配置Nacos SSL设置"); + logger.info("TLS启用: {}", sdkConfig.isTlsEnabled()); + logger.info("客户端认证: {}", sdkConfig.isClientAuth()); + logger.info("信任证书路径: {}", sdkConfig.getTrustCertPath()); + + // 设置TLS系统属性 + System.setProperty(TlsSystemConfig.TLS_ENABLE, String.valueOf(sdkConfig.isTlsEnabled())); + System.setProperty(TlsSystemConfig.CLIENT_AUTH, String.valueOf(sdkConfig.isClientAuth())); + if (sdkConfig.getTrustCertPath() != null) { + System.setProperty(TlsSystemConfig.CLIENT_TRUST_CERT, sdkConfig.getTrustCertPath()); + } + + logger.info("Nacos SSL配置完成"); + } else { + logger.info("Nacos SSL未启用"); + } + } + + /** + * 检查是否为商业版Nacos配置 + * + * @param sdkConfig SDK配置 + * @return 是否为商业版Nacos配置 + */ + public static boolean isCommercialNacosConfig(TerrabaseSDKConfig sdkConfig) { + return sdkConfig != null && + sdkConfig.isNacosDiscoveryEnabled() && + sdkConfig.getNacosServerAddr() != null && + !sdkConfig.getNacosServerAddr().trim().isEmpty(); + } +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/FeignInnerRequestAuthInterceptor.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/FeignInnerRequestAuthInterceptor.java new file mode 100644 index 0000000..6c9a2dd --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/FeignInnerRequestAuthInterceptor.java @@ -0,0 +1,75 @@ +package com.terrabase.enterprise.impl.commercial.config; + +import feign.RequestInterceptor; +import feign.RequestTemplate; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * FeignClient内部调用前置拦截器 + * 在请求中添加机器令牌,并支持重试机制 + */ +@Slf4j +@Component +public class FeignInnerRequestAuthInterceptor implements RequestInterceptor { + private static final String X_AUTH_TOKEN_NAME = "X-Auth-Token-Inner"; + + @Autowired + private TokenRetryConfig tokenRetryConfig; + + @Override + public void apply(RequestTemplate requestTemplate) { + String token = getTokenWithRetry(); + if (StringUtils.isBlank(token)) { + log.warn("Failed to get machine token, using empty token for inner request"); + token = StringUtils.EMPTY; + } + requestTemplate.header(X_AUTH_TOKEN_NAME, token); + + if (tokenRetryConfig.isVerboseLogging()) { + log.debug("Added token to request: {} -> {}", requestTemplate.url(), + token.length() > 10 ? token.substring(0, 10) + "..." : token); + } + } + + /** + * 获取 token,支持重试机制 + */ + private String getTokenWithRetry() { + int retryCount = 0; + String token = null; + + while (retryCount <= tokenRetryConfig.getMaxRetries()) { + try { + if (retryCount > 0) { + log.info("重试获取 token,第 {} 次尝试", retryCount); + if (tokenRetryConfig.isRefreshTokenBeforeRetry()) { + MachineTokenConfig.refreshMachineToken(); + } + Thread.sleep(tokenRetryConfig.getRetryInterval()); + } + + token = MachineTokenConfig.getMachineToken(); + if (StringUtils.isNotBlank(token)) { + if (retryCount > 0) { + log.info("Token 获取成功,重试 {} 次后成功", retryCount); + } + break; + } + + retryCount++; + } catch (Exception e) { + log.error("获取 token 时发生异常,第 {} 次尝试", retryCount, e); + retryCount++; + } + } + + if (StringUtils.isBlank(token)) { + log.error("经过 {} 次重试后仍无法获取 token", tokenRetryConfig.getMaxRetries()); + } + + return token; + } +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/HttpsFeignClientConfig.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/HttpsFeignClientConfig.java new file mode 100644 index 0000000..2c1a809 --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/HttpsFeignClientConfig.java @@ -0,0 +1,84 @@ +package com.terrabase.enterprise.impl.commercial.config; + +import feign.Client; +import feign.codec.ErrorDecoder; +import feign.Retryer; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; +import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; +import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.Collections; + +/** + * Feign配置 + * 支持 HTTPS、负载均衡和 Token 重试机制 + */ +@Configuration +@ConditionalOnClass(LoadBalancerClientFactory.class) +public class HttpsFeignClientConfig { + + @Autowired(required = false) + private LoadBalancerClient loadBalancerClient; + + @Autowired + private TokenRetryErrorDecoder tokenRetryErrorDecoder; + + @Autowired + private TokenRetryer tokenRetryer; + + @Bean + public Client feignClient(LoadBalancerClientFactory loadBalancerClientFactory) + throws NoSuchAlgorithmException, KeyManagementException { + SSLContext instance = SSLContext.getInstance("TLSv1.2"); + instance.init(null, new TrustManager[]{getX509TrustManager()}, SecureRandom.getInstanceStrong()); + Client.Default client = new Client.Default(instance.getSocketFactory(), (s, sslSession) -> true); + + // 如果 LoadBalancerClient 可用,使用负载均衡客户端 + if (loadBalancerClient != null) { + return new FeignBlockingLoadBalancerClient(client, loadBalancerClient, loadBalancerClientFactory, + Collections.emptyList()); + } else { + // 否则返回普通的 HTTPS 客户端 + return client; + } + } + + @Bean + public ErrorDecoder errorDecoder() { + return tokenRetryErrorDecoder; + } + + @Bean + public Retryer retryer() { + return tokenRetryer; + } + + private static X509TrustManager getX509TrustManager() { + return new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String authType) { + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String authType) { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[] {}; + } + }; + } +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/LoadBalancerClientFactoryConfig.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/LoadBalancerClientFactoryConfig.java new file mode 100644 index 0000000..2f700c5 --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/LoadBalancerClientFactoryConfig.java @@ -0,0 +1,101 @@ +package com.terrabase.enterprise.impl.commercial.config; + + +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.cloud.nacos.loadbalancer.LoadBalancerAlgorithm; +import com.alibaba.cloud.nacos.loadbalancer.DefaultLoadBalancerAlgorithm; +import com.alibaba.cloud.nacos.loadbalancer.NacosLoadBalancer; +import com.alibaba.cloud.nacos.util.InetIPv6Utils; + +import jakarta.annotation.Resource; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.LoadBalancerClientsProperties; +import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer; +import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; +import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.GenericApplicationContext; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * LoadBalancerClientFactory配置类 + * 规避Nacos在使用FeignClient客户端下无法正确获取Nacos实例地址的问题 + * + * @author Yehong Pan + * @since 2025-10-10 + */ +@Configuration +@EnableConfigurationProperties(LoadBalancerClientsProperties.class) +public class +LoadBalancerClientFactoryConfig { + @Resource + private NacosDiscoveryProperties discoveryProperties; + + @Resource + private InetIPv6Utils inetIPv6Utils; + + /** + * 获取LoadBalancerClientFactory对象 + * @param properties properties + * @return LoadBalancerClientFacotry对象 + */ + @Bean + @ConditionalOnMissingBean + public LoadBalancerClientFactory loadBalancerClientFactory(LoadBalancerClientsProperties properties) { + return new CubeLoadBalancerClientFactory(properties, discoveryProperties, inetIPv6Utils); + } + + private static class CubeLoadBalancerClientFactory extends LoadBalancerClientFactory { + private static final Map<String, ReactiveLoadBalancer<ServiceInstance>> SERVICE_INSTANCE_MAP = + new ConcurrentHashMap<>(); + + private final NacosDiscoveryProperties discoveryProperties; + + private final InetIPv6Utils inetIPv6Utils; + + /** + * 构造函数 + * + * @param properties springcloud负载均衡环境参数 + * @param discoveryProperties nacos环境配置参数 + * @param inetIPv6Utils 工具 + */ + public CubeLoadBalancerClientFactory(LoadBalancerClientsProperties properties, + NacosDiscoveryProperties discoveryProperties, + InetIPv6Utils inetIPv6Utils) { + super(properties); + this.discoveryProperties = discoveryProperties; + this.inetIPv6Utils = inetIPv6Utils; + } + + @Override + public GenericApplicationContext createContext(String name) { + ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); + GenericApplicationContext context = super.createContext(name); + Thread.currentThread().setContextClassLoader(originalClassLoader); + return context; + } + + @Override + public ReactiveLoadBalancer<ServiceInstance> getInstance(String serviceId) { + Map<String, LoadBalancerAlgorithm> loadBalancerAlgorithmMap = new HashMap<>(); + loadBalancerAlgorithmMap.put("defaultServiceId", new DefaultLoadBalancerAlgorithm()); + return SERVICE_INSTANCE_MAP.computeIfAbsent(serviceId, key-> { + NacosLoadBalancer nacosLoadBalancer = new NacosLoadBalancer( + getLazyProvider(serviceId, ServiceInstanceListSupplier.class), serviceId, discoveryProperties, + inetIPv6Utils, new ArrayList<>(), loadBalancerAlgorithmMap); + nacosLoadBalancer.init(); + return nacosLoadBalancer; + }); + } + } + +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/MachineTokenConfig.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/MachineTokenConfig.java new file mode 100644 index 0000000..9545f54 --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/MachineTokenConfig.java @@ -0,0 +1,122 @@ +package com.terrabase.enterprise.impl.commercial.config; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Configuration; + +import java.io.BufferedReader; +import java.io.InputStreamReader; + +/** + * 机器令牌配置类,提供令牌的获取和刷新功能。 + * 通过静态方法管理机器令牌,其他类可直接调用获取或刷新令牌。 + * + * 采用直接执行命令的方式获取和解密机器令牌,无需依赖外部脚本文件。 + */ +@Configuration +public class MachineTokenConfig { + + private static final Logger logger = LoggerFactory.getLogger(MachineTokenConfig.class); + + /** + * 存储解密后的机器令牌 + */ + private static volatile String MACHINE_TOKEN_DECRYPTED = null; + + /** + * 获取机器令牌,如果令牌为空则自动调用脚本获取 + * @return 解密后的机器令牌,如果获取失败返回null + */ + public static String getMachineToken() { + if (MACHINE_TOKEN_DECRYPTED == null || MACHINE_TOKEN_DECRYPTED.isEmpty()) { + refreshMachineToken(); + } + return MACHINE_TOKEN_DECRYPTED; + } + + /** + * 刷新机器令牌,强制重新调用脚本获取新令牌 + * @return 是否成功获取到新令牌 + */ + public static synchronized boolean refreshMachineToken() { + String token = getTokenWithFallback(); + if (token != null && !token.isEmpty()) { + MACHINE_TOKEN_DECRYPTED = token; + return true; + } + return false; + } + + /** + * 从脚本获取机器令牌(简化版本) + * 直接执行命令获取加密令牌,然后通过Python解密 + * @return 解密后的机器令牌,失败时返回null + */ + private static String getTokenFromScript() { + try { + logger.info("开始执行机器令牌获取脚本..."); + + // 执行脚本获取加密令牌 + ProcessBuilder pb = new ProcessBuilder("bash", "-c", + "grep 'machine_token=' /opt/huawei/fce/runtime/security/priv/platform.conf | awk -F'=' '{print $2}'"); + Process process = pb.start(); + + BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String encryptedToken = reader.readLine(); + reader.close(); + + if (encryptedToken == null || encryptedToken.trim().isEmpty()) { + logger.warn("无法获取加密机器令牌"); + return null; + } + + logger.info("成功获取加密机器令牌,开始解密..."); + + // 执行Python脚本解密 + ProcessBuilder pythonPb = new ProcessBuilder("python", "-c", + "import kmc.kmc as K; import os; os.environ['KMC_DATA_USER']='tomcat'; machine_token='" + + encryptedToken.trim() + "'; plain_machine_token=K.API().decrypt(0,machine_token); print(plain_machine_token)"); + Process pythonProcess = pythonPb.start(); + + BufferedReader pythonReader = new BufferedReader(new InputStreamReader(pythonProcess.getInputStream())); + String decryptedToken = pythonReader.readLine(); + pythonReader.close(); + + if (decryptedToken == null || decryptedToken.trim().isEmpty()) { + logger.warn("机器令牌解密失败"); + return null; + } + + logger.info("机器令牌获取成功"); + return decryptedToken.trim(); + + } catch (Exception e) { + logger.error("机器令牌获取脚本执行失败", e); + return null; + } + } + + /** + * 获取机器令牌(带重试机制) + * 如果脚本获取失败,会尝试从环境变量获取 + * @return 机器令牌 + */ + public static String getTokenWithFallback() { + // 首先尝试从脚本获取 + String token = getTokenFromScript(); + + // 如果脚本获取失败,尝试从环境变量获取 + if (token == null || token.isEmpty()) { + String envToken = System.getenv("MACHINE_TOKEN"); + if (envToken != null && !envToken.trim().isEmpty()) { + logger.info("从环境变量获取机器令牌"); + return envToken; + } + } + + return token; + } + +} + + diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/NacosListener.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/NacosListener.java new file mode 100644 index 0000000..e236a8c --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/NacosListener.java @@ -0,0 +1,34 @@ +package com.terrabase.enterprise.impl.commercial.config; + +import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled; +import com.alibaba.cloud.nacos.registry.NacosAutoServiceRegistration; +import com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Configuration; + + +/** + * Nacos控制器 + */ +@Configuration +@ConditionalOnNacosDiscoveryEnabled +@AutoConfigureAfter(NacosServiceRegistryAutoConfiguration.class) +public class NacosListener implements ApplicationContextAware { + private NacosAutoServiceRegistration registration; + + @Autowired(required = false) + public void setRegistration(NacosAutoServiceRegistration registration) { + this.registration = registration; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + if (registration != null) { + registration.start(); + } + } +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/TokenRetryConfig.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/TokenRetryConfig.java new file mode 100644 index 0000000..9faed10 --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/TokenRetryConfig.java @@ -0,0 +1,91 @@ +package com.terrabase.enterprise.impl.commercial.config; + +import org.springframework.context.annotation.Configuration; + +/** + * Token 重试配置 + * 用于配置 Feign 客户端在遇到 token 失效时的重试策略 + * 配置值直接硬编码在类中,无需外部配置文件 + */ +@Configuration +public class TokenRetryConfig { + + /** + * 是否启用 token 重试机制 + */ + private boolean enabled = true; + + /** + * 最大重试次数 + */ + private int maxRetries = 2; + + /** + * 重试间隔(毫秒) + */ + private long retryInterval = 1000; + + /** + * 是否在重试前刷新 token + */ + private boolean refreshTokenBeforeRetry = true; + + /** + * 无效 token 的错误消息关键词 + */ + private String invalidTokenMessage = "invalid token"; + + /** + * 是否启用详细日志 + */ + private boolean verboseLogging = true; + + // Getters and Setters + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public int getMaxRetries() { + return maxRetries; + } + + public void setMaxRetries(int maxRetries) { + this.maxRetries = maxRetries; + } + + public long getRetryInterval() { + return retryInterval; + } + + public void setRetryInterval(long retryInterval) { + this.retryInterval = retryInterval; + } + + public boolean isRefreshTokenBeforeRetry() { + return refreshTokenBeforeRetry; + } + + public void setRefreshTokenBeforeRetry(boolean refreshTokenBeforeRetry) { + this.refreshTokenBeforeRetry = refreshTokenBeforeRetry; + } + + public String getInvalidTokenMessage() { + return invalidTokenMessage; + } + + public void setInvalidTokenMessage(String invalidTokenMessage) { + this.invalidTokenMessage = invalidTokenMessage; + } + + public boolean isVerboseLogging() { + return verboseLogging; + } + + public void setVerboseLogging(boolean verboseLogging) { + this.verboseLogging = verboseLogging; + } +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/TokenRetryErrorDecoder.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/TokenRetryErrorDecoder.java new file mode 100644 index 0000000..89c674e --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/TokenRetryErrorDecoder.java @@ -0,0 +1,93 @@ +package com.terrabase.enterprise.impl.commercial.config; + +import com.terrabase.enterprise.api.response.ResultVo; +import feign.Response; +import feign.Util; +import feign.codec.ErrorDecoder; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +/** + * Token 重试错误解码器 + * 检测 "invalid token" 错误并抛出特殊异常以触发重试机制 + */ +@Slf4j +@Component +public class TokenRetryErrorDecoder implements ErrorDecoder { + + @Autowired + private TokenRetryConfig tokenRetryConfig; + + private final ErrorDecoder defaultErrorDecoder = new Default(); + + @Override + public Exception decode(String methodKey, Response response) { + // 如果重试机制未启用,使用默认错误解码器 + if (!tokenRetryConfig.isEnabled()) { + return defaultErrorDecoder.decode(methodKey, response); + } + + // 检查是否是 token 相关错误 + if (isTokenError(response)) { + log.warn("检测到 token 错误,响应状态: {}, 方法: {}", response.status(), methodKey); + return new TokenRetryException("Token 验证失败,需要重试", response.status()); + } + + // 其他错误使用默认处理 + return defaultErrorDecoder.decode(methodKey, response); + } + + /** + * 检查是否是 token 相关错误 + */ + private boolean isTokenError(Response response) { + try { + // 检查状态码 + if (response.status() == 401 || response.status() == 403) { + return true; + } + + // 检查响应体中的错误消息 + if (response.body() != null) { + String body = Util.toString(response.body().asReader(StandardCharsets.UTF_8)); + if (body != null && body.toLowerCase().contains(tokenRetryConfig.getInvalidTokenMessage().toLowerCase())) { + return true; + } + + // 尝试解析 ResultVo 格式的响应 + try { + // 简单的字符串匹配,避免依赖 Jackson + if (body.contains("\"msg\"") && body.contains(tokenRetryConfig.getInvalidTokenMessage())) { + return true; + } + } catch (Exception e) { + // 忽略解析错误 + } + } + } catch (IOException e) { + log.warn("读取响应体失败", e); + } + + return false; + } + + /** + * Token 重试异常 + */ + public static class TokenRetryException extends RuntimeException { + private final int status; + + public TokenRetryException(String message, int status) { + super(message); + this.status = status; + } + + public int getStatus() { + return status; + } + } +} diff --git a/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/TokenRetryer.java b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/TokenRetryer.java new file mode 100644 index 0000000..b93e47f --- /dev/null +++ b/enterprise-impl-commercial/src/main/java/com/terrabase/enterprise/impl/commercial/config/TokenRetryer.java @@ -0,0 +1,97 @@ +package com.terrabase.enterprise.impl.commercial.config; + +import feign.RetryableException; +import feign.Retryer; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Token 重试器 + * 专门处理 token 相关的重试逻辑 + */ +@Slf4j +@Component +public class TokenRetryer implements Retryer { + + @Autowired + private TokenRetryConfig tokenRetryConfig; + + private int attempt = 1; + private final long period; + private final long maxPeriod; + private final int maxAttempts; + + public TokenRetryer() { + this(1000L, 5000L, 3); + } + + public TokenRetryer(long period, long maxPeriod, int maxAttempts) { + this.period = period; + this.maxPeriod = maxPeriod; + this.maxAttempts = maxAttempts; + } + + @Override + public void continueOrPropagate(RetryableException e) { + // 检查是否是 token 重试异常 + if (e.getCause() instanceof TokenRetryErrorDecoder.TokenRetryException) { + TokenRetryErrorDecoder.TokenRetryException tokenException = + (TokenRetryErrorDecoder.TokenRetryException) e.getCause(); + + if (attempt++ >= maxAttempts) { + log.error("Token 重试次数已达上限 ({}), 放弃重试", maxAttempts); + throw e; + } + + log.warn("Token 验证失败,准备重试 (第 {} 次)", attempt - 1); + + // 刷新 token + if (tokenRetryConfig.isRefreshTokenBeforeRetry()) { + boolean refreshSuccess = MachineTokenConfig.refreshMachineToken(); + if (refreshSuccess) { + log.info("Token 刷新成功,准备重试请求"); + } else { + log.warn("Token 刷新失败,但仍将重试"); + } + } + + // 计算重试间隔 + long sleepTime = period * attempt; + if (sleepTime > maxPeriod) { + sleepTime = maxPeriod; + } + + try { + Thread.sleep(sleepTime); + } catch (InterruptedException interrupted) { + Thread.currentThread().interrupt(); + throw e; + } + + return; + } + + // 其他类型的异常,使用默认重试逻辑 + if (attempt++ >= maxAttempts) { + throw e; + } + + long sleepTime = period * attempt; + if (sleepTime > maxPeriod) { + sleepTime = maxPeriod; + } + + try { + Thread.sleep(sleepTime); + } catch (InterruptedException interrupted) { + Thread.currentThread().interrupt(); + throw e; + } + } + + @Override + public Retryer clone() { + return new TokenRetryer(period, maxPeriod, maxAttempts); + } +} diff --git a/enterprise-impl-open/KMC_README.md b/enterprise-impl-open/KMC_README.md new file mode 100644 index 0000000..6babaa4 --- /dev/null +++ b/enterprise-impl-open/KMC_README.md @@ -0,0 +1,234 @@ +# Terrabase 开源版 KMC 加解密功能 + +## 概述 + +本文档介绍了 Terrabase 开源版企业服务中集成的 KMC (Key Management Center) 加解密功能。该功能基于开源算法实现,使用 AES-GCM 加密模式,提供安全可靠的数据加解密服务。 + +## 功能特性 + +### 核心功能 +- **AES-GCM 加密**: 使用 256 位密钥的 AES-GCM 模式进行加解密 +- **随机 IV 生成**: 每次加密都生成随机的初始化向量,确保密文唯一性 +- **密钥管理**: 支持多密钥管理,包括默认密钥和自定义密钥 +- **安全存储**: 密钥在内存中安全缓存,支持动态管理 + +### 安全特性 +- **认证加密**: GCM 模式提供数据完整性和真实性验证 +- **密钥隔离**: 不同密钥的密文无法相互解密 +- **随机性**: 使用安全随机数生成器确保加密的随机性 +- **审计日志**: 可选的加解密操作审计记录 + +## 技术实现 + +### 依赖库 +- **BouncyCastle**: 提供加密算法实现 +- **Apache Commons Codec**: Base64 编码/解码 +- **Spring Boot**: 配置管理和依赖注入 + +### 加密算法 +- **算法**: AES (Advanced Encryption Standard) +- **模式**: GCM (Galois/Counter Mode) +- **密钥长度**: 256 位 +- **IV 长度**: 96 位 (12 字节) +- **标签长度**: 128 位 (16 字节) + +## 配置说明 + +### application.properties 配置 + +```properties +# KMC 基本配置 +terrabase.kmc.enabled=true # 启用 KMC 功能 +terrabase.kmc.default-key-id=default # 默认密钥 ID +terrabase.kmc.algorithm=AES # 加密算法 +terrabase.kmc.key-length=256 # 密钥长度(位) +terrabase.kmc.transformation=AES/GCM/NoPadding # 转换模式 + +# GCM 参数配置 +terrabase.kmc.gcm-iv-length=12 # GCM IV 长度(字节) +terrabase.kmc.gcm-tag-length=16 # GCM 标签长度(字节) + +# 缓存配置 +terrabase.kmc.key-caching=true # 启用密钥缓存 +terrabase.kmc.max-cache-size=100 # 最大缓存密钥数量 +terrabase.kmc.key-expiration-time=86400000 # 密钥过期时间(毫秒) + +# 高级功能 +terrabase.kmc.enable-key-rotation=false # 启用密钥轮换 +terrabase.kmc.key-rotation-interval=604800000 # 密钥轮换间隔(毫秒) +terrabase.kmc.enable-audit-log=true # 启用审计日志 +``` + +## 使用方法 + +### 1. 基本加解密 + +```java +// 获取企业服务实例 +@Autowired +private EnterpriseService enterpriseService; + +// 加密数据 +String plaintext = "需要加密的敏感数据"; +String encryptResult = enterpriseService.encrypt(plaintext); +// 返回格式: "开源版KMC加密成功: <Base64编码的密文>" + +// 解密数据 +String ciphertext = "<从加密结果中提取的Base64密文>"; +String decryptResult = enterpriseService.decrypt(ciphertext); +// 返回格式: "开源版KMC解密成功: <原始明文>" +``` + +### 2. 直接使用工具类 + +```java +import com.terrabase.enterprise.impl.open.util.KmcCryptoUtil; + +// 生成密钥 +String keyId = "my_custom_key"; +KmcCryptoUtil.generateKey(keyId); + +// 使用指定密钥加密 +String ciphertext = KmcCryptoUtil.encrypt(plaintext, keyId); + +// 使用指定密钥解密 +String decryptedText = KmcCryptoUtil.decrypt(ciphertext, keyId); + +// 使用默认密钥 +String defaultCiphertext = KmcCryptoUtil.encrypt(plaintext); +String defaultDecryptedText = KmcCryptoUtil.decrypt(defaultCiphertext); +``` + +### 3. 密钥管理 + +```java +// 检查密钥是否存在 +boolean exists = KmcCryptoUtil.hasKey("my_key"); + +// 获取密钥信息 +String keyInfo = KmcCryptoUtil.getKeyInfo("my_key"); + +// 删除指定密钥 +boolean removed = KmcCryptoUtil.removeKey("my_key"); + +// 获取当前密钥数量 +int keyCount = KmcCryptoUtil.getKeyCount(); + +// 清空所有密钥 +KmcCryptoUtil.clearAllKeys(); +``` + +## API 接口 + +### REST API 端点 + +通过企业服务的 REST API 可以使用 KMC 功能: + +```bash +# 加密数据 +curl -X POST http://localhost:8080/api/enterprise/encrypt \ + -H "Content-Type: application/json" \ + -d '{"data": "需要加密的数据"}' + +# 解密数据 +curl -X POST http://localhost:8080/api/enterprise/decrypt \ + -H "Content-Type: application/json" \ + -d '{"data": "Base64编码的密文"}' +``` + +## 安全注意事项 + +### 1. 密钥管理 +- 密钥仅在内存中存储,应用重启后需要重新生成 +- 生产环境建议使用外部密钥管理系统 +- 定期轮换密钥以提高安全性 + +### 2. 数据保护 +- 密文使用 Base64 编码,便于传输和存储 +- 每次加密都使用随机 IV,确保相同明文产生不同密文 +- GCM 模式提供数据完整性验证 + +### 3. 性能考虑 +- 密钥缓存可提高性能,但需注意内存使用 +- 长文本加密可能影响性能,建议分批处理 +- 审计日志可能影响性能,生产环境可考虑异步记录 + +## 测试 + +### 运行单元测试 + +```bash +# 运行 KMC 工具类测试 +mvn test -Dtest=KmcCryptoUtilTest + +# 运行集成测试 +mvn test -Dtest=OpenEnterpriseServiceImplKmcTest + +# 运行所有测试 +mvn test +``` + +### 测试覆盖范围 + +- 基本加解密功能 +- 密钥管理功能 +- 错误处理 +- 边界条件测试 +- 性能测试 +- 安全测试 + +## 故障排除 + +### 常见问题 + +1. **加密失败** + - 检查 KMC 功能是否启用 + - 验证服务是否正常运行 + - 检查输入数据是否为空 + +2. **解密失败** + - 验证密文格式是否正确 + - 检查密钥是否存在 + - 确认使用相同的密钥进行解密 + +3. **性能问题** + - 检查密钥缓存配置 + - 考虑禁用审计日志 + - 优化长文本处理 + +### 日志分析 + +启用 DEBUG 日志级别可以查看详细的加解密过程: + +```properties +logging.level.com.terrabase.enterprise.impl.open.util=DEBUG +logging.level.com.terrabase.enterprise.impl.open.config=DEBUG +``` + +## 扩展开发 + +### 添加新的加密算法 + +1. 在 `KmcCryptoUtil` 中添加新的算法支持 +2. 更新 `KmcConfig` 配置类 +3. 添加相应的测试用例 +4. 更新文档 + +### 集成外部密钥管理 + +1. 实现密钥提供者接口 +2. 修改密钥获取逻辑 +3. 添加密钥同步机制 +4. 更新配置管理 + +## 版本历史 + +- **v1.0.0**: 初始版本,支持基本的 AES-GCM 加解密功能 + +## 许可证 + +本功能基于开源许可证,遵循项目的整体许可证条款。 + +## 支持 + +如有问题或建议,请联系 Terrabase 开发团队。 diff --git a/enterprise-impl-open/pom.xml b/enterprise-impl-open/pom.xml index c96f1ee..e3cfc50 100644 --- a/enterprise-impl-open/pom.xml +++ b/enterprise-impl-open/pom.xml @@ -88,6 +88,33 @@ <version>1.10.0</version> </dependency> + <!-- 加密相关依赖 --> + <dependency> + <groupId>org.bouncycastle</groupId> + <artifactId>bcprov-jdk15on</artifactId> + <version>1.70</version> + </dependency> + + <dependency> + <groupId>org.bouncycastle</groupId> + <artifactId>bcpkix-jdk15on</artifactId> + <version>1.70</version> + </dependency> + + <!-- Base64编码 --> + <dependency> + <groupId>commons-codec</groupId> + <artifactId>commons-codec</artifactId> + <version>1.15</version> + </dependency> + + <!-- Java注解支持 --> + <dependency> + <groupId>javax.annotation</groupId> + <artifactId>javax.annotation-api</artifactId> + <version>1.3.2</version> + </dependency> + <!-- 测试依赖 --> <dependency> <groupId>org.springframework.boot</groupId> @@ -119,6 +146,17 @@ <target>17</target> </configuration> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.1.2</version> + <configuration> + <includes> + <include>**/*Test.java</include> + <include>**/*Tests.java</include> + </includes> + </configuration> + </plugin> </plugins> </build> </project> \ No newline at end of file diff --git a/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/OpenCertificateServiceImpl.java b/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/OpenCertificateServiceImpl.java new file mode 100644 index 0000000..1ceb8f6 --- /dev/null +++ b/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/OpenCertificateServiceImpl.java @@ -0,0 +1,93 @@ +package com.terrabase.enterprise.impl.open; + +import com.terrabase.enterprise.api.CertificateService; +import com.terrabase.enterprise.api.dto.*; +import com.terrabase.enterprise.api.response.ResultVo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 开源版证书管理服务实现 + * 基于开源技术实现证书管理功能 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@Service +public class OpenCertificateServiceImpl implements CertificateService { + + private static final Logger logger = LoggerFactory.getLogger(OpenCertificateServiceImpl.class); + + // ========== 证书管理相关接口实现 ========== + + @Override + public ResultVo<List<CertCollectInfo>> listCertificateServiceList() { + try { + logger.info("开源版执行证书信息查询"); + + // 返回模拟数据 + List<CertCollectInfo> certificates = new java.util.ArrayList<>(); + + // 创建数据使能证书 + CertCollectInfo cert1 = new CertCollectInfo(); + cert1.setProductName("数据使能平台"); + cert1.setIssueTime(System.currentTimeMillis() - 86400000L); // 1天前 + cert1.setExpirationTime(System.currentTimeMillis() + 86400000L * 365); // 1年后 + cert1.setIssuer("Terrabase CA"); + cert1.setSubject("CN=terrabase-data-enable"); + cert1.setSerialNumber("1234567890ABCDEF"); + cert1.setCertType("SSL"); + cert1.setStatus("有效"); + cert1.setAlertBeforeExpirationDays(30); + cert1.setCertName("数据使能证书"); + cert1.setProductVersion("1.0.0"); + cert1.setPatchVersion("1.0.1"); + cert1.setDeviceEsn("DEVICE-001"); + certificates.add(cert1); + + logger.info("开源版证书信息查询成功,查询到证书数量: {}", certificates.size()); + return ResultVo.success(certificates); + + } catch (Exception e) { + logger.error("开源版证书信息查询失败", e); + return ResultVo.error("500", "证书信息查询失败: " + e.getMessage()); + } + } + + @Override + public ResultVo<LicenseInfo> getLicenseInfo() { + try { + logger.info("开源版执行License信息查询"); + + // 返回模拟数据 + LicenseInfo licenseInfo = new LicenseInfo(); + + // 设置许可证状态:2表示已经激活 + licenseInfo.setStatus("2"); + + // 设置SBOM信息 + List<LicenseInfoEx> sboms = new java.util.ArrayList<>(); + + // 添加数据使能模块信息 + LicenseInfoEx dataEnable = new LicenseInfoEx(); + dataEnable.setName("数据使能"); + dataEnable.setTotal("1000"); + dataEnable.setUnit("用户"); + sboms.add(dataEnable); + + licenseInfo.setSboms(sboms); + + logger.info("开源版License信息查询成功 - 状态: {}, SBOM模块数量: {}", + licenseInfo.getStatus(), licenseInfo.getSboms().size()); + + return ResultVo.success(licenseInfo); + + } catch (Exception e) { + logger.error("开源版License信息查询失败", e); + return ResultVo.error("500", "License信息查询失败: " + e.getMessage()); + } + } +} diff --git a/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/OpenCryptoServiceImpl.java b/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/OpenCryptoServiceImpl.java new file mode 100644 index 0000000..c97deaf --- /dev/null +++ b/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/OpenCryptoServiceImpl.java @@ -0,0 +1,153 @@ +package com.terrabase.enterprise.impl.open; + +import com.terrabase.enterprise.api.CryptoService; +import com.terrabase.enterprise.api.CryptoAlgorithm; +import com.terrabase.enterprise.impl.open.config.KmcConfig; +import com.terrabase.enterprise.impl.open.util.KmcCryptoUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + + +/** + * 开源版加解密服务实现 + * 基于开源KMC技术实现 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@Service +public class OpenCryptoServiceImpl implements CryptoService { + + private static final Logger logger = LoggerFactory.getLogger(OpenCryptoServiceImpl.class); + + @Autowired(required = false) + private KmcConfig kmcConfig; + + /** + * 默认构造函数,用于非Spring环境下的实例化 + */ + public OpenCryptoServiceImpl() { + // 在非Spring环境下,自动创建KmcConfig实例 + this.kmcConfig = createDefaultKmcConfig(); + if (this.kmcConfig != null) { + logger.info("OpenCryptoServiceImpl 已在非Spring环境下成功初始化,KmcConfig自动创建完成"); + } else { + logger.warn("OpenCryptoServiceImpl 初始化失败,KmcConfig创建失败"); + } + } + + /** + * 创建默认的KmcConfig实例 + * @return KmcConfig实例 + */ + private KmcConfig createDefaultKmcConfig() { + try { + KmcConfig config = new KmcConfig(); + // 设置默认配置 + config.setEnabled(true); + config.setDefaultKeyId("sdk_default"); + config.setKeyCaching(true); + config.setMaxCacheSize(100); + config.setKeyExpirationTime(24 * 60 * 60 * 1000); // 24小时 + config.setEnableKeyRotation(false); + config.setKeyRotationInterval(7 * 24 * 60 * 60 * 1000); // 7天 + config.setEnableAuditLog(true); + + // 手动调用初始化方法(非Spring环境) + config.initialize(); + + return config; + } catch (Exception e) { + logger.error("创建默认KmcConfig失败", e); + return null; + } + } + + @Override + public String encrypt(String plaintext, CryptoAlgorithm algorithm, String username) { + + if (kmcConfig == null) { + logger.warn("KmcConfig未初始化,尝试重新创建"); + kmcConfig = createDefaultKmcConfig(); + if (kmcConfig == null) { + return "KMC配置初始化失败,无法执行加密操作"; + } + } + + if (!kmcConfig.isEnabled()) { + return "KMC功能未启用,无法执行加密操作"; + } + + if (plaintext == null || plaintext.trim().isEmpty()) { + return "明文数据不能为空"; + } + + if (algorithm == null) { + algorithm = CryptoAlgorithm.AES; // 默认使用AES + } + + try { + logger.info("开源版执行数据加密,原文长度: {}, 算法: {}, 用户: {}", plaintext.length(), algorithm.getAlgorithm(), username); + + // 使用KMC工具进行加密 + String ciphertext = KmcCryptoUtil.encrypt(plaintext, kmcConfig.getDefaultKeyId(), algorithm); + + // 记录审计日志 + if (kmcConfig.isEnableAuditLog()) { + logger.info("KMC加密审计 - 密钥ID: {}, 算法: {}, 原文长度: {}, 密文长度: {}", + kmcConfig.getDefaultKeyId(), algorithm.getAlgorithm(), plaintext.length(), ciphertext.length()); + } + + return ciphertext; + + } catch (Exception e) { + logger.error("开源版KMC加密失败,算法: {}", algorithm, e); + return "KMC加密失败: " + e.getMessage(); + } + } + + @Override + public String decrypt(String ciphertext, CryptoAlgorithm algorithm, String username) { + + if (kmcConfig == null) { + logger.warn("KmcConfig未初始化,尝试重新创建"); + kmcConfig = createDefaultKmcConfig(); + if (kmcConfig == null) { + return "KMC配置初始化失败,无法执行解密操作"; + } + } + + if (!kmcConfig.isEnabled()) { + return "KMC功能未启用,无法执行解密操作"; + } + + if (ciphertext == null || ciphertext.trim().isEmpty()) { + return "密文数据不能为空"; + } + + if (algorithm == null) { + algorithm = CryptoAlgorithm.AES; // 默认使用AES + } + + try { + logger.info("开源版执行数据解密,密文长度: {}, 算法: {}, 用户: {}", ciphertext.length(), algorithm.getAlgorithm(), username); + + // 使用KMC工具进行解密 + String plaintext = KmcCryptoUtil.decrypt(ciphertext, kmcConfig.getDefaultKeyId(), algorithm); + + // 记录审计日志 + if (kmcConfig.isEnableAuditLog()) { + logger.info("KMC解密审计 - 密钥ID: {}, 算法: {}, 密文长度: {}, 原文长度: {}", + kmcConfig.getDefaultKeyId(), algorithm.getAlgorithm(), ciphertext.length(), plaintext.length()); + } + + return plaintext; + + } catch (Exception e) { + logger.error("开源版KMC解密失败,算法: {}", algorithm, e); + return "KMC解密失败: " + e.getMessage(); + } + } +} diff --git a/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/OpenLogServiceImpl.java b/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/OpenLogServiceImpl.java new file mode 100644 index 0000000..b68f260 --- /dev/null +++ b/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/OpenLogServiceImpl.java @@ -0,0 +1,76 @@ +package com.terrabase.enterprise.impl.open; + +import com.terrabase.enterprise.api.LogService; +import com.terrabase.enterprise.api.dto.LogI18n; +import com.terrabase.enterprise.api.request.LogAttributeVo; +import com.terrabase.enterprise.api.response.ResultVo; + +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +/** + * 开源版日志管理服务实现 + * 基于开源技术实现日志管理功能 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@Service +public class OpenLogServiceImpl implements LogService { + + private static final Logger logger = LoggerFactory.getLogger(OpenLogServiceImpl.class); + + @Override + public ResultVo<Integer> registerLogs(List<LogAttributeVo> logs) { + if (logs == null || logs.isEmpty()) { + logger.warn("审计日志对象不能为空"); + return ResultVo.error("400", "审计日志对象不能为空"); + } + + try { + logger.info("开源版执行审计日志上报,日志数量: {}", logs.size()); + + // 开源版不记录审计日志,只进行简单的日志输出 + for (LogAttributeVo log : logs) { + logger.debug("开源版审计日志处理 - 序号: {}, 日志类型: {}, 用户名: {}, 操作: {}, 来源: {}, 终端: {}, 结果: {}", + log.getSn(), log.getLogType(), log.getUsername(), log.getOperation(), + log.getSource(), log.getTerminal(), log.getResult()); + } + + return ResultVo.success(logs.size()); + + } catch (Exception e) { + logger.error("开源版审计日志上报失败", e); + return ResultVo.error("500", "审计日志上报失败: " + e.getMessage()); + } + } + + @Override + public ResultVo<Boolean> registryInternational(List<LogI18n> logI18ns) { + if (logI18ns == null || logI18ns.isEmpty()) { + logger.warn("审计日志国际化对象不能为空"); + return ResultVo.error("400", "审计日志国际化对象不能为空"); + } + + try { + logger.info("开源版执行审计日志国际化注册,国际化信息数量: {}", logI18ns.size()); + + // 开源版不记录审计日志,只进行简单的日志输出 + for (LogI18n logI18n : logI18ns) { + logger.debug("开源版审计日志国际化处理 - 代码: {}, 语言: {}, 内容: {}", + logI18n.getCode(), logI18n.getLanguage(), logI18n.getContent()); + } + + return ResultVo.success(true); + + } catch (Exception e) { + logger.error("开源版审计日志国际化注册失败", e); + return ResultVo.error("500", "审计日志国际化注册失败: " + e.getMessage()); + } + } +} + + + diff --git a/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/OpenMenuServiceImpl.java b/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/OpenMenuServiceImpl.java new file mode 100644 index 0000000..8fbb9ac --- /dev/null +++ b/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/OpenMenuServiceImpl.java @@ -0,0 +1,36 @@ +package com.terrabase.enterprise.impl.open; + +import com.terrabase.enterprise.api.MenuService; +import com.terrabase.enterprise.api.dto.MenuRegisterInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +/** + * 开源版菜单管理服务实现 + * 提供菜单注册功能 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@Service +public class OpenMenuServiceImpl implements MenuService { + + private static final Logger logger = LoggerFactory.getLogger(OpenMenuServiceImpl.class); + + @Override + public void registerMenuInfo(MenuRegisterInfo menuRegisterInfo) { + + if (menuRegisterInfo == null) { + logger.warn("菜单注册对象不能为空"); + return; + } + + try { + logger.info("开源版执行菜单注册 - 菜单ID: {}, 菜单名称: {}, URL: {}", + menuRegisterInfo.getMenuId(), menuRegisterInfo.getMenuNameCode(), menuRegisterInfo.getUrl()); + } catch (Exception e) { + logger.error("开源版菜单注册失败: {}", menuRegisterInfo, e); + } + } +} diff --git a/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/OpenMonitoringServiceImpl.java b/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/OpenMonitoringServiceImpl.java new file mode 100644 index 0000000..171ef89 --- /dev/null +++ b/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/OpenMonitoringServiceImpl.java @@ -0,0 +1,204 @@ +package com.terrabase.enterprise.impl.open; + +import com.terrabase.enterprise.api.MonitoringService; +import com.terrabase.enterprise.api.dto.*; +import com.terrabase.enterprise.api.request.RegisterEventDefineReq; +import com.terrabase.enterprise.api.request.GetEventsParams; +import com.terrabase.enterprise.api.response.ResultVo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 开源版监控告警服务实现 + * 基于开源技术实现监控告警功能 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@Service +public class OpenMonitoringServiceImpl implements MonitoringService { + + private static final Logger logger = LoggerFactory.getLogger(OpenMonitoringServiceImpl.class); + + @Override + public ResultVo registerEventDefine(RegisterEventDefineReq req) { + if (req == null) { + logger.warn("告警定义注册请求对象不能为空"); + return ResultVo.error("400", "告警定义注册请求对象不能为空"); + } + + try { + logger.info("开源版执行告警定义注册,服务名称: {}, 服务英文名: {}, 服务中文名: {}, 删除所有: {}, 告警定义数量: {}", + req.getServiceName(), req.getServiceEn(), req.getServiceZh(), req.isDeleteAll(), req.getEventDefines().size()); + + // 开源版不进行实际的告警定义注册,只进行详细的日志输出 + for (EventDefine eventDefine : req.getEventDefines()) { + logger.info("开源版告警定义注册处理 - 事件ID: {}, 事件名称: {}, 事件类型: {}, 严重级别: {}, 分类: {}, 部件: {}", + eventDefine.getEventId(), + eventDefine.getName(), + eventDefine.getType(), + eventDefine.getSeverity(), + eventDefine.getCategory(), + eventDefine.getParts()); + + logger.debug("开源版告警定义详细信息 - 影响: {}, 描述: {}, 主体类型: {}, 原因: {}, 建议: {}, 版本: {}, 语言: {}, 匹配键: {}", + eventDefine.getEffect(), + eventDefine.getDescription(), + eventDefine.getSubjectType(), + eventDefine.getCause(), + eventDefine.getSuggestion(), + eventDefine.getVersion(), + eventDefine.getLanguage(), + eventDefine.getDefineMatchKey()); + } + + return ResultVo.success("告警定义注册成功"); + + } catch (Exception e) { + logger.error("开源版告警定义注册失败", e); + return ResultVo.error("500", "告警定义注册失败: " + e.getMessage()); + } + } + + @Override + public ResultVo<Boolean> sendEvents(List<EventInfo> eventInfos) { + if (eventInfos == null || eventInfos.isEmpty()) { + logger.warn("事件信息列表不能为空"); + return ResultVo.error("400", "事件信息列表不能为空"); + } + + try { + logger.info("开源版执行事件上报,事件数量: {}", eventInfos.size()); + + // 开源版不进行实际的事件上报,只进行详细的日志输出 + for (EventInfo eventInfo : eventInfos) { + logger.info("开源版事件上报处理 - 事件ID: {}, 序列号: {}, 事件名称: {}, 事件类型: {}, 严重级别: {}, 状态: {}", + eventInfo.getId(), + eventInfo.getSerialNumber(), + eventInfo.getEventName(), + eventInfo.getEventType(), + eventInfo.getSeverity(), + eventInfo.getStatus()); + + logger.debug("开源版事件详细信息 - 事件主体: {}, 主体类型: {}, 描述: {}, 影响: {}, 分类: {}, 原因: {}, 建议: {}", + eventInfo.getEventSubject(), + eventInfo.getEventSubjectType(), + eventInfo.getEventDescription(), + eventInfo.getEffect(), + eventInfo.getEventCategory(), + eventInfo.getPossibleCause(), + eventInfo.getSuggestion()); + + logger.debug("开源版事件时间信息 - 首次发生时间: {}, 清除时间: {}, 事件来源: {}, 设备序列号: {}, 设备类型: {}, 部件: {}, 语言: {}", + eventInfo.getFirstOccurTime(), + eventInfo.getClearTime(), + eventInfo.getEvenSource(), + eventInfo.getDeviceSn(), + eventInfo.getDeviceType(), + eventInfo.getParts(), + eventInfo.getLanguage()); + } + + return ResultVo.success(true); + + } catch (Exception e) { + logger.error("开源版事件上报失败", e); + return ResultVo.error("500", "事件上报失败: " + e.getMessage()); + } + } + + @Override + public ResultVo<EventsCollection> getEventsByPage(GetEventsParams getEventsParams) { + if (getEventsParams == null) { + logger.warn("事件查询参数对象不能为空"); + return ResultVo.error("400", "事件查询参数对象不能为空"); + } + + try { + logger.info("开源版执行事件分页查询,页码: {}, 每页大小: {}, 告警名称: {}, 严重级别: {}, 事件类型: {}, 语言: {}", + getEventsParams.getPageNum(), getEventsParams.getPageSize(), + getEventsParams.getAlarmName(), getEventsParams.getSeverity(), + getEventsParams.getEventType(), getEventsParams.getLanguage()); + + // 开源版返回模拟数据,使用 EventObject 结构 + List<EventObject> events = new java.util.ArrayList<>(); + + // 创建第一个事件对象 + EventObject event1 = new EventObject(); + event1.setId("1"); + event1.setEventId("event_001"); + event1.setEventName("数据使能服务异常"); + event1.setEventType("alter"); + event1.setEventCategory("system"); + event1.setDeviceSn("DEVICE_001"); + event1.setDeviceId("device_001"); + event1.setEventSubject("数据使能服务"); + event1.setSeverity("critical"); + event1.setEventSource("数据使能模块"); + event1.setParts("data_service"); + event1.setClearType("auto"); + event1.setStatus("Uncleared"); + event1.setConfirmStatus("unconfirmed"); + event1.setBlockingStatus(false); + event1.setOccurCounts(1); + event1.setFirstOccurTime(System.currentTimeMillis()); + event1.setLastOccurTime(System.currentTimeMillis()); + event1.setEventDescription("数据使能服务响应超时"); + event1.setSuggestion("检查服务状态和网络连接"); + event1.setCause("网络连接超时"); + event1.setSendToeService(false); + event1.setSerialNumber("SN_001"); + event1.setDevUrl("http://device001:8080"); + events.add(event1); + + // 创建第二个事件对象 + EventObject event2 = new EventObject(); + event2.setId("2"); + event2.setEventId("event_002"); + event2.setEventName("KMC加密失败"); + event2.setEventType("alter"); + event2.setEventCategory("security"); + event2.setDeviceSn("DEVICE_002"); + event2.setDeviceId("device_002"); + event2.setEventSubject("KMC加密服务"); + event2.setSeverity("major"); + event2.setEventSource("KMC模块"); + event2.setParts("kmc_service"); + event2.setClearType("manual"); + event2.setClearUser("admin"); + event2.setStatus("Cleared"); + event2.setConfirmStatus("confirmed"); + event2.setBlockingStatus(false); + event2.setOccurCounts(1); + event2.setFirstOccurTime(System.currentTimeMillis() - 3600000L); // 1小时前 + event2.setLastOccurTime(System.currentTimeMillis() - 3600000L); + event2.setClearTime(System.currentTimeMillis() - 1800000L); // 30分钟前 + event2.setEventDescription("KMC加密操作失败"); + event2.setSuggestion("检查密钥配置和证书状态"); + event2.setCause("密钥配置错误"); + event2.setSendToeService(true); + event2.setSerialNumber("SN_002"); + event2.setDevUrl("http://device002:8080"); + events.add(event2); + + EventsCollection eventsCollection = new EventsCollection(); + eventsCollection.setEvents(events); + eventsCollection.setTotalCount(2); + + logger.info("开源版事件分页查询成功,查询到事件数量: {}, 总记录数: {}", + events.size(), eventsCollection.getTotalCount()); + + return ResultVo.success(eventsCollection); + + } catch (Exception e) { + logger.error("开源版事件分页查询失败", e); + return ResultVo.error("500", "事件分页查询失败: " + e.getMessage()); + } + } +} + + + diff --git a/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/OpenUserManagementServiceImpl.java b/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/OpenUserManagementServiceImpl.java new file mode 100644 index 0000000..ab610ab --- /dev/null +++ b/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/OpenUserManagementServiceImpl.java @@ -0,0 +1,171 @@ +package com.terrabase.enterprise.impl.open; + +import com.terrabase.enterprise.api.UserManagementService; +import com.terrabase.enterprise.api.dto.AuthorityInfo; +import com.terrabase.enterprise.api.dto.LoginUserDto; +import com.terrabase.enterprise.api.dto.ResourceGroup; +import com.terrabase.enterprise.api.request.RoleRegisterVo; +import com.terrabase.enterprise.api.response.ResultVo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +/** + * 开源版用户管理服务实现 + * 基于开源技术实现用户管理功能 + * + * @author Yehong Pan + * @version 1.0.0 + */ +@Service +public class OpenUserManagementServiceImpl implements UserManagementService { + + private static final Logger logger = LoggerFactory.getLogger(OpenUserManagementServiceImpl.class); + + // ========== 用户注册相关接口实现 ========== + + @Override + public void batchRegisterRole(RoleRegisterVo roleRegister) { + if (roleRegister == null) { + logger.warn("角色注册对象不能为空"); + return; + } + + try { + logger.info("开源版执行批量角色注册"); + + // 处理角色注册信息列表 + if (roleRegister.getRoleRegisterInfos() != null && !roleRegister.getRoleRegisterInfos().isEmpty()) { + logger.info("开源版处理角色注册信息,数量: {}", roleRegister.getRoleRegisterInfos().size()); + + for (com.terrabase.enterprise.api.dto.RoleRegisterInfo roleInfo : roleRegister.getRoleRegisterInfos()) { + logger.info("开源版角色注册成功 - 角色名: {}, 角色名代码: {}, 描述: {}, 可创建: {}, 支持登录类型: {}", + roleInfo.getName(), + roleInfo.getNameCode(), + roleInfo.getDescription(), + roleInfo.isCreatable(), + roleInfo.getSupportLoginType()); + } + } + + // 处理角色国际化信息列表 + if (roleRegister.getRoleI18nInfos() != null && !roleRegister.getRoleI18nInfos().isEmpty()) { + logger.info("开源版处理角色国际化信息,数量: {}", roleRegister.getRoleI18nInfos().size()); + + for (com.terrabase.enterprise.api.dto.RoleI18nInfo i18nInfo : roleRegister.getRoleI18nInfos()) { + logger.info("开源版角色国际化信息 - 角色名: {}, 代码: {}, 语言: {}, 内容: {}", + i18nInfo.getName(), + i18nInfo.getCode(), + i18nInfo.getLanguage(), + i18nInfo.getContent()); + } + } + + } catch (Exception e) { + logger.error("开源版批量角色注册失败: {}", roleRegister, e); + } + } + + @Override + public void registerPermission(List<AuthorityInfo> authorityInfos) { + if (authorityInfos == null || authorityInfos.isEmpty()) { + logger.warn("权限注册列表不能为空"); + return; + } + + try { + logger.info("开源版执行批量权限注册,权限数量: {}", authorityInfos.size()); + + for (AuthorityInfo authorityInfo : authorityInfos) { + logger.info("开源版权限注册成功 - 资源标识: {}, 描述: {}, 跳过检查: {}, 所需角色: {}", + authorityInfo.getResourceKey(), + authorityInfo.getDescription(), + authorityInfo.isSkipCheck(), + authorityInfo.getRoles()); + } + + } catch (Exception e) { + logger.error("开源版批量权限注册失败: {}", authorityInfos, e); + } + } + + @Override + public List<ResourceGroup> getUserGroups(String userName) { + if (userName == null || userName.trim().isEmpty()) { + logger.warn("用户名不能为空"); + return new ArrayList<>(); + } + + try { + logger.info("开源版获取用户资源组: {}", userName); + + // 开源版实现:返回默认的公共资源组 + List<ResourceGroup> groups = new ArrayList<>(); + groups.add(ResourceGroup.buildPublicGroup()); + + logger.info("开源版获取用户资源组成功 - 用户: {}, 资源组数量: {}", userName, groups.size()); + + return groups; + + } catch (Exception e) { + logger.error("开源版获取用户资源组失败 - 用户: {}", userName, e); + return new ArrayList<>(); + } + } + + // ========== 用户认证相关接口实现 ========== + + @Override + public ResultVo<List<String>> queryRolesByToken() { + try { + logger.info("开源版执行根据token查询角色名"); + + // 开源版实现:返回模拟的角色数据 + List<String> roles = new java.util.ArrayList<>(); + roles.add("admin"); + roles.add("user"); + roles.add("operator"); + + logger.info("开源版根据token查询角色名成功,角色数量: {}", roles.size()); + return ResultVo.success(roles); + + } catch (Exception e) { + logger.error("开源版根据token查询角色名失败", e); + return ResultVo.error("500", "角色查询失败: " + e.getMessage()); + } + } + + @Override + public ResultVo<List<LoginUserDto>> getCurrentUserInfo() { + try { + logger.info("开源版执行获取当前用户信息"); + + // 开源版实现:返回模拟的用户信息 + List<LoginUserDto> users = new java.util.ArrayList<>(); + + LoginUserDto user = new LoginUserDto(); + user.setUserName("admin"); + user.setUserId("admin_001"); + user.setRole("admin"); + + // 设置资源组 + List<ResourceGroup> resourceGroups = new java.util.ArrayList<>(); + resourceGroups.add(ResourceGroup.buildPublicGroup()); + user.setResourceGroups(resourceGroups); + users.add(user); + + logger.info("开源版获取当前用户信息成功,用户数量: {}", users.size()); + return ResultVo.success(users); + + } catch (Exception e) { + logger.error("开源版获取当前用户信息失败", e); + return ResultVo.error("500", "用户信息查询失败: " + e.getMessage()); + } + } +} + + + diff --git a/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/config/KmcConfig.java b/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/config/KmcConfig.java new file mode 100644 index 0000000..645ee94 --- /dev/null +++ b/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/config/KmcConfig.java @@ -0,0 +1,193 @@ +package com.terrabase.enterprise.impl.open.config; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.concurrent.ConcurrentHashMap; +import java.util.Map; + +/** + * KMC配置管理类 + * 管理密钥管理中心的配置信息 + * + * @author Terrabase Team + * @version 1.0.0 + */ +@Component +public class KmcConfig { + + private static final Logger logger = LoggerFactory.getLogger(KmcConfig.class); + + // 默认配置 + private boolean enabled = true; + private String defaultKeyId = "default"; + private boolean keyCaching = true; + private int maxCacheSize = 100; + private long keyExpirationTime = 24 * 60 * 60 * 1000; // 24小时 + + // 自定义密钥配置 + private Map<String, String> customKeys = new ConcurrentHashMap<>(); + + // 安全配置 + private boolean enableKeyRotation = false; + private long keyRotationInterval = 7 * 24 * 60 * 60 * 1000; // 7天 + private boolean enableAuditLog = true; + + @PostConstruct + public void init() { + initialize(); + } + + /** + * 手动初始化方法,用于非Spring环境 + */ + public void initialize() { + logger.info("初始化KMC配置..."); + logger.info("KMC启用状态: {}", enabled); + logger.info("默认密钥ID: {}", defaultKeyId); + logger.info("密钥缓存: {}", keyCaching); + logger.info("最大缓存大小: {}", maxCacheSize); + logger.info("密钥过期时间: {} ms", keyExpirationTime); + logger.info("密钥轮换: {}", enableKeyRotation); + logger.info("密钥轮换间隔: {} ms", keyRotationInterval); + logger.info("审计日志: {}", enableAuditLog); + logger.info("自定义密钥数量: {}", customKeys.size()); + + if (customKeys.isEmpty()) { + logger.info("未配置自定义密钥,将使用默认密钥"); + } else { + logger.info("已配置自定义密钥: {}", customKeys.keySet()); + } + } + + // Getter和Setter方法 + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getDefaultKeyId() { + return defaultKeyId; + } + + public void setDefaultKeyId(String defaultKeyId) { + this.defaultKeyId = defaultKeyId; + } + + + public boolean isKeyCaching() { + return keyCaching; + } + + public void setKeyCaching(boolean keyCaching) { + this.keyCaching = keyCaching; + } + + public int getMaxCacheSize() { + return maxCacheSize; + } + + public void setMaxCacheSize(int maxCacheSize) { + this.maxCacheSize = maxCacheSize; + } + + public long getKeyExpirationTime() { + return keyExpirationTime; + } + + public void setKeyExpirationTime(long keyExpirationTime) { + this.keyExpirationTime = keyExpirationTime; + } + + public Map<String, String> getCustomKeys() { + return customKeys; + } + + public void setCustomKeys(Map<String, String> customKeys) { + this.customKeys = customKeys; + } + + public boolean isEnableKeyRotation() { + return enableKeyRotation; + } + + public void setEnableKeyRotation(boolean enableKeyRotation) { + this.enableKeyRotation = enableKeyRotation; + } + + public long getKeyRotationInterval() { + return keyRotationInterval; + } + + public void setKeyRotationInterval(long keyRotationInterval) { + this.keyRotationInterval = keyRotationInterval; + } + + public boolean isEnableAuditLog() { + return enableAuditLog; + } + + public void setEnableAuditLog(boolean enableAuditLog) { + this.enableAuditLog = enableAuditLog; + } + + /** + * 添加自定义密钥 + * @param keyId 密钥ID + * @param keyValue 密钥值(Base64编码) + */ + public void addCustomKey(String keyId, String keyValue) { + customKeys.put(keyId, keyValue); + logger.info("添加自定义密钥: {}", keyId); + } + + /** + * 移除自定义密钥 + * @param keyId 密钥ID + * @return 是否移除成功 + */ + public boolean removeCustomKey(String keyId) { + String removed = customKeys.remove(keyId); + if (removed != null) { + logger.info("移除自定义密钥: {}", keyId); + return true; + } + return false; + } + + /** + * 获取自定义密钥 + * @param keyId 密钥ID + * @return 密钥值 + */ + public String getCustomKey(String keyId) { + return customKeys.get(keyId); + } + + /** + * 检查是否为自定义密钥 + * @param keyId 密钥ID + * @return 是否为自定义密钥 + */ + public boolean isCustomKey(String keyId) { + return customKeys.containsKey(keyId); + } + + /** + * 获取配置摘要 + * @return 配置摘要字符串 + */ + public String getConfigSummary() { + return String.format( + "KMC配置摘要 - 启用: %s, 默认密钥: %s, 缓存: %s, 自定义密钥: %d", + enabled, defaultKeyId, keyCaching, customKeys.size() + ); + } +} diff --git a/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/util/KmcCryptoUtil.java b/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/util/KmcCryptoUtil.java new file mode 100644 index 0000000..6d8357e --- /dev/null +++ b/enterprise-impl-open/src/main/java/com/terrabase/enterprise/impl/open/util/KmcCryptoUtil.java @@ -0,0 +1,535 @@ +package com.terrabase.enterprise.impl.open.util; + +import com.terrabase.enterprise.api.CryptoAlgorithm; +import org.apache.commons.codec.binary.Base64; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.Map; + +/** + * KMC (Key Management Center) 加解密工具类 + * 基于开源算法实现数据加解密功能 + * + * @author Yehong Pan + * @version 1.0.0 + */ +public class KmcCryptoUtil { + + private static final Logger logger = LoggerFactory.getLogger(KmcCryptoUtil.class); + + // 加密算法 + private static final String ALGORITHM = "AES"; + private static final String TRANSFORMATION = "AES/GCM/NoPadding"; + private static final int GCM_IV_LENGTH = 12; // 96位 + private static final int GCM_TAG_LENGTH = 16; // 128位 + private static final int KEY_LENGTH = 256; // 256位密钥 + + // 密钥缓存 + private static final Map<String, SecretKey> keyCache = new ConcurrentHashMap<>(); + private static final Map<String, KeyPair> keyPairCache = new ConcurrentHashMap<>(); + + // 随机数生成器 + private static final SecureRandom secureRandom = new SecureRandom(); + + static { + // 添加BouncyCastle安全提供者 + if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { + Security.addProvider(new BouncyCastleProvider()); + } + } + + /** + * 生成AES密钥(不缓存) + * @param keyId 密钥ID + * @return 生成的密钥 + */ + public static SecretKey generateKey(String keyId) { + try { + KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM); + keyGenerator.init(KEY_LENGTH); + SecretKey key = keyGenerator.generateKey(); + logger.info("成功生成密钥: {}", keyId); + return key; + } catch (NoSuchAlgorithmException e) { + logger.error("生成密钥失败", e); + throw new RuntimeException("生成密钥失败", e); + } + } + + /** + * 生成并缓存AES密钥 + * @param keyId 密钥ID + * @return 生成的密钥 + */ + public static SecretKey generateAndCacheKey(String keyId) { + SecretKey key = generateKey(keyId); + keyCache.put(keyId, key); + return key; + } + + /** + * 从字节数组创建密钥 + * @param keyBytes 密钥字节数组 + * @return SecretKey对象 + */ + public static SecretKey createKeyFromBytes(byte[] keyBytes) { + return new SecretKeySpec(keyBytes, ALGORITHM); + } + + /** + * 获取或生成密钥 + * @param keyId 密钥ID + * @return 密钥对象 + */ + public static SecretKey getOrGenerateKey(String keyId) { + SecretKey key = keyCache.get(keyId); + if (key == null) { + key = generateAndCacheKey(keyId); + } + return key; + } + + /** + * 使用指定密钥加密数据 + * @param plaintext 明文数据 + * @param keyId 密钥ID + * @return 加密后的Base64编码字符串 + */ + public static String encrypt(String plaintext, String keyId) { + if (plaintext == null || plaintext.isEmpty()) { + throw new IllegalArgumentException("明文数据不能为空"); + } + + try { + SecretKey key = getOrGenerateKey(keyId); + + // 生成随机IV + byte[] iv = new byte[GCM_IV_LENGTH]; + secureRandom.nextBytes(iv); + + // 创建Cipher对象 + Cipher cipher = Cipher.getInstance(TRANSFORMATION); + GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv); + cipher.init(Cipher.ENCRYPT_MODE, key, gcmSpec); + + // 加密数据 + byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); + + // 将IV和加密数据组合 + byte[] combined = new byte[GCM_IV_LENGTH + encryptedBytes.length]; + System.arraycopy(iv, 0, combined, 0, GCM_IV_LENGTH); + System.arraycopy(encryptedBytes, 0, combined, GCM_IV_LENGTH, encryptedBytes.length); + + // 返回Base64编码的结果 + return Base64.encodeBase64String(combined); + + } catch (Exception e) { + logger.error("数据加密失败,密钥ID: {}", keyId, e); + throw new RuntimeException("数据加密失败", e); + } + } + + /** + * 使用指定密钥解密数据 + * @param ciphertext 密文数据(Base64编码) + * @param keyId 密钥ID + * @return 解密后的明文数据 + */ + public static String decrypt(String ciphertext, String keyId) { + if (ciphertext == null || ciphertext.isEmpty()) { + throw new IllegalArgumentException("密文数据不能为空"); + } + + try { + SecretKey key = getOrGenerateKey(keyId); + + // 解码Base64 + byte[] combined = Base64.decodeBase64(ciphertext); + + // 分离IV和加密数据 + byte[] iv = new byte[GCM_IV_LENGTH]; + byte[] encryptedBytes = new byte[combined.length - GCM_IV_LENGTH]; + System.arraycopy(combined, 0, iv, 0, GCM_IV_LENGTH); + System.arraycopy(combined, GCM_IV_LENGTH, encryptedBytes, 0, encryptedBytes.length); + + // 创建Cipher对象 + Cipher cipher = Cipher.getInstance(TRANSFORMATION); + GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv); + cipher.init(Cipher.DECRYPT_MODE, key, gcmSpec); + + // 解密数据 + byte[] decryptedBytes = cipher.doFinal(encryptedBytes); + return new String(decryptedBytes, StandardCharsets.UTF_8); + + } catch (Exception e) { + logger.error("数据解密失败,密钥ID: {}", keyId, e); + throw new RuntimeException("数据解密失败", e); + } + } + + /** + * 使用默认密钥加密 + * @param plaintext 明文数据 + * @return 加密后的Base64编码字符串 + */ + public static String encrypt(String plaintext) { + return encrypt(plaintext, "default"); + } + + /** + * 使用默认密钥解密 + * @param ciphertext 密文数据(Base64编码) + * @return 解密后的明文数据 + */ + public static String decrypt(String ciphertext) { + return decrypt(ciphertext, "default"); + } + + /** + * 验证密钥是否存在 + * @param keyId 密钥ID + * @return 密钥是否存在 + */ + public static boolean hasKey(String keyId) { + return keyCache.containsKey(keyId); + } + + /** + * 删除密钥 + * @param keyId 密钥ID + * @return 是否删除成功 + */ + public static boolean removeKey(String keyId) { + SecretKey removed = keyCache.remove(keyId); + if (removed != null) { + logger.info("密钥已删除: {}", keyId); + return true; + } + return false; + } + + /** + * 清空所有密钥 + */ + public static void clearAllKeys() { + int count = keyCache.size(); + keyCache.clear(); + logger.info("已清空所有密钥,共删除 {} 个密钥", count); + } + + /** + * 获取密钥数量 + * @return 当前缓存的密钥数量 + */ + public static int getKeyCount() { + return keyCache.size(); + } + + /** + * 获取密钥信息 + * @param keyId 密钥ID + * @return 密钥信息字符串 + */ + public static String getKeyInfo(String keyId) { + SecretKey key = keyCache.get(keyId); + if (key == null) { + return "密钥不存在: " + keyId; + } + + return String.format("密钥ID: %s, 算法: %s, 长度: %d bits", + keyId, key.getAlgorithm(), key.getEncoded().length * 8); + } + + // ==================== 多算法支持方法 ==================== + + /** + * 使用指定算法加密数据 + * @param plaintext 明文数据 + * @param keyId 密钥ID + * @param algorithm 加密算法 + * @return 加密后的Base64编码字符串 + */ + public static String encrypt(String plaintext, String keyId, CryptoAlgorithm algorithm) { + if (plaintext == null || plaintext.isEmpty()) { + throw new IllegalArgumentException("明文数据不能为空"); + } + + if (algorithm == null) { + algorithm = CryptoAlgorithm.AES; // 默认使用AES + } + + try { + if (algorithm.isSymmetric()) { + return encryptSymmetric(plaintext, keyId, algorithm); + } else if (algorithm.isAsymmetric()) { + return encryptAsymmetric(plaintext, keyId, algorithm); + } else { + throw new IllegalArgumentException("不支持的加密算法: " + algorithm); + } + } catch (Exception e) { + logger.error("数据加密失败,密钥ID: {}, 算法: {}", keyId, algorithm, e); + throw new RuntimeException("数据加密失败", e); + } + } + + /** + * 使用指定算法解密数据 + * @param ciphertext 密文数据(Base64编码) + * @param keyId 密钥ID + * @param algorithm 解密算法 + * @return 解密后的明文数据 + */ + public static String decrypt(String ciphertext, String keyId, CryptoAlgorithm algorithm) { + if (ciphertext == null || ciphertext.isEmpty()) { + throw new IllegalArgumentException("密文数据不能为空"); + } + + if (algorithm == null) { + algorithm = CryptoAlgorithm.AES; // 默认使用AES + } + + try { + if (algorithm.isSymmetric()) { + return decryptSymmetric(ciphertext, keyId, algorithm); + } else if (algorithm.isAsymmetric()) { + return decryptAsymmetric(ciphertext, keyId, algorithm); + } else { + throw new IllegalArgumentException("不支持的解密算法: " + algorithm); + } + } catch (Exception e) { + logger.error("数据解密失败,密钥ID: {}, 算法: {}", keyId, algorithm, e); + throw new RuntimeException("数据解密失败", e); + } + } + + /** + * 对称加密 + * @param plaintext 明文数据 + * @param keyId 密钥ID + * @param algorithm 加密算法 + * @return 加密后的Base64编码字符串 + */ + private static String encryptSymmetric(String plaintext, String keyId, CryptoAlgorithm algorithm) { + try { + SecretKey key = getOrGenerateKey(keyId); + + // 根据算法选择不同的加密方式 + if (algorithm == CryptoAlgorithm.AES) { + return encryptAES(plaintext, key); + } else if (algorithm == CryptoAlgorithm.DES) { + return encryptDES(plaintext, key); + } else if (algorithm == CryptoAlgorithm.TRIPLE_DES) { + return encryptTripleDES(plaintext, key); + } else if (algorithm == CryptoAlgorithm.BLOWFISH) { + return encryptBlowfish(plaintext, key); + } else { + // 默认使用AES + return encryptAES(plaintext, key); + } + } catch (Exception e) { + logger.error("对称加密失败,算法: {}", algorithm, e); + throw new RuntimeException("对称加密失败", e); + } + } + + /** + * 对称解密 + * @param ciphertext 密文数据 + * @param keyId 密钥ID + * @param algorithm 解密算法 + * @return 解密后的明文数据 + */ + private static String decryptSymmetric(String ciphertext, String keyId, CryptoAlgorithm algorithm) { + try { + SecretKey key = getOrGenerateKey(keyId); + + // 根据算法选择不同的解密方式 + if (algorithm == CryptoAlgorithm.AES) { + return decryptAES(ciphertext, key); + } else if (algorithm == CryptoAlgorithm.DES) { + return decryptDES(ciphertext, key); + } else if (algorithm == CryptoAlgorithm.TRIPLE_DES) { + return decryptTripleDES(ciphertext, key); + } else if (algorithm == CryptoAlgorithm.BLOWFISH) { + return decryptBlowfish(ciphertext, key); + } else { + // 默认使用AES + return decryptAES(ciphertext, key); + } + } catch (Exception e) { + logger.error("对称解密失败,算法: {}", algorithm, e); + throw new RuntimeException("对称解密失败", e); + } + } + + /** + * AES加密 + */ + private static String encryptAES(String plaintext, SecretKey key) throws Exception { + Cipher cipher = Cipher.getInstance(TRANSFORMATION); + GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, new byte[GCM_IV_LENGTH]); + cipher.init(Cipher.ENCRYPT_MODE, key, gcmSpec); + + byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); + byte[] iv = cipher.getIV(); + + byte[] combined = new byte[iv.length + encryptedBytes.length]; + System.arraycopy(iv, 0, combined, 0, iv.length); + System.arraycopy(encryptedBytes, 0, combined, iv.length, encryptedBytes.length); + + return Base64.encodeBase64String(combined); + } + + /** + * AES解密 + */ + private static String decryptAES(String ciphertext, SecretKey key) throws Exception { + byte[] combined = Base64.decodeBase64(ciphertext); + byte[] iv = new byte[GCM_IV_LENGTH]; + byte[] encryptedBytes = new byte[combined.length - GCM_IV_LENGTH]; + + System.arraycopy(combined, 0, iv, 0, GCM_IV_LENGTH); + System.arraycopy(combined, GCM_IV_LENGTH, encryptedBytes, 0, encryptedBytes.length); + + Cipher cipher = Cipher.getInstance(TRANSFORMATION); + GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv); + cipher.init(Cipher.DECRYPT_MODE, key, gcmSpec); + + byte[] decryptedBytes = cipher.doFinal(encryptedBytes); + return new String(decryptedBytes, StandardCharsets.UTF_8); + } + + /** + * DES加密 + */ + private static String encryptDES(String plaintext, SecretKey key) throws Exception { + Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, key); + + byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); + byte[] iv = cipher.getIV(); + + byte[] combined = new byte[iv.length + encryptedBytes.length]; + System.arraycopy(iv, 0, combined, 0, iv.length); + System.arraycopy(encryptedBytes, 0, combined, iv.length, encryptedBytes.length); + + return Base64.encodeBase64String(combined); + } + + /** + * DES解密 + */ + private static String decryptDES(String ciphertext, SecretKey key) throws Exception { + byte[] combined = Base64.decodeBase64(ciphertext); + byte[] iv = new byte[8]; + byte[] encryptedBytes = new byte[combined.length - 8]; + + System.arraycopy(combined, 0, iv, 0, 8); + System.arraycopy(combined, 8, encryptedBytes, 0, encryptedBytes.length); + + Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); + + byte[] decryptedBytes = cipher.doFinal(encryptedBytes); + return new String(decryptedBytes, StandardCharsets.UTF_8); + } + + /** + * 3DES加密 + */ + private static String encryptTripleDES(String plaintext, SecretKey key) throws Exception { + Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, key); + + byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); + byte[] iv = cipher.getIV(); + + byte[] combined = new byte[iv.length + encryptedBytes.length]; + System.arraycopy(iv, 0, combined, 0, iv.length); + System.arraycopy(encryptedBytes, 0, combined, iv.length, encryptedBytes.length); + + return Base64.encodeBase64String(combined); + } + + /** + * 3DES解密 + */ + private static String decryptTripleDES(String ciphertext, SecretKey key) throws Exception { + byte[] combined = Base64.decodeBase64(ciphertext); + byte[] iv = new byte[8]; + byte[] encryptedBytes = new byte[combined.length - 8]; + + System.arraycopy(combined, 0, iv, 0, 8); + System.arraycopy(combined, 8, encryptedBytes, 0, encryptedBytes.length); + + Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); + + byte[] decryptedBytes = cipher.doFinal(encryptedBytes); + return new String(decryptedBytes, StandardCharsets.UTF_8); + } + + /** + * Blowfish加密 + */ + private static String encryptBlowfish(String plaintext, SecretKey key) throws Exception { + Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, key); + + byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); + byte[] iv = cipher.getIV(); + + byte[] combined = new byte[iv.length + encryptedBytes.length]; + System.arraycopy(iv, 0, combined, 0, iv.length); + System.arraycopy(encryptedBytes, 0, combined, iv.length, encryptedBytes.length); + + return Base64.encodeBase64String(combined); + } + + /** + * Blowfish解密 + */ + private static String decryptBlowfish(String ciphertext, SecretKey key) throws Exception { + byte[] combined = Base64.decodeBase64(ciphertext); + byte[] iv = new byte[8]; + byte[] encryptedBytes = new byte[combined.length - 8]; + + System.arraycopy(combined, 0, iv, 0, 8); + System.arraycopy(combined, 8, encryptedBytes, 0, encryptedBytes.length); + + Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); + + byte[] decryptedBytes = cipher.doFinal(encryptedBytes); + return new String(decryptedBytes, StandardCharsets.UTF_8); + } + + /** + * 非对称加密 + */ + private static String encryptAsymmetric(String plaintext, String keyId, CryptoAlgorithm algorithm) { + // 简化实现,实际项目中需要更复杂的非对称加密逻辑 + logger.warn("非对称加密暂未完全实现,使用对称加密替代"); + return encryptSymmetric(plaintext, keyId, CryptoAlgorithm.AES); + } + + /** + * 非对称解密 + */ + private static String decryptAsymmetric(String ciphertext, String keyId, CryptoAlgorithm algorithm) { + // 简化实现,实际项目中需要更复杂的非对称解密逻辑 + logger.warn("非对称解密暂未完全实现,使用对称解密替代"); + return decryptSymmetric(ciphertext, keyId, CryptoAlgorithm.AES); + } +} \ No newline at end of file diff --git a/enterprise-impl-open/src/test/java/com/terrabase/enterprise/impl/open/KmcCryptoUtilTest.java b/enterprise-impl-open/src/test/java/com/terrabase/enterprise/impl/open/KmcCryptoUtilTest.java new file mode 100644 index 0000000..9f59ecc --- /dev/null +++ b/enterprise-impl-open/src/test/java/com/terrabase/enterprise/impl/open/KmcCryptoUtilTest.java @@ -0,0 +1,254 @@ +package com.terrabase.enterprise.impl.open; + +import com.terrabase.enterprise.impl.open.util.KmcCryptoUtil; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import static org.junit.jupiter.api.Assertions.*; + +/** + * KMC加解密工具类测试 + * + * @author Terrabase Team + * @version 1.0.0 + */ +@DisplayName("KMC加解密工具类测试") +public class KmcCryptoUtilTest { + + private static final String TEST_KEY_ID = "test_key"; + private static final String TEST_PLAINTEXT = "Hello, Terrabase KMC!"; + + @BeforeEach + void setUp() { + // 清空密钥缓存 + KmcCryptoUtil.clearAllKeys(); + } + + @Test + @DisplayName("测试密钥生成") + void testGenerateKey() { + // 生成密钥 + var key = KmcCryptoUtil.generateAndCacheKey(TEST_KEY_ID); + + // 验证密钥不为空 + assertNotNull(key); + + // 验证密钥已缓存 + assertTrue(KmcCryptoUtil.hasKey(TEST_KEY_ID)); + + // 验证密钥信息 + String keyInfo = KmcCryptoUtil.getKeyInfo(TEST_KEY_ID); + assertTrue(keyInfo.contains(TEST_KEY_ID)); + assertTrue(keyInfo.contains("AES")); + } + + @Test + @DisplayName("测试基本加解密功能") + void testBasicEncryptDecrypt() { + // 生成密钥 + KmcCryptoUtil.generateAndCacheKey(TEST_KEY_ID); + + // 加密 + String ciphertext = KmcCryptoUtil.encrypt(TEST_PLAINTEXT, TEST_KEY_ID); + + // 验证加密结果 + assertNotNull(ciphertext); + assertNotEquals(TEST_PLAINTEXT, ciphertext); + assertTrue(ciphertext.length() > 0); + + // 解密 + String decryptedText = KmcCryptoUtil.decrypt(ciphertext, TEST_KEY_ID); + + // 验证解密结果 + assertNotNull(decryptedText); + assertEquals(TEST_PLAINTEXT, decryptedText); + } + + @Test + @DisplayName("测试默认密钥加解密") + void testDefaultKeyEncryptDecrypt() { + // 使用默认密钥加密 + String ciphertext = KmcCryptoUtil.encrypt(TEST_PLAINTEXT); + + // 验证加密结果 + assertNotNull(ciphertext); + assertNotEquals(TEST_PLAINTEXT, ciphertext); + + // 使用默认密钥解密 + String decryptedText = KmcCryptoUtil.decrypt(ciphertext); + + // 验证解密结果 + assertEquals(TEST_PLAINTEXT, decryptedText); + } + + @Test + @DisplayName("测试不同密钥的加解密") + void testDifferentKeys() { + String keyId1 = "key1"; + String keyId2 = "key2"; + + // 生成两个不同的密钥 + KmcCryptoUtil.generateAndCacheKey(keyId1); + KmcCryptoUtil.generateAndCacheKey(keyId2); + + // 使用第一个密钥加密 + String ciphertext1 = KmcCryptoUtil.encrypt(TEST_PLAINTEXT, keyId1); + + // 使用第二个密钥加密 + String ciphertext2 = KmcCryptoUtil.encrypt(TEST_PLAINTEXT, keyId2); + + // 验证不同密钥产生的密文不同 + assertNotEquals(ciphertext1, ciphertext2); + + // 验证只能用对应的密钥解密 + assertEquals(TEST_PLAINTEXT, KmcCryptoUtil.decrypt(ciphertext1, keyId1)); + assertEquals(TEST_PLAINTEXT, KmcCryptoUtil.decrypt(ciphertext2, keyId2)); + + // 验证用错误的密钥解密会失败 + assertThrows(RuntimeException.class, () -> { + KmcCryptoUtil.decrypt(ciphertext1, keyId2); + }); + } + + @Test + @DisplayName("测试空字符串和null值处理") + void testEmptyAndNullValues() { + KmcCryptoUtil.generateAndCacheKey(TEST_KEY_ID); + + // 测试空字符串 + assertThrows(IllegalArgumentException.class, () -> { + KmcCryptoUtil.encrypt("", TEST_KEY_ID); + }); + + assertThrows(IllegalArgumentException.class, () -> { + KmcCryptoUtil.decrypt("", TEST_KEY_ID); + }); + + // 测试null值 + assertThrows(IllegalArgumentException.class, () -> { + KmcCryptoUtil.encrypt(null, TEST_KEY_ID); + }); + + assertThrows(IllegalArgumentException.class, () -> { + KmcCryptoUtil.decrypt(null, TEST_KEY_ID); + }); + } + + @Test + @DisplayName("测试长文本加解密") + void testLongTextEncryptDecrypt() { + KmcCryptoUtil.generateAndCacheKey(TEST_KEY_ID); + + // 生成长文本 + StringBuilder longText = new StringBuilder(); + for (int i = 0; i < 1000; i++) { + longText.append("This is a long text for testing KMC encryption and decryption. "); + } + String longPlaintext = longText.toString(); + + // 加密长文本 + String ciphertext = KmcCryptoUtil.encrypt(longPlaintext, TEST_KEY_ID); + + // 验证加密成功 + assertNotNull(ciphertext); + assertNotEquals(longPlaintext, ciphertext); + + // 解密长文本 + String decryptedText = KmcCryptoUtil.decrypt(ciphertext, TEST_KEY_ID); + + // 验证解密成功 + assertEquals(longPlaintext, decryptedText); + } + + @Test + @DisplayName("测试特殊字符加解密") + void testSpecialCharactersEncryptDecrypt() { + KmcCryptoUtil.generateAndCacheKey(TEST_KEY_ID); + + // 包含特殊字符的文本 + String specialText = "特殊字符测试: !@#$%^&*()_+-=[]{}|;':\",./<>?`~ 中文测试 1234567890"; + + // 加密 + String ciphertext = KmcCryptoUtil.encrypt(specialText, TEST_KEY_ID); + + // 验证加密成功 + assertNotNull(ciphertext); + assertNotEquals(specialText, ciphertext); + + // 解密 + String decryptedText = KmcCryptoUtil.decrypt(ciphertext, TEST_KEY_ID); + + // 验证解密成功 + assertEquals(specialText, decryptedText); + } + + @Test + @DisplayName("测试密钥管理功能") + void testKeyManagement() { + String keyId1 = "key1"; + String keyId2 = "key2"; + + // 初始状态应该没有密钥 + assertEquals(0, KmcCryptoUtil.getKeyCount()); + + // 生成密钥 + KmcCryptoUtil.generateAndCacheKey(keyId1); + KmcCryptoUtil.generateAndCacheKey(keyId2); + + // 验证密钥数量 + assertEquals(2, KmcCryptoUtil.getKeyCount()); + + // 验证密钥存在 + assertTrue(KmcCryptoUtil.hasKey(keyId1)); + assertTrue(KmcCryptoUtil.hasKey(keyId2)); + + // 删除一个密钥 + assertTrue(KmcCryptoUtil.removeKey(keyId1)); + assertFalse(KmcCryptoUtil.hasKey(keyId1)); + assertTrue(KmcCryptoUtil.hasKey(keyId2)); + assertEquals(1, KmcCryptoUtil.getKeyCount()); + + // 清空所有密钥 + KmcCryptoUtil.clearAllKeys(); + assertEquals(0, KmcCryptoUtil.getKeyCount()); + assertFalse(KmcCryptoUtil.hasKey(keyId2)); + } + + @Test + @DisplayName("测试无效密文解密") + void testInvalidCiphertextDecrypt() { + KmcCryptoUtil.generateAndCacheKey(TEST_KEY_ID); + + // 测试无效的Base64字符串 + assertThrows(RuntimeException.class, () -> { + KmcCryptoUtil.decrypt("invalid_base64_string", TEST_KEY_ID); + }); + + // 测试太短的密文 + assertThrows(RuntimeException.class, () -> { + KmcCryptoUtil.decrypt("short", TEST_KEY_ID); + }); + + // 测试不存在的密钥 + assertThrows(RuntimeException.class, () -> { + KmcCryptoUtil.decrypt("some_ciphertext", "non_existent_key"); + }); + } + + @Test + @DisplayName("测试多次加密同一明文") + void testMultipleEncryptionOfSamePlaintext() { + KmcCryptoUtil.generateAndCacheKey(TEST_KEY_ID); + + // 多次加密同一明文 + String ciphertext1 = KmcCryptoUtil.encrypt(TEST_PLAINTEXT, TEST_KEY_ID); + String ciphertext2 = KmcCryptoUtil.encrypt(TEST_PLAINTEXT, TEST_KEY_ID); + + // 验证每次加密的结果都不同(由于随机IV) + assertNotEquals(ciphertext1, ciphertext2); + + // 验证都能正确解密 + assertEquals(TEST_PLAINTEXT, KmcCryptoUtil.decrypt(ciphertext1, TEST_KEY_ID)); + assertEquals(TEST_PLAINTEXT, KmcCryptoUtil.decrypt(ciphertext2, TEST_KEY_ID)); + } +} diff --git a/enterprise-impl-open/src/test/java/com/terrabase/enterprise/impl/open/TestConfiguration.java b/enterprise-impl-open/src/test/java/com/terrabase/enterprise/impl/open/TestConfiguration.java new file mode 100644 index 0000000..4fe17ef --- /dev/null +++ b/enterprise-impl-open/src/test/java/com/terrabase/enterprise/impl/open/TestConfiguration.java @@ -0,0 +1,28 @@ +package com.terrabase.enterprise.impl.open; + +import com.terrabase.enterprise.impl.open.config.KmcConfig; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.context.annotation.Bean; + +/** + * 测试配置类 + * 为Spring Boot测试提供必要的配置 + * + * @author Terrabase Team + * @version 1.0.0 + */ +@SpringBootConfiguration +public class TestConfiguration { + + /** + * KMC配置Bean + */ + @Bean + public KmcConfig kmcConfig() { + KmcConfig config = new KmcConfig(); + config.setEnabled(true); + config.setDefaultKeyId("test_default"); + config.setEnableAuditLog(true); + return config; + } +} diff --git a/pom.xml b/pom.xml index 36fb8ab..d77237b 100644 --- a/pom.xml +++ b/pom.xml @@ -5,17 +5,17 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> - <groupId>com.example</groupId> - <artifactId>project-name</artifactId> + <groupId>com.terrabase</groupId> + <artifactId>terrabase</artifactId> <version>1.0.0</version> - <packaging>jar</packaging> + <packaging>pom</packaging> - <name>Project Name</name> - <description>Project description</description> + <name>Terrabase</name> + <description>Terrabase - 企业级数据使能平台</description> <modules> <module>business-app</module> - <module>enterprise-app</module> + <module>enterprise-api</module> <module>enterprise-impl-commercial</module> <module>enterprise-impl-open</module> </modules> @@ -24,5 +24,94 @@ <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <spring-boot.version>3.2.0</spring-boot.version> + <spring-cloud.version>2023.0.4</spring-cloud.version> + <spring-cloud-alibaba.version>2023.0.3.2</spring-cloud-alibaba.version> </properties> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-dependencies</artifactId> + <version>${spring-boot.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + <dependency> + <groupId>org.springframework.cloud</groupId> + <artifactId>spring-cloud-dependencies</artifactId> + <version>${spring-cloud.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + <dependency> + <groupId>com.alibaba.cloud</groupId> + <artifactId>spring-cloud-alibaba-dependencies</artifactId> + <version>${spring-cloud-alibaba.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <repositories> + <repository> + <id>aliyun-maven</id> + <name>Aliyun Maven Repository</name> + <url>https://maven.aliyun.com/repository/public</url> + <releases> + <enabled>true</enabled> + </releases> + <snapshots> + <enabled>false</enabled> + </snapshots> + </repository> + <repository> + <id>central</id> + <name>Maven Central Repository</name> + <url>https://repo1.maven.org/maven2</url> + <releases> + <enabled>true</enabled> + </releases> + <snapshots> + <enabled>false</enabled> + </snapshots> + </repository> + </repositories> + + <pluginRepositories> + <pluginRepository> + <id>aliyun-maven</id> + <name>Aliyun Maven Plugin Repository</name> + <url>https://maven.aliyun.com/repository/public</url> + <releases> + <enabled>true</enabled> + </releases> + <snapshots> + <enabled>false</enabled> + </snapshots> + </pluginRepository> + </pluginRepositories> + + <build> + <pluginManagement> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <version>${spring-boot.version}</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.11.0</version> + <configuration> + <source>17</source> + <target>17</target> + </configuration> + </plugin> + </plugins> + </pluginManagement> + </build> </project>