Browse Source

外部接口加解密

dev-V1.0.7
vincent 3 days ago
parent
commit
d80ccde4f7
  1. 117
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/ApiSignDemo.java
  2. 43
      zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/ApiSignUtil.java

117
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/ApiSignDemo.java

@ -1,10 +1,13 @@
package com.zdxt.enforcementcode.common.util; package com.zdxt.enforcementcode.common.util;
import cn.hutool.core.util.IdUtil; 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.HttpRequest;
import cn.hutool.http.HttpResponse; import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import java.nio.charset.StandardCharsets;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -15,12 +18,23 @@ public class ApiSignDemo {
private static final String BASE_URL = "http://localhost:8080"; private static final String BASE_URL = "http://localhost:8080";
// 平台公钥:加密请求体
private static final String PLATFORM_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD4u2+dXNQvhTFDJNGvuz4TQAaVpg5Fse0YbPdHbDolpiglbDEuCfuhbekiJiB/9m3h1uArMvXHde26+F8SG2PFjljAmKFuMWkOwH6Zi5Ho7bGRr+nMFZfcEucZOcgb2yMV9RhDrPm5bGtOAgxCf33xKRH08fFzuhZ+T3DvSbpEwQIDAQAB";
// 调用方私钥:解密响应体
private static final String CALLER_PRIVATE_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAK6n1r9N+OSANSVfU6I89EJ+i+fJUNgB4sKdUh/79H/ks1d4gvhsdsytPDkFo27xCz6qw7OtXHJt9vK9KQ/gKMMC+uGoTOWnys8OOn9iGTwZ0DHmzMHrZi6aNZ5DDqO4GzNiro/2h+lZMTDMNTG8zGVuzyFPqQEv7XRq0CmEq76NAgMBAAECgYAaDIlQXgI9nkuO5+FS5LFvi201ZEas+NcHvKwHfi4QtxzyclRiVdcAHuRJAujFvqi1L4Xhsf0EShdXS9QofH/Hmtia7sSqDSzJ06jR4EDucJfUYKfHEcVBroRjjwgGwmtQ6IMp9lVqiqRQWvkwG9xyfPjbqNx/1NvuQ+Mc521kwQJBAOM/a1O9pKS8e/ZT3R2mtDliOuua0y7oT5gq9mqo6HBgDBY/whyxy0MJdnsuKRcgXxu3RBVjq6panNEex1r6D20CQQDEwPR3sRV2ECUQk6k+yDwD1QnkKAbROeGvcp4DSEJwLjNCUkUmL2eQECgFesSTlf1AbGC2NrOaI7L6QsXeklehAkAvXLlmw6C8ANipN0Nfea4YQ6PaFPOs1Bg6s8GV60w+l5YNUQ6I4nllqGQ8OhuwSTvpT+Htv084+xex8gNEr9dlAkBuReL+bm6j1Bqh9tCFkn4oe69mg5SGkVVPGwM6vbYW3531Dt0YsLrvgE6d+5fPS+I7yEJP/cqdmmKGZwQ1YXnhAkEAp6Vd2Q5OXdS/SUezCGf8xhb9SBaPQChgzfgC//N0iug+fDfaSnShHn0LKpBf7yEBvk0M4La7cqnyY233nYHTrQ==";
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
System.out.println("========================================"); System.out.println("========================================");
System.out.println(" 模拟第三方请求 - 完整加签示例"); System.out.println(" 模拟第三方请求 - 加签 + 加解密");
System.out.println("========================================\n"); 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 timestamp = String.valueOf(System.currentTimeMillis());
String nonce = IdUtil.fastSimpleUUID(); String nonce = IdUtil.fastSimpleUUID();
@ -29,7 +43,9 @@ public class ApiSignDemo {
signParams.put("timestamp", timestamp); signParams.put("timestamp", timestamp);
signParams.put("nonce", nonce); signParams.put("nonce", nonce);
// buildSignContent: TreeMap 排序 → 过滤空值 → 拼接 "appKey=xxx&nonce=xxx&timestamp=xxx"
String signContent = ApiSignUtil.buildSignContent(signParams); String signContent = ApiSignUtil.buildSignContent(signParams);
// hmacSign: HmacSHA256(签名原文, appSecret) → Base64 编码
String sign = ApiSignUtil.hmacSign(signContent, APP_SECRET); String sign = ApiSignUtil.hmacSign(signContent, APP_SECRET);
System.out.println("【签名参数】"); System.out.println("【签名参数】");
@ -40,29 +56,55 @@ public class ApiSignDemo {
System.out.println(" Sign: " + sign); System.out.println(" Sign: " + sign);
System.out.println(); System.out.println();
// ② 构建请求体 // =====================================================================
JSONObject body = new JSONObject(); // ② 构建业务请求体(JSON 格式,与 Controller 的 @RequestBody Bo 类字段对应)
body.put("planType", 1); // =====================================================================
body.put("planDate", "2026-09-03"); String rawBody = """
body.put("planTime", 1); {
body.put("enforceContent", "日常环保执法检查0904"); "planType": 1,
body.put("enterpriseName", "深新粤云(深圳)信息技术有限公司圳市XX科技有限公司"); "planDate": "2026-09-03",
body.put("enterpriseNumber", "91440300769156389U"); "planTime": 1,
body.put("contactPerson", "张三"); "enforceContent": "日常环保执法检查0904",
body.put("contactPhone", "13800138000"); "enterpriseName": "深新粤云(深圳)信息技术有限公司圳市XX科技有限公司",
body.put("enforceUserNames", "测试1,测试2"); "enterpriseNumber": "91440300769156389U",
body.put("enforceUserIds", "17608417058,17608416858"); "contactPerson": "张三",
body.put("deptId", "20210922212955057-A601-27251E588"); "contactPhone": "13800138000",
body.put("deptName", "深圳市司法局"); "enforceUserNames": "测试1,测试2",
body.put("createBy", "17608417058"); "enforceUserIds": "17608417058,17608416858",
body.put("remark", "测试数据"); "deptId": "20210922212955057-A601-27251E588",
body.put("outSerialNo", "LG20260903001"); "deptName": "深圳市司法局",
"createBy": "17608417058",
System.out.println("【请求体】"); "remark": "测试数据",
System.out.println(" " + body.toJSONString()); "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(); System.out.println();
// ③ 发起 HTTP 请求 // =====================================================================
// ④ 发起 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"; String url = BASE_URL + "/external/api/lg/plan/saveOrUpdatePlan";
System.out.println("【发送请求】"); System.out.println("【发送请求】");
System.out.println(" URL: " + url); System.out.println(" URL: " + url);
@ -72,23 +114,34 @@ public class ApiSignDemo {
.header("Timestamp", timestamp) .header("Timestamp", timestamp)
.header("Nonce", nonce) .header("Nonce", nonce)
.header("Sign", sign) .header("Sign", sign)
.header("Encrypted", "true")
.header("Content-Type", "application/json;charset=UTF-8") .header("Content-Type", "application/json;charset=UTF-8")
.body(body.toJSONString()) .body(requestBody)
.timeout(10000) .timeout(10000)
.execute(); .execute();
System.out.println(" 状态码: " + response.getStatus()); System.out.println(" 状态码: " + response.getStatus());
System.out.println(" Encrypted: " + response.header("Encrypted"));
System.out.println(); System.out.println();
// ④ 输出响应 // =====================================================================
// ⑤ 解密响应体:RSA 私钥解密(服务端用调用方公钥加密,调用方用自己的私钥解密)
// =====================================================================
// 服务端 ApiCryptoFilter 处理完业务后,用 callerPublicKey 加密响应体
// 调用方收到密文后,用 callerPrivateKey 解密还原为原始 JSON 响应
String responseBody = response.body(); String responseBody = response.body();
System.out.println("【响应结果】"); System.out.println("【加密响应】");
try { System.out.println(" " + (responseBody.length() > 200 ? responseBody.substring(0, 200) + "..." : responseBody));
JSONObject json = JSONObject.parseObject(responseBody); System.out.println();
System.out.println(" " + json.toString());
} catch (Exception e) { // SecureUtil.rsa(privateKey, null): 用调用方私钥构造 RSA 解密器
System.out.println(" " + responseBody); // 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(); response.close();
System.out.println(); System.out.println();

43
zdxt-modules/zdxt-enforcement-code/src/main/java/com/zdxt/enforcementcode/common/util/ApiSignUtil.java

@ -101,26 +101,29 @@ public class ApiSignUtil {
String platformPublicKey = platformRsa.getPublicKeyBase64(); String platformPublicKey = platformRsa.getPublicKeyBase64();
String platformPrivateKey = platformRsa.getPrivateKeyBase64(); String platformPrivateKey = platformRsa.getPrivateKeyBase64();
RSA callerRsa = SecureUtil.rsa(); System.out.println("app_platformPublicKeykey: " + platformPublicKey);
String callerPublicKey = callerRsa.getPublicKeyBase64(); System.out.println("platformPrivateKey: " + platformPrivateKey);
//
String sql = "INSERT INTO sys_api_platform_config " + // RSA callerRsa = SecureUtil.rsa();
"(channel_name, channel_no, app_key, app_secret, " + // String callerPublicKey = callerRsa.getPublicKeyBase64();
"platform_public_key, platform_private_key, caller_public_key, " + //
"sign_enabled, encrypt_enabled, status, create_time) VALUES (\n" + // String sql = "INSERT INTO sys_api_platform_config " +
" '龙岗', 'longgang', '" + appKey + "', '" + appSecret + "',\n" + // "(channel_name, channel_no, app_key, app_secret, " +
" '" + platformPublicKey + "',\n" + // "platform_public_key, platform_private_key, caller_public_key, " +
" '" + platformPrivateKey + "',\n" + // "sign_enabled, encrypt_enabled, status, create_time) VALUES (\n" +
" '" + callerPublicKey + "',\n" + // " '龙岗', 'longgang', '" + appKey + "', '" + appSecret + "',\n" +
" 1, 0, '0', NOW()\n" + // " '" + platformPublicKey + "',\n" +
");"; // " '" + platformPrivateKey + "',\n" +
// " '" + callerPublicKey + "',\n" +
System.out.println(sql); // " 1, 0, '0', NOW()\n" +
System.out.println(); // ");";
System.out.println("=== 密钥信息(交给调用方) ==="); //
System.out.println("app_key: " + appKey); // System.out.println(sql);
System.out.println("app_secret: " + appSecret + " (HMAC签名用)"); // System.out.println();
System.out.println("platformPublicKey: " + platformPublicKey + " (加密请求用)"); // System.out.println("=== 密钥信息(交给调用方) ===");
// System.out.println("app_key: " + appKey);
// System.out.println("app_secret: " + appSecret + " (HMAC签名用)");
// System.out.println("platformPublicKey: " + platformPublicKey + " (加密请求用)");
} }

Loading…
Cancel
Save