package com.ctg.ai.platform.demo.util; import org.apache.commons.lang3.RandomStringUtils; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.*; public class OpenApiUtil { private static final String KEY_SEPARATOR = "&"; /** ak 由管理员提供 */ private static final String HTTP_HEADER_YL_APPCODE = "YL-3rd-Appcode"; /** 时间戳 单位毫秒 */ private static final String HTTP_HEADER_YL_TIMESTAMP = "YL-Timestamp"; /** 8位随机数 */ private static final String HTTP_HEADER_YL_RANDOM = "YL-Random"; /** 签名 */ private static final String HTTP_HEADER_YL_SIGNATURE = "YL-Signature"; /** * 组装验签头部 * @param appKey * @param appSecret * @param paramMap * @return */ public static Map buildHeaders(String appKey, String appSecret, Map paramMap) { if (appKey == null || appSecret == null) { return null; } // 组装前置条件 Long timestamp = System.currentTimeMillis(); String random = RandomStringUtils.randomAlphanumeric(8); // 计算签名 String signature = buildSignature(appKey, appSecret, random, timestamp, paramMap); // 组装HTTP头部 return buildHeaders(appKey, random, timestamp, signature); } /** * 签名 * @param appKey * @param appSecret * @param random * @param timestamp * @param paramMap * @return */ private static String buildSignature(String appKey, String appSecret, String random, Long timestamp, Map paramMap) { if (appKey == null || appSecret == null || random == null || timestamp == null) { return null; } // 组成签名前原串 StringBuilder sb = new StringBuilder(); if (paramMap != null && paramMap.size() > 0) { List keys = new ArrayList<>(paramMap.keySet()); // 参数排序 (ASCII 升序) Collections.sort(keys); for (String key : keys) { String val = paramMap.get(key)[0]; sb.append(key).append("=").append(val).append(KEY_SEPARATOR); } } sb.append(appSecret).append(KEY_SEPARATOR) .append(timestamp).append(KEY_SEPARATOR) .append(random).append(KEY_SEPARATOR) .append(appKey); //System.out.println("text=\n"+sb); return sha256(sb.toString().getBytes(StandardCharsets.UTF_8)); } /** * 组装验签头部 * @param appKey * @param random * @param timestamp * @param signature * @return */ private static Map buildHeaders(String appKey, String random, Long timestamp, String signature) { Map headers = new HashMap<>(4); if (appKey != null) { headers.put(HTTP_HEADER_YL_APPCODE, appKey); } if (random != null) { headers.put(HTTP_HEADER_YL_RANDOM, random); } if (timestamp != null) { headers.put(HTTP_HEADER_YL_TIMESTAMP, ""+timestamp); } if (signature != null) { headers.put(HTTP_HEADER_YL_SIGNATURE, signature); } return headers; } // 哈希部分逻辑 private static final char[] LOWER_HEX_DIGITS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; private static final String MD5_ALGORITHM_NAME = "MD5"; private static final String SHA256_ALGORITHM_NAME = "SHA-256"; public static String md5(byte[] data) { return encodeAsHexString(MD5_ALGORITHM_NAME, data); } public static String sha256(byte[] data) { return encodeAsHexString(SHA256_ALGORITHM_NAME, data); } /** * 使用 {@code algorithmName} 加密算法编码 {@code data} * @param algorithmName 算法名 * @param data 被编码的字节数组 * @return {@code data} 编码后的小写 16 进制形式字符串 * @throw IllegalStateException 当找不到 {@code algorithmName} 对应算法 */ private static String encodeAsHexString(String algorithmName, byte[] data) { try { byte[] hash = MessageDigest.getInstance(algorithmName).digest(data); return new String(encodeHex(hash)); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("MessageDigest 找不到算法:" + algorithmName, e); } } private static char[] encodeHex(byte[] data) { int l = data.length; char[] arr = new char[l << 1]; int i = 0; for(int j = 0; i < l; ++i) { arr[j++] = LOWER_HEX_DIGITS[(240 & data[i]) >>> 4]; arr[j++] = LOWER_HEX_DIGITS[15 & data[i]]; } return arr; } public static void main(String[] args) { String ak = "ak"; String sk = "sk"; Map paramMap = new HashMap<>(2); paramMap.put("param2", new String[]{"456","789"}); paramMap.put("param1", new String[]{"123"}); Map header = buildHeaders(ak, sk, paramMap); System.out.println("header="+header); } }