From 9b39491fb0462ea6304d6fa25554026eb56ec072 Mon Sep 17 00:00:00 2001 From: vincent Date: Mon, 12 Oct 2026 10:16:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=B9=B3=E5=8F=B0=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../externalInterface/ApiSignDemo.java | 153 ++++++++++++++++++ .../LongGangInterfaceController.java | 1 + .../src/main/resources/application-dev.yml | 16 +- 3 files changed, 162 insertions(+), 8 deletions(-) create mode 100644 zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/externalInterface/ApiSignDemo.java diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/externalInterface/ApiSignDemo.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/externalInterface/ApiSignDemo.java new file mode 100644 index 00000000..43de7ae9 --- /dev/null +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/externalInterface/ApiSignDemo.java @@ -0,0 +1,153 @@ +package com.zdxt.enforcementcode.controller.externalInterface; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.asymmetric.KeyType; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import com.alibaba.fastjson2.JSONObject; +import com.zdxt.enforcementcode.common.util.ApiSignUtil; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +public class ApiSignDemo { + + private static final String APP_KEY = "XXX"; + private static final String APP_SECRET = "XXX"; + + private static final String BASE_URL = "http://localhost:8080"; + + // 平台公钥:加密请求体 + private static final String PLATFORM_PUBLIC_KEY = "XXXXXX"; + // 调用方私钥:解密响应体 + private static final String CALLER_PRIVATE_KEY = "XXXXXX"; + + public static void main(String[] args) throws Exception { + System.out.println("========================================"); + System.out.println(" 模拟第三方请求 - 加签 + 加解密"); + System.out.println("========================================\n"); + + // ===================================================================== + // ① 加签:生成 HMAC-SHA256 签名 + // ===================================================================== + // timestamp: 毫秒级时间戳(13位),服务端校验与服务器时间差不超过配置阈值 + // nonce: 随机字符串,防止重放攻击 + // 签名原文: 将 appKey、timestamp、nonce 按 key 字母排序后拼接为 key=value&key=value 格式 + // 签名值: 使用 HmacSHA256 算法 + appSecret 对签名原文计算摘要,结果 Base64 编码 + String timestamp = String.valueOf(System.currentTimeMillis()); + String nonce = IdUtil.fastSimpleUUID(); + + Map signParams = new HashMap<>(); + signParams.put("appKey", APP_KEY); + signParams.put("timestamp", timestamp); + signParams.put("nonce", nonce); + + // buildSignContent: TreeMap 排序 → 过滤空值 → 拼接 "appKey=xxx&nonce=xxx×tamp=xxx" + String signContent = ApiSignUtil.buildSignContent(signParams); + // hmacSign: HmacSHA256(签名原文, appSecret) → Base64 编码 + String sign = ApiSignUtil.hmacSign(signContent, APP_SECRET); + + System.out.println("【签名参数】"); + System.out.println(" App-Key: " + APP_KEY); + System.out.println(" Timestamp: " + timestamp); + System.out.println(" Nonce: " + nonce); + System.out.println(" 签名原文: " + signContent); + System.out.println(" Sign: " + sign); + System.out.println(); + + // ===================================================================== + // ② 构建业务请求体(JSON 格式,与 Controller 的 @RequestBody Bo 类字段对应) + // ===================================================================== + String rawBody = """ + { + "planType": 1, + "planDate": "2026-09-03", + "planTime": 1, + "enforceContent": "日常环保执法检查0904", + "enterpriseName": "深新粤云(深圳)信息技术有限公司圳市XX科技有限公司", + "enterpriseNumber": "91440300769156389U", + "contactPerson": "张三", + "contactPhone": "13800138000", + "enforceUserNames": "测试1,测试2", + "enforceUserIds": "17608417058,17608416858", + "deptId": "20210922212955057-A601-27251E588", + "deptName": "深圳市司法局", + "createBy": "17608417058", + "remark": "测试数据", + "outSerialNo": "LG20260903001" + }"""; + + System.out.println("【原始请求体】"); + System.out.println(" " + rawBody); + System.out.println(); + + // ===================================================================== + // ③ 加密请求体:RSA 公钥加密(使用平台公钥,服务端用平台私钥解密) + // ===================================================================== + // 加密后的密文是 Base64 编码的字符串,不再是可读的 JSON + // 服务端 ApiCryptoFilter 会用 platformPrivateKey 解密还原为原始 JSON + String requestBody = ApiSignUtil.rsaEncrypt(rawBody, PLATFORM_PUBLIC_KEY); + System.out.println("【加密后请求体】"); + System.out.println(" " + requestBody.substring(0, Math.min(80, requestBody.length())) + "..."); + System.out.println(); + + // ===================================================================== + // ④ 发起 HTTP POST 请求 + // ===================================================================== + // 请求头说明: + // App-Key: 应用标识,服务端通过它查 sys_api_platform_config 获取密钥和渠道信息 + // Timestamp: 毫秒级时间戳,服务端校验请求是否过期 + // Nonce: 随机串,防重放 + // Sign: HMAC-SHA256 签名值,服务端用 appSecret 验签 + // Encrypted: 标记请求体已加密,触发服务端 ApiCryptoFilter 解密逻辑 + // Content-Type: 固定为 application/json + // + // 服务端处理链路: + // ApiCryptoFilter(解密请求体) → ApiSignInterceptor(验签) → Controller(业务处理) + String url = BASE_URL + "/external/api/lg/plan/saveOrUpdatePlan"; + System.out.println("【发送请求】"); + System.out.println(" URL: " + url); + + HttpResponse response = HttpRequest.post(url) + .header("App-Key", APP_KEY) + .header("Timestamp", timestamp) + .header("Nonce", nonce) + .header("Sign", sign) + .header("Encrypted", "true") + .header("Content-Type", "application/json;charset=UTF-8") + .body(requestBody) + .timeout(10000) + .execute(); + + System.out.println(" 状态码: " + response.getStatus()); + System.out.println(" Encrypted: " + response.header("Encrypted")); + System.out.println(); + + // ===================================================================== + // ⑤ 解密响应体:RSA 私钥解密(服务端用调用方公钥加密,调用方用自己的私钥解密) + // ===================================================================== + // 服务端 ApiCryptoFilter 处理完业务后,用 callerPublicKey 加密响应体 + // 调用方收到密文后,用 callerPrivateKey 解密还原为原始 JSON 响应 + String responseBody = response.body(); + System.out.println("【加密响应】"); + System.out.println(" " + (responseBody.length() > 200 ? responseBody.substring(0, 200) + "..." : responseBody)); + System.out.println(); + + // SecureUtil.rsa(privateKey, null): 用调用方私钥构造 RSA 解密器 + // decryptStr: Base64 解码密文 → RSA 私钥解密 → UTF-8 字符串 + String decryptedResponse = SecureUtil.rsa(CALLER_PRIVATE_KEY, null) + .decryptStr(responseBody, KeyType.PrivateKey, StandardCharsets.UTF_8); + + System.out.println("【解密后响应】"); + JSONObject json = JSONObject.parseObject(decryptedResponse); + System.out.println(" " + json.toString()); + + response.close(); + System.out.println(); + System.out.println("========================================"); + System.out.println(" 请求完成"); + System.out.println("========================================"); + } +} diff --git a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/externalInterface/LongGangInterfaceController.java b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/externalInterface/LongGangInterfaceController.java index 6562970d..6ccf062a 100644 --- a/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/externalInterface/LongGangInterfaceController.java +++ b/zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/controller/externalInterface/LongGangInterfaceController.java @@ -15,6 +15,7 @@ import com.zdxt.enforcementcode.domain.vo.SrzDeptVo; import com.zdxt.enforcementcode.domain.vo.externalInterface.LongGangCaseSyncVo; import com.zdxt.enforcementcode.domain.vo.externalInterface.LongGangCreatePlanVo; import com.zdxt.enforcementcode.service.*; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.domain.R; diff --git a/zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-dev.yml b/zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-dev.yml index 3a3952c6..ed352a70 100644 --- a/zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-dev.yml +++ b/zdxt-web-server/zdxt-admin-web-server/src/main/resources/application-dev.yml @@ -58,12 +58,12 @@ spring: # 主库数据源 master: driverClassName: com.mysql.cj.jdbc.Driver -# url: jdbc:mysql://146.56.223.227:3306/zfjd2.0?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 -# username: zfjd -# password: zdxt@1234 - url: jdbc:mysql://172.16.20.72:3306/zfjd_ef20_1_2026_07_13?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 - username: root - password: zdxt@8888 + url: jdbc:mysql://146.56.223.227:3306/zfjd2.0?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 + username: zfjd + password: zdxt@1234 +# url: jdbc:mysql://172.16.20.72:3306/zfjd_ef20_1_2026_07_13?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 +# username: root +# password: zdxt@8888 # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) @@ -376,9 +376,9 @@ business-data-sync: springdoc: api-docs: - enabled: false + enabled: true swagger-ui: - enabled: false + enabled: true info: # 标题 title: '执法监督码接口文档'