面向第三方开发者的接口接入指南:鉴权签名、密钥分级、调用示例与限流说明。所有 /api/v1/* 请求都需要 HMAC-SHA256 签名鉴权。

1. 鉴权签名(HMAC-SHA256)

请求需携带 4 个请求头,并在 body 上计算签名。签名过程:

  • 将 POST body 解析为 JSON 对象,然后 按键名排序(ksort),拼成 k=v&k2=v2 的查询字符串(调用示例见下)。
  • 拼接 payload = app_key + "\n" + timestamp + "\n" + nonce + "\n" + canonicalBody
  • 计算 X-Sign = HMAC-SHA256(payload, app_secret)(分级密钥则用该密钥的 secret)

公共请求头

Header说明
X-App-Key应用 Key(主密钥或分级密钥的 key_value)
X-TimestampUnix 秒级时间戳,服务端校验 ±300s 窗口(防重放)
X-Nonce一次性随机串(UUID/随机数),重复使用会被拒绝
X-Sign上述 HMAC-SHA256 签名,十六进制小写

响应结构

统一 JSON:{ "ok": true/false, "code": 200, "msg": "...", "data": {...} }。写操作成功返回 X-Resp-Sign 头(对返回体做 HMAC-SHA256,供客户端防篡改)。HTTP 状态码与业务码在部分端点可能分离(业务失败也可能返回 HTTP 200 + code 非 0),请以响应体 ok/code 为准。

2. 密钥分级(M4)

级别管理范围代表能力典型场景
read只读verify、heartbeat、check-version、cloud/var/get 等读操作服务端例行状态上报
write读写read 全部 + activate、trial/start、quick/auth、lease、cloud/function/call 等写操作正式业务客户端
manage管理读写全部 + offline/issue、版本下发等管理端点;等价主密钥权限后台/运营脚本
master产品主密钥products.app_key / app_secret,management 全权限,向后兼容兼容旧 SDK

任一密钥权限不满足端点所需最低级别时返回 403(code=200)「该 API 密钥权限不足」。分级密钥在后台「系统 → API 密钥分级」管理与生成;主密钥即产品管理页中的 AppKey/AppSecret。

3. 限流说明

层级阈值窗口对象说明
入口限流600 次60 秒每 IP先于签名鉴权,拦截未签名洪泛;阈值可用 api_entry_rate_limit 配置调整
读操作限流300 次60 秒每 IPverify / heartbeat 等高频读端点
写操作限流30 次60 秒每 IPactivate / trial / lease 等低频写端点

超限时返回错误(业务码非 0,提示请求频率超限)。常见触发原因:客户端未缓存验证结果而高频轮询。建议客户端做本地结果缓存 + 退避重试,正常使用远低于阈值。

4. 调用示例:激活卡密(POST /api/v1/activate)

curl

BODY='{"card_code":"AAAA-BBBB-CCCC","device_id":"DEV-001","fingerprint":"fp123"}'
# 注意:canonical 需按 key 排序后拼装
TS=$(date +%s); NONCE=$(uuidgen)
CANON=$(python3 -c "import json,sys,urllib.parse;d=json.loads(sys.argv[1]);print(urllib.parse.urlencode(sorted(d.items())))" "$BODY")
PAYLOAD="${APP_KEY}
${TS}
${NONCE}
${CANON}"
SIGN=$(printf "%b" "$PAYLOAD" | openssl dgst -sha256 -hmac "$APP_SECRET" | awk '{print $2}')
curl -sX POST https://your-domain/api/v1/activate \
  -H "Content-Type: application/json" \
  -H "X-App-Key: $APP_KEY" -H "X-Timestamp: $TS" -H "X-Nonce: $NONCE" -H "X-Sign: $SIGN" \
  -d "$BODY"

PHP

function sign(string $secret, string $appKey, int $ts, string $nonce, array $body): array {
    ksort($body);
    $canonical = http_build_query($body);          // 键排序 + RFC1738 编码
    $payload = $appKey."\n".$ts."\n".$nonce."\n".$canonical;
    $sign = hash_hmac('sha256', $payload, $secret);
    return ['ts'=>$ts,'nonce'=>$nonce,'sign'=>$sign,'canonical'=>$canonical];
}

Python

import hashlib, hmac, json, time, uuid, urllib.parse
def sign(secret, app_key, body):
    ts, nonce = int(time.time()), str(uuid.uuid4())
    canonical = urllib.parse.urlencode(sorted(body.items()))  # 键排序
    payload = f"{app_key}\n{ts}\n{nonce}\n{canonical}"
    sign = hmac.new(secret.encode(), payload.encode(), hashlib.sha256).hexdigest()
    return ts, nonce, sign

Go

func sign(secret, appKey, ts, nonce string, body map[string]any) string {
    keys := make([]string, 0, len(body))
    for k := range body { keys = append(keys, k) }
    sort.Strings(keys)
    q := make(url.Values)
    for _, k := range keys { q.Set(k, fmt.Sprint(body[k])) }
    payload := appKey + "\n" + ts + "\n" + nonce + "\n" + q.Encode()
    mac := hmac.New(sha256.New, []byte(secret))
    _, _ = io.WriteString(mac, payload)
    return hex.EncodeToString(mac.Sum(nil))
}

Node.js

const crypto = require('crypto');
function sign(secret, appKey, ts, nonce, body) {
  const canonical = Object.keys(body).sort().map(k => `${k}=${encodeURIComponent(body[k])}`).join('&');
  const payload = `${appKey}\n${ts}\n${nonce}\n${canonical}`;
  return crypto.createHmac('sha256', secret).update(payload).digest('hex');
}

5. 常见返回与处理

情况响应客户端处理
缺鉴权头 / 时间戳过期 / 重复 nonce / 签名错误HTTP 401检查时间同步、nonce 唯一性、签名算法与密钥
密钥禁用 / 产品停用401联系运营重新签发密钥
密钥权限不足403换用更高级别密钥或主密钥
频率超限非 0 业务码本地退避重试,避免放大
业务失败(卡密无效/设备已绑定/已过期等)HTTP 200 + ok:false + code按 code 提示用户对应文案

完整端点定义请查看 OpenAPI 3.0 JSON。各语言官方 SDK 已内置上述签名逻辑,直接调用即可,无需手工签名。