1. 新增外部系统编码部门编码关联管理

2. 新增统一的 api 对外门户管理
3. 修正各个模块的 api 命名
This commit is contained in:
chenbowen
2025-10-17 17:40:46 +08:00
parent ce8e06d2a3
commit 78bc88b7a6
106 changed files with 4200 additions and 1377 deletions

View File

@@ -22,7 +22,7 @@
<!-- <module>zt-module-ai</module>-->
<module>zt-module-template</module>
<!-- <module>zt-module-iot</module>-->
<!-- <module>zt-module-databus</module>-->
<module>zt-module-databus</module>
<!-- <module>zt-module-rule</module>-->
<!-- <module>zt-module-html2pdf</module>-->
</modules>

View File

@@ -1,31 +1,37 @@
-- 统一外部 API 网关菜单权限初始化DM8
-- 可重复执行的初始化脚本,统一外部网关所有页面改为权限菜单控制
DELETE FROM system_menu
WHERE id IN (6500,6501,6502,6503,
650101,650102,650103,650104,650105,650106,
650201,650202,650203,650204,
650301,650302,650303,650304);
-- 清理旧数据,确保脚本可重复执行
DELETE FROM system_menu WHERE id IN (6500,6501,650101,650102,650103);
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name,
status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted)
VALUES
(6500, '数据总线', '', 1, 20, 1, 'databus', 'ep:data-board', '', 'DatabusRoot',
0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
(6501, 'API 定义', 'databus:gateway:query', 2, 10, 6500, 'gateway', 'ep:list', 'databus/gateway/index', 'DatabusGateway',
0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
(6502, '客户端凭证', 'databus:credential:query', 2, 20, 6500, 'credential', 'ep:key', 'databus/credential/index', 'DatabusCredential',
0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
(6503, '限流策略', 'databus:policy:query', 2, 30, 6500, 'policy/rate-limit', 'ep:stopwatch', 'databus/policy/RateLimitPolicy', 'DatabusRateLimitPolicy',
0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0');
-- 顶级目录(父级假定为 id=2 的系统管理目录)
INSERT INTO system_menu (
id, name, permission, type, sort, parent_id,
path, icon, component, status, component_name
) VALUES (
6500, '统一外部网关', '', 1, 20, 1,
'databus', 'ep:data-line', '', 0, NULL
);
-- API 门户页面
INSERT INTO system_menu (
id, name, permission, type, sort, parent_id,
path, icon, component, status, component_name
) VALUES (
6501, 'API 门户', 'databus:gateway:query', 2, 1, 6500,
'databus-gateway', 'ep:cpu', 'databus/gateway/index', 0, 'DatabusGateway'
);
-- 页面内操作按钮权限
INSERT INTO system_menu (
id, name, permission, type, sort, parent_id,
path, icon, component, status
) VALUES
(650101, 'API 列表', 'databus:gateway:query', 3, 1, 6501, '', '', '', 0),
(650102, 'API 调试', 'databus:gateway:invoke', 3, 2, 6501, '', '', '', 0),
(650103, '刷新定义', 'databus:gateway:refresh', 3, 3, 6501, '', '', '', 0);
d
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name,
status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted)
VALUES
(650101, 'API 查询', 'databus:gateway:query', 3, 1, 6501, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
(650102, 'API 新建', 'databus:gateway:create', 3, 2, 6501, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
(650103, 'API 编辑', 'databus:gateway:update', 3, 3, 6501, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
(650104, 'API 删除', 'databus:gateway:delete', 3, 4, 6501, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
(650105, 'API 调试', 'databus:gateway:invoke', 3, 5, 6501, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
(650106, 'API 刷新', 'databus:gateway:refresh', 3, 6, 6501, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
(650201, '凭证查询', 'databus:credential:query', 3, 1, 6502, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
(650202, '凭证新增', 'databus:credential:create', 3, 2, 6502, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
(650203, '凭证修改', 'databus:credential:update', 3, 3, 6502, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
(650204, '凭证删除', 'databus:credential:delete', 3, 4, 6502, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
(650301, '策略查询', 'databus:policy:query', 3, 1, 6503, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
(650302, '策略新增', 'databus:policy:create', 3, 2, 6503, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
(650303, '策略修改', 'databus:policy:update', 3, 3, 6503, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0'),
(650304, '策略删除', 'databus:policy:delete', 3, 4, 6503, '', '', '', '', 0, '1', '1', '1', 'admin', CURRENT_TIMESTAMP, 'admin', CURRENT_TIMESTAMP, '0');

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,50 @@
package com.zt.plat.framework.common.util.security;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class CryptoSignatureUtilsTest {
@Test
void decryptShouldIgnoreWhitespaceInCiphertext() {
String key = "test-key";
String plaintext = "{\"sample\":123}";
String encrypted = CryptoSignatureUtils.encrypt(plaintext, key, CryptoSignatureUtils.ENCRYPT_TYPE_AES);
int splitIndex = Math.max(1, encrypted.length() / 2);
String cipherWithWhitespace = " " + encrypted.substring(0, splitIndex)
+ " \n\t "
+ encrypted.substring(splitIndex);
String decrypted = CryptoSignatureUtils.decrypt(cipherWithWhitespace, key, CryptoSignatureUtils.ENCRYPT_TYPE_AES);
assertEquals(plaintext, decrypted);
}
@Test
void decryptShouldRestorePlusCharactersConvertedToSpaces() {
String key = "test-key";
String basePlaintext = "payload-";
String encryptedWithPlus = null;
String chosenPlaintext = null;
for (int i = 0; i < 100; i++) {
String candidatePlaintext = basePlaintext + i;
String candidateEncrypted = CryptoSignatureUtils.encrypt(candidatePlaintext, key, CryptoSignatureUtils.ENCRYPT_TYPE_AES);
if (candidateEncrypted.indexOf('+') >= 0) {
encryptedWithPlus = candidateEncrypted;
chosenPlaintext = candidatePlaintext;
break;
}
}
assertNotNull(encryptedWithPlus, "Expected to generate ciphertext containing '+' character");
String mutatedCipher = encryptedWithPlus.replace('+', ' ');
String decrypted = CryptoSignatureUtils.decrypt(mutatedCipher, key, CryptoSignatureUtils.ENCRYPT_TYPE_AES);
assertEquals(chosenPlaintext, decrypted);
}
}

View File

@@ -1,11 +1,20 @@
package com.zt.plat.framework.signature.config;
import com.zt.plat.framework.redis.config.ZtRedisAutoConfiguration;
import com.zt.plat.framework.signature.core.aop.ApiSignatureAspect;
import com.zt.plat.framework.signature.core.ApiSignatureVerifier;
import com.zt.plat.framework.signature.core.config.ApiSignatureProperties;
import com.zt.plat.framework.signature.core.redis.ApiSignatureRedisDAO;
import com.zt.plat.framework.signature.core.web.ApiSignatureHandlerInterceptor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
/**
* HTTP API 签名的自动配置类
@@ -13,16 +22,47 @@ import org.springframework.data.redis.core.StringRedisTemplate;
* @author Zhougang
*/
@AutoConfiguration(after = ZtRedisAutoConfiguration.class)
@EnableConfigurationProperties(ApiSignatureProperties.class)
public class ZtApiSignatureAutoConfiguration {
@Bean
public ApiSignatureAspect signatureAspect(ApiSignatureRedisDAO signatureRedisDAO) {
return new ApiSignatureAspect(signatureRedisDAO);
}
@Bean
public ApiSignatureRedisDAO signatureRedisDAO(StringRedisTemplate stringRedisTemplate) {
return new ApiSignatureRedisDAO(stringRedisTemplate);
}
@Bean
public ApiSignatureVerifier apiSignatureVerifier(ApiSignatureRedisDAO signatureRedisDAO) {
return new ApiSignatureVerifier(signatureRedisDAO);
}
@Bean
public ApiSignatureHandlerInterceptor apiSignatureHandlerInterceptor(ApiSignatureVerifier verifier,
ApiSignatureProperties properties) {
return new ApiSignatureHandlerInterceptor(verifier, properties);
}
@Bean
public WebMvcConfigurer apiSignatureWebMvcConfigurer(ApiSignatureHandlerInterceptor interceptor,
ApiSignatureProperties properties) {
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
if (!properties.isEnabled()) {
return;
}
InterceptorRegistration registration = registry.addInterceptor(interceptor);
List<String> includePaths = properties.getIncludePaths();
if (CollectionUtils.isEmpty(includePaths)) {
registration.addPathPatterns("/**");
} else {
registration.addPathPatterns(includePaths.toArray(new String[0]));
}
List<String> excludePaths = properties.getExcludePaths();
if (!CollectionUtils.isEmpty(excludePaths)) {
registration.excludePathPatterns(excludePaths.toArray(new String[0]));
}
}
};
}
}

View File

@@ -0,0 +1,68 @@
package com.zt.plat.framework.signature.core.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* API 签名配置
*/
@Data
@ConfigurationProperties(prefix = "zt.api-signature")
public class ApiSignatureProperties {
/**
* 是否开启全局签名校验
*/
private boolean enabled = true;
/**
* 签名有效期
*/
private int timeout = 60;
/**
* 时间单位
*/
private TimeUnit timeUnit = TimeUnit.SECONDS;
/**
* 校验失败时的提示信息
*/
private String message = "签名不正确";
/**
* 请求头appId
*/
private String appId = "appId";
/**
* 请求头timestamp
*/
private String timestamp = "timestamp";
/**
* 请求头nonce
*/
private String nonce = "nonce";
/**
* 请求头sign
*/
private String sign = "sign";
/**
* 需要进行签名校验的路径,默认全量
*/
private List<String> includePaths = new ArrayList<>(Arrays.asList("/**"));
/**
* 无需签名校验的路径
*/
private List<String> excludePaths = new ArrayList<>(Arrays.asList("/error", "/swagger-ui/**", "/v3/api-docs/**"));
}

View File

@@ -0,0 +1,50 @@
package com.zt.plat.framework.signature.core.model;
import cn.hutool.core.util.StrUtil;
import com.zt.plat.framework.signature.core.annotation.ApiSignature;
import com.zt.plat.framework.signature.core.config.ApiSignatureProperties;
import lombok.Builder;
import lombok.Getter;
import java.util.concurrent.TimeUnit;
/**
* 签名校验规则
*/
@Getter
@Builder
public class ApiSignatureRule {
private final int timeout;
private final TimeUnit timeUnit;
private final String message;
private final String appId;
private final String timestamp;
private final String nonce;
private final String sign;
public static ApiSignatureRule from(ApiSignatureProperties properties) {
return ApiSignatureRule.builder()
.timeout(properties.getTimeout())
.timeUnit(properties.getTimeUnit())
.message(properties.getMessage())
.appId(properties.getAppId())
.timestamp(properties.getTimestamp())
.nonce(properties.getNonce())
.sign(properties.getSign())
.build();
}
public static ApiSignatureRule from(ApiSignature signature, ApiSignatureProperties defaults) {
return ApiSignatureRule.builder()
.timeout(signature.timeout())
.timeUnit(signature.timeUnit())
.message(StrUtil.blankToDefault(signature.message(), defaults.getMessage()))
.appId(signature.appId())
.timestamp(signature.timestamp())
.nonce(signature.nonce())
.sign(signature.sign())
.build();
}
}

Some files were not shown because too many files have changed in this diff Show More