diff --git a/script/sql/zfjd-refactoring-v1/其他/gh/ghV1.0.6-0921.sql b/script/sql/zfjd-refactoring-v1/其他/gh/ghV1.0.6-0921.sql new file mode 100644 index 00000000..823c6c08 --- /dev/null +++ b/script/sql/zfjd-refactoring-v1/其他/gh/ghV1.0.6-0921.sql @@ -0,0 +1,26 @@ +DROP TABLE IF EXISTS sys_api_platform_config; +CREATE TABLE `sys_api_platform_config` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `channel_name` varchar(100) COLLATE utf8mb4_general_ci NOT NULL COMMENT '渠道名称', + `channel_no` varchar(16) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '渠道编号(shiju=市局,longgang=龙岗,longhua=龙华,CIM=CIM大屏)', + `app_key` varchar(64) COLLATE utf8mb4_general_ci NOT NULL COMMENT '应用Key', + `app_secret` varchar(256) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '应用密钥(HMAC模式)', + `platform_public_key` text COLLATE utf8mb4_general_ci COMMENT '平台公钥(RSA)', + `platform_private_key` text COLLATE utf8mb4_general_ci COMMENT '平台私钥(RSA)', + `caller_public_key` text COLLATE utf8mb4_general_ci COMMENT '调用方公钥(RSA)', + `sign_enabled` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否启用签名验证', + `encrypt_enabled` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否启用加解密', + `ip_whitelist_enabled` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否启用IP白名单(0否 1是)', + `ip_whitelist` varchar(500) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'IP白名单,多个用逗号分隔,为空则不限制', + `status` char(1) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0' COMMENT '状态(0正常 1停用)', + `remark` varchar(500) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注', + `create_dept` bigint DEFAULT NULL COMMENT '创建部门', + `create_by` bigint DEFAULT NULL COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_by` bigint DEFAULT NULL COMMENT '更新者', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_app_key` (`app_key`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='接口平台配置表'; + + diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/ApiSignUtil.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/ApiSignUtil.java index f02b6b1e..a096b9f2 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/ApiSignUtil.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/ApiSignUtil.java @@ -101,29 +101,37 @@ public class ApiSignUtil { String platformPublicKey = platformRsa.getPublicKeyBase64(); String platformPrivateKey = platformRsa.getPrivateKeyBase64(); - System.out.println("app_platformPublicKeykey: " + platformPublicKey); - System.out.println("platformPrivateKey: " + platformPrivateKey); -// -// RSA callerRsa = SecureUtil.rsa(); -// String callerPublicKey = callerRsa.getPublicKeyBase64(); -// -// String sql = "INSERT INTO sys_api_platform_config " + -// "(channel_name, channel_no, app_key, app_secret, " + -// "platform_public_key, platform_private_key, caller_public_key, " + -// "sign_enabled, encrypt_enabled, status, create_time) VALUES (\n" + -// " '龙岗', 'longgang', '" + appKey + "', '" + appSecret + "',\n" + -// " '" + platformPublicKey + "',\n" + -// " '" + platformPrivateKey + "',\n" + -// " '" + callerPublicKey + "',\n" + -// " 1, 0, '0', NOW()\n" + -// ");"; -// -// System.out.println(sql); -// System.out.println(); -// System.out.println("=== 密钥信息(交给调用方) ==="); -// System.out.println("app_key: " + appKey); -// System.out.println("app_secret: " + appSecret + " (HMAC签名用)"); -// System.out.println("platformPublicKey: " + platformPublicKey + " (加密请求用)"); + RSA callerRsa = SecureUtil.rsa(); + String callerPublicKey = callerRsa.getPublicKeyBase64(); + String callerPrivateKey = callerRsa.getPrivateKeyBase64(); + + System.out.println("=== 平台密钥 ==="); + System.out.println("platformPublicKey: " + platformPublicKey); + System.out.println("platformPrivateKey: " + platformPrivateKey); + System.out.println(); + + String sql = "INSERT INTO sys_api_platform_config " + + "(channel_name, channel_no, app_key, app_secret, " + + "platform_public_key, platform_private_key, caller_public_key, " + + "sign_enabled, encrypt_enabled, ip_whitelist_enabled, ip_whitelist, " + + "status, create_time) VALUES (\n" + + " 'CIM大屏', 'CIM', '" + appKey + "', '" + appSecret + "',\n" + + " '" + platformPublicKey + "',\n" + + " '" + platformPrivateKey + "',\n" + + " '" + callerPublicKey + "',\n" + + " 1, 0, 0, NULL,\n" + + " '0', NOW()\n" + + ");"; + + System.out.println(sql); + System.out.println(); + System.out.println("=== 密钥信息(交给调用方) ==="); + System.out.println("app_key: " + appKey); + System.out.println("app_secret: " + appSecret + " (HMAC签名用)"); + System.out.println("platformPublicKey: " + platformPublicKey + " (加密请求用)"); + System.out.println(); + System.out.println("=== 调用方密钥(自行保管,勿泄露) ==="); + System.out.println("callerPrivateKey: " + callerPrivateKey + " (解密响应用)"); } diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/handler/ApiSignInterceptor.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/handler/ApiSignInterceptor.java index d180a2c8..8442fa20 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/handler/ApiSignInterceptor.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/handler/ApiSignInterceptor.java @@ -11,11 +11,14 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.constant.HttpStatus; import org.dromara.common.core.domain.R; +import org.dromara.common.core.utils.ServletUtils; import org.dromara.common.json.utils.JsonUtils; +import org.dromara.common.redis.utils.RedisUtils; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import java.io.IOException; +import java.time.Duration; import java.util.HashMap; import java.util.Map; @@ -48,7 +51,7 @@ public class ApiSignInterceptor implements HandlerInterceptor { return false; } - SysApiPlatformConfigEntity consumer = sysApiPlatformConfigService.findByAppKey(appKey).orElse(null); + SysApiPlatformConfigEntity consumer = sysApiPlatformConfigService.findByAppKeyFromDb(appKey).orElse(null); if (consumer == null) { writeError(response, "无效的 appKey"); @@ -104,7 +107,33 @@ public class ApiSignInterceptor implements HandlerInterceptor { return false; } + // IP 白名单校验 + if (Boolean.TRUE.equals(consumer.getIpWhitelistEnabled())) { + String ipWhitelist = consumer.getIpWhitelist(); + if (StrUtil.isNotBlank(ipWhitelist)) { + String clientIp = ServletUtils.getClientIP(request); + boolean ipAllowed = StrUtil.split(ipWhitelist, ',') + .stream() + .map(String::trim) + .anyMatch(ip -> ip.equals(clientIp)); + if (!ipAllowed) { + writeError(response, "IP 不在白名单中: " + clientIp); + return false; + } + } + } + log.info("API签名验证通过, appKey={}, path={}", appKey, request.getRequestURI()); + +// // Nonce 唯一性校验(防重放,有效期与 timestamp 超时一致) +// String nonceKey = "api_nonce:" + appKey + ":" + nonce; +// if (Boolean.TRUE.equals(RedisUtils.hasKey(nonceKey))) { +// writeError(response, "nonce 重复"); +// return false; +// } +// RedisUtils.setCacheObject(nonceKey, "1", +// Duration.ofSeconds(apiPlatformConfig.getTimestampExpireSeconds())); + return true; } diff --git a/zdxt-web-server/zdxt-admin-web-server/src/main/resources/application.yml b/zdxt-web-server/zdxt-admin-web-server/src/main/resources/application.yml index a0588930..7eca7dc1 100644 --- a/zdxt-web-server/zdxt-admin-web-server/src/main/resources/application.yml +++ b/zdxt-web-server/zdxt-admin-web-server/src/main/resources/application.yml @@ -340,15 +340,3 @@ http: api-platform: enabled: true timestamp-expire-seconds: 36000 - consumers: - partner-a: - app-key: ak_partner_a_xxxx - app-secret: sk_partner_a_xxxx - platform-public-key: MIGfMA0GCSq... - platform-private-key: MIICdQIBADANBg... - caller-public-key: MIGfMA0GCSq... - sign-enabled: true - encrypt-enabled: true - allowed-paths: - - /api/v1/data/** - - /api/v1/query/**