3 changed files with 162 additions and 8 deletions
@ -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<String, String> 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("========================================"); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue