package com.zy.kingdee.utils;
|
|
|
import com.smecloud.apigw.util.SHAUtil;
|
import lombok.extern.slf4j.Slf4j;
|
|
import javax.crypto.Mac;
|
import javax.crypto.spec.SecretKeySpec;
|
import java.net.URLEncoder;
|
import java.nio.charset.StandardCharsets;
|
import java.util.Base64;
|
import java.util.Map;
|
import java.util.TreeMap;
|
|
@Slf4j
|
public class KingDeeUtils {
|
public static String generateSignature(String method,
|
String path,
|
Map<String, String> params,
|
Map<String, String> headers,
|
String appSecret) throws Exception {
|
// 1. 处理请求方法(转大写)
|
String processedMethod = method.toUpperCase();
|
|
// 2. 处理路径(URL编码+大写)
|
String processedPath = encodePath(path);
|
|
// 3. 处理请求参数(双重URL编码+ASCII排序+大写)
|
String processedParams = processParams(params);
|
|
// 4. 处理请求头(提取特定参数)
|
String nonce = headers.getOrDefault("x-api-nonce", "");
|
String timestamp = headers.getOrDefault("x-api-timestamp", "");
|
|
// 5. 构建签名原文
|
String signSource = buildSignSource(processedMethod, processedPath, processedParams, nonce, timestamp);
|
|
log.info("签名原文:\n"+signSource);
|
// 6. 计算HMAC-SHA256签名
|
return hmacSha256(appSecret, signSource);
|
}
|
|
private static String encodePath(String path) {
|
try {
|
// 只对路径部分进行URL编码(不包含协议/域名)
|
String[] parts = path.split("://", 2);
|
String toEncode = parts.length > 1 ? parts[1].substring(parts[1].indexOf('/')) : path;
|
return URLEncoder.encode(toEncode, StandardCharsets.UTF_8.name()).toUpperCase();
|
} catch (Exception e) {
|
throw new RuntimeException("Path encoding failed", e);
|
}
|
}
|
|
private static String processParams(Map<String, String> params) {
|
if (params == null || params.isEmpty()) {
|
return "";
|
}
|
|
// 按参数名ASCII升序排序
|
TreeMap<String, String> sortedParams = new TreeMap<>(params);
|
StringBuilder sb = new StringBuilder();
|
|
try {
|
for (Map.Entry<String, String> entry : sortedParams.entrySet()) {
|
if (sb.length() > 0) {
|
sb.append('&');
|
}
|
// 双重URL编码(每次编码后转大写)
|
String key = doubleUrlEncode(entry.getKey());
|
String value = doubleUrlEncode(entry.getValue());
|
sb.append(key).append('=').append(value);
|
}
|
} catch (Exception e) {
|
throw new RuntimeException("Parameter encoding failed", e);
|
}
|
return sb.toString();
|
}
|
|
private static String doubleUrlEncode(String s) throws Exception {
|
if (s == null) return "";
|
String firstEncode = URLEncoder.encode(s, StandardCharsets.UTF_8.name());
|
return URLEncoder.encode(firstEncode, StandardCharsets.UTF_8.name());
|
}
|
|
private static String buildSignSource(String method,
|
String path,
|
String params,
|
String nonce,
|
String timestamp) {
|
return method + "\n" +
|
path + "\n" +
|
params + "\n" +
|
"x-api-nonce:" + nonce + "\n" +
|
"x-api-timestamp:" + timestamp + "\n"; // 注意结尾的换行符
|
}
|
|
private static String hmacSha256(String secret, String message) throws Exception {
|
Mac sha256 = Mac.getInstance("HmacSHA256");
|
SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
|
sha256.init(secretKey);
|
byte[] hash = sha256.doFinal(message.getBytes(StandardCharsets.UTF_8));
|
return Base64.getEncoder().encodeToString(hash);
|
}
|
|
private static String hmacSha256_16(String secret, String message) throws Exception {
|
String appSignature = SHAUtil.SHA256HMAC(message, secret);
|
appSignature = Base64.getEncoder().encodeToString(appSignature.getBytes());
|
return appSignature;
|
}
|
|
// 测试示例
|
public static void main(String[] args) throws Exception {
|
// 示例数据
|
|
|
System.out.println("X-Api-Signature: " + hmacSha256_16("abc123","abc"));
|
}
|
}
|