Merge branch 'dev' into test
This commit is contained in:
2
pom.xml
2
pom.xml
@@ -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>
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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]));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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/**"));
|
||||
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user