环能聚合API使用HMAC-SHA256签名算法确保请求数据的完整性和安全性。每个请求都需要包含签名参数,服务器会验证签名的有效性。
参数名 | 是否必须 | 说明 | 示例 |
---|---|---|---|
app_key | 是 | 应用唯一标识 | "abc123xyz" |
timestamp | 是 | 当前时间戳(毫秒) | 1640995200000 |
nonce | 是 | 随机字符串(32位) | "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" |
version | 是 | API版本号 | "1.0" |
body | 否 | 请求体JSON字符串 | "{\"name\":\"张三\"}" |
signString = HTTP_METHOD + "\n" +
CONTENT_TYPE + "\n" +
TIMESTAMP + "\n" +
NONCE + "\n" +
REQUEST_URI + "\n" +
SORTED_QUERY_STRING + "\n" +
BODY_HASH
signature = HMAC-SHA256(signString, app_secret)
import hmac
import hashlib
import urllib.parse
def generate_signature(method, content_type, timestamp, nonce, uri, params, body, app_secret):
# 排序参数
sorted_params = sorted(params.items())
query_string = urllib.parse.urlencode(sorted_params)
# 计算body hash
body_hash = hashlib.sha256(body.encode('utf-8')).hexdigest()
# 构造签名串
sign_string = f"{method}\n{content_type}\n{timestamp}\n{nonce}\n{uri}\n{query_string}\n{body_hash}"
# 计算签名
signature = hmac.new(
app_secret.encode('utf-8'),
sign_string.encode('utf-8'),
hashlib.sha256
).hexdigest()
return signature
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.*;
public class SignGenerator {
public static String generateSignature(String method, String contentType,
String timestamp, String nonce, String uri,
Map params, String body,
String appSecret) throws Exception {
// 排序参数
List keys = new ArrayList<>(params.keySet());
Collections.sort(keys);
StringBuilder queryString = new StringBuilder();
for (String key : keys) {
if (queryString.length() > 0) queryString.append("&");
queryString.append(key).append("=")
.append(URLEncoder.encode(params.get(key), "UTF-8"));
}
// 计算body hash
MessageDigest md = MessageDigest.getInstance("SHA-256");
String bodyHash = bytesToHex(md.digest(body.getBytes(StandardCharsets.UTF_8)));
// 构造签名串
String signString = String.join("\n", method, contentType, timestamp,
nonce, uri, queryString.toString(), bodyHash);
// 计算签名
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(appSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
mac.init(secretKey);
return bytesToHex(mac.doFinal(signString.getBytes(StandardCharsets.UTF_8)));
}
private static String bytesToHex(byte[] bytes) {
StringBuilder result = new StringBuilder();
for (byte b : bytes) {
result.append(String.format("%02x", b));
}
return result.toString();
}
}
POST /api/v1/user/info HTTP/1.1
Host: api.huaneng.com
Content-Type: application/json
X-App-Key: abc123xyz
X-Timestamp: 1640995200000
X-Nonce: a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
X-Signature: 7d8f9e2a1b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1
{
"user_id": 12345
}
验证项 | 规则 | 时间窗口 | 错误码 |
---|---|---|---|
时间戳有效性 | 绝对值偏差 ≤ 5分钟 | ±300秒 | 4001 |
nonce唯一性 | 5分钟内不可重复 | 300秒 | 4002 |
签名有效期 | 生成后5分钟内有效 | 300秒 | 4003 |
错误码 | 错误描述 | 原因 | 解决方案 |
---|---|---|---|
4001 | 时间戳过期 | 时间偏差超过5分钟 | 校准本地时间 |
4002 | nonce重复 | nonce已被使用 | 生成新的nonce |
4003 | 签名无效 | 签名计算错误 | 检查签名计算步骤 |
4004 | app_key无效 | 应用不存在或已禁用 | 联系管理员获取有效app_key |
4005 | 签名算法不支持 | 使用了不支持的算法 | 使用HMAC-SHA256算法 |
调试工具 | 用途 | 推荐工具 |
---|---|---|
签名计算器 | 验证签名算法 | Postman、curl |
时间戳同步 | 校准本地时间 | ntpdate、timedatectl |
日志分析 | 排查签名问题 | ELK、Splunk |
单元测试 | 自动化验证 | pytest、JUnit |
A: 常见原因包括:时间戳不同步、参数排序错误、body格式不匹配、编码问题等。建议使用调试模式逐步验证每个步骤。
A: nonce必须是32位随机字符串,建议使用UUID或随机数生成器。每次请求必须使用不同的nonce。
A: GET请求没有body,body_hash计算为空字符串""的SHA256值。