package com.qiandun.openapi.test;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.qiandun.openapi.demo.utils.JsonUtils;
import okhttp3.*;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.stream.Collectors;
/**
* @BelongsProject: saas-manage
* @BelongsPackage: com.qiandun.generator
* @Author: SQQ
* @CreateTime: 2026/1/2312:47
* @Description: TODO
* @Version: 1.0
*/
public class Test {
// 签盾APP凭证 - 需替换为你的实际值
private static final String APP_KEY = "签盾官网开发平台申请";
private static final String APP_SECRET = "在应用管理中获取";
// private static final String APP_KEY = "";
//
// private static final String APP_SECRET = "";
// 默认算法
private static final String SIGNATURE_METHOD = "HmacSHA256";
public static void main(String[] args) {
try {
// 示例:调用一个查询接口 pre 测试环境
String url = "https://api.pre-qiandun365.com/api/open/psnCert/2element";
//生产环境
// String url = "https://api.qiandun365.com/api/open/psnCert/2element";
// // GET请求示例
// String getResult = sendGetRequest(url);
// System.out.println("GET响应结果: " + getResult);
// POST请求示例(JSON格式)
Map<String, Object> postData = new HashMap<>();
postData.put("psnName", "姓名");
postData.put("psnIDCardNum", "身份证号");
String postResult = sendPostRequest(url, postData);
System.out.println("POST响应结果: " + JsonUtils.getMapper().readTree(postResult).get("result"));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 发送GET请求
*/
public static String sendGetRequest(String apiUrl) throws Exception {
return sendRequest(apiUrl, "GET", null, null);
}
/**
* 发送POST请求(JSON格式)
*/
public static String sendPostRequest(String apiUrl, Map<String, Object> bodyParams) throws Exception {
// 设置请求头
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/json; charset=utf-8");
headers.put("Accept", "application/json");
// 将请求体转换为JSON字符串
String bodyJson = mapToJson(bodyParams);
return sendRequest(apiUrl, "POST", bodyJson, headers);
}
/**
* 通用请求方法
*/
public static String sendRequest(String apiUrl, String httpMethod,
String requestBody, Map<String, String> customHeaders) throws Exception {
// 1. 准备基础请求头
Map<String, String> allHeaders = new HashMap<>();
// 必需头信息
String timestamp = String.valueOf(System.currentTimeMillis());
String nonce = UUID.randomUUID().toString();
// allHeaders.put("Date", getGMTTime());
allHeaders.put("Accept", "application/json");
allHeaders.put("X-Ca-Key", APP_KEY);
allHeaders.put("X-Ca-Timestamp", timestamp);
allHeaders.put("X-Ca-Nonce", nonce);
allHeaders.put("X-Ca-Signature-Method", SIGNATURE_METHOD);
// 添加自定义头
if (customHeaders != null) {
allHeaders.putAll(customHeaders);
}
// 2. 计算Content-MD5(如果有请求体)
String contentMD5 = "";
if (requestBody != null && !requestBody.isEmpty()) {
contentMD5 = calculateContentMD5(requestBody);
allHeaders.put("Content-MD5", contentMD5);
}
// 3. 解析URL,获取路径和查询参数
URL url = new URL(apiUrl);
String path = url.getPath();
String query = url.getQuery();
// 4. 构建参数映射(查询参数 + 表单参数)
Map<String, String> paramMap = new HashMap<>();
// 解析查询参数
if (query != null && !query.isEmpty()) {
String[] pairs = query.split("&");
for (String pair : pairs) {
String[] kv = pair.split("=");
if (kv.length == 2) {
paramMap.put(kv[0], kv[1]);
} else if (kv.length == 1) {
paramMap.put(kv[0], "");
}
}
}
// 5. 构造签名串
String stringToSign = buildStringToSign(httpMethod, allHeaders, path, paramMap);
// System.out.println("待签名字符串:\n" + stringToSign);
// 6. 计算签名
String signature = calculateSignature(stringToSign, APP_SECRET);
allHeaders.put("X-Ca-Signature", signature);
// 7. 设置参与签名的Header列表
List<String> signedHeaders = allHeaders.keySet().stream()
.filter(key -> key.startsWith("X-Ca-"))
.sorted()
.collect(Collectors.toList());
signedHeaders.remove("X-Ca-Signature");
if (!signedHeaders.isEmpty()) {
String signatureHeaders = String.join(",", signedHeaders);
allHeaders.put("X-Ca-Signature-Headers", signatureHeaders);
}
// 8. 发送HTTP请求
return executeHttpRequest(apiUrl, httpMethod, requestBody, allHeaders);
}
/**
* 构造待签名字符串
*/
private static String buildStringToSign(String httpMethod, Map<String, String> headers,
String path, Map<String, String> paramMap) {
StringBuilder sb = new StringBuilder();
// 1. HTTP方法
sb.append(httpMethod.toUpperCase()).append("\n");
// 2. Accept头
sb.append(headers.getOrDefault("Accept", "")).append("\n");
// 3. Content-MD5
sb.append(headers.getOrDefault("Content-MD5", "")).append("\n");
// 4. Content-Type
sb.append(headers.getOrDefault("Content-Type", "")).append("\n");
// 5. Date头
sb.append(headers.getOrDefault("Date", "")).append("\n");
// 6. Headers部分(以X-Ca-开头的自定义头)
List<String> headerKeys = headers.keySet().stream()
.filter(key -> key.startsWith("X-Ca-"))
.filter(key -> !"X-Ca-Signature".equals(key))
.filter(key -> !"X-Ca-Signature-Headers".equals(key))
.sorted()
.collect(Collectors.toList());
for (String key : headerKeys) {
sb.append(key).append(":").append(headers.get(key)).append("\n");
}
// 7. PathAndParameters
sb.append(buildPathAndParameters(path, paramMap));
return sb.toString();
}
/**
* 构建PathAndParameters部分
*/
private static String buildPathAndParameters(String path, Map<String, String> paramMap) {
StringBuilder sb = new StringBuilder();
sb.append(path);
if (!paramMap.isEmpty()) {
// 对参数Key进行字典序排序
List<String> sortedKeys = new ArrayList<>(paramMap.keySet());
Collections.sort(sortedKeys);
sb.append("?");
for (int i = 0; i < sortedKeys.size(); i++) {
String key = sortedKeys.get(i);
String value = paramMap.get(key);
if (i > 0) {
sb.append("&");
}
sb.append(key);
if (value != null && !value.isEmpty()) {
sb.append("=").append(value);
}
}
}
return sb.toString();
}
/**
* 计算签名
*/
private static String calculateSignature(String stringToSign, String appSecret) throws Exception {
Mac mac = Mac.getInstance(SIGNATURE_METHOD);
byte[] secretBytes = appSecret.getBytes(StandardCharsets.UTF_8);
SecretKeySpec secretKey = new SecretKeySpec(secretBytes, SIGNATURE_METHOD);
mac.init(secretKey);
byte[] signBytes = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
return Base64.encodeBase64String(signBytes);
}
/**
* 计算Content-MD5
*/
private static String calculateContentMD5(String content) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(content.getBytes(StandardCharsets.UTF_8));
return Base64.encodeBase64String(digest);
}
/**
* 获取GMT格式时间
*/
private static String getGMTTime() {
// 简化实现,实际使用时建议使用更精确的GMT时间格式
Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(TimeZone.getTimeZone("GMT"));
return String.format("%1$tA, %1$td %1$tb %1$tY %1$tT GMT", calendar);
}
/**
* 执行HTTP请求
*/
private static String executeHttpRequest(String urlStr, String method,
String body, Map<String, String> headers) throws Exception {
HttpURLConnection conn = null;
try {
URL url = new URL(urlStr);
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod(method);
conn.setConnectTimeout(15000);
conn.setReadTimeout(30000);
// 设置请求头
for (Map.Entry<String, String> entry : headers.entrySet()) {
conn.setRequestProperty(entry.getKey(), entry.getValue());
}
// 发送请求体(POST/PUT等)
if (body != null && !body.isEmpty() &&
("POST".equals(method) || "PUT".equals(method) || "PATCH".equals(method))) {
conn.setDoOutput(true);
try (OutputStream os = conn.getOutputStream()) {
byte[] input = body.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
}
}
// 获取响应
int responseCode = conn.getResponseCode();
// System.out.println("响应码: " + responseCode);
// 读取响应头(可用于调试)
Map<String, List<String>> responseHeaders = conn.getHeaderFields();
for (Map.Entry<String, List<String>> entry : responseHeaders.entrySet()) {
if (entry.getKey() != null) {
// System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
// 读取响应体
StringBuilder response = new StringBuilder();
try (BufferedReader br = new BufferedReader(
new InputStreamReader(
(responseCode >= 200 && responseCode < 300) ?
conn.getInputStream() : conn.getErrorStream(),
StandardCharsets.UTF_8))) {
String line;
while ((line = br.readLine()) != null) {
response.append(line);
}
}
return response.toString();
} finally {
if (conn != null) {
conn.disconnect();
}
}
}
/**
* 将Map转换为JSON字符串(简单实现)
*/
private static String mapToJson(Map<String, Object> map) {
if (map == null || map.isEmpty()) {
return "{}";
}
StringBuilder sb = new StringBuilder();
sb.append("{");
List<String> entries = map.entrySet().stream()
.map(entry -> {
String key = "\"" + entry.getKey() + "\"";
Object value = entry.getValue();
if (value instanceof String) {
return key + ":\"" + value + "\"";
} else if (value instanceof Number || value instanceof Boolean) {
return key + ":" + value;
} else {
return key + ":\"" + value.toString() + "\"";
}
})
.collect(Collectors.toList());
sb.append(String.join(",", entries));
sb.append("}");
return sb.toString();
}
}