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 params, Map 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 params) { if (params == null || params.isEmpty()) { return ""; } // 按参数名ASCII升序排序 TreeMap sortedParams = new TreeMap<>(params); StringBuilder sb = new StringBuilder(); try { for (Map.Entry 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")); } }