认证机制
本文档详细介绍 Zanbara API 的认证机制,包括 API Key 生成、JWT Token 获取、请求签名算法以及安全最佳实践。
认证概述
Zanbara API 使用两层认证机制:
JWT Token 认证 (必需): 用于验证用户身份
HMAC-SHA256 签名 (可选): 用于防止请求篡改和重放攻击
所有私有 API 端点都需要有效的 JWT Token。对于高安全性要求的场景 (如提现操作),还需要额外的请求签名验证。
认证流程图
┌─────────────┐
│ 生成 API │
│ Key/Secret │
└──────┬──────┘
│
▼
┌─────────────┐
│ 请求 JWT │
│ Token │
└──────┬──────┘
│
▼
┌─────────────┐
│ 使用 Token │
│ 调用 API │
└──────┬──────┘
│
▼
┌─────────────┐
│ (可选) 签名 │
│ 请求 │
└─────────────┘第一步: 生成 API Key
在网页端生成
登录 Zanbara 账户: https://zanbarax.com
访问 "账户设置" → "API 管理"
点击 "创建 API Key"
设置 API Key 权限和备注
权限选项:
只读: 仅能查询账户、订单、仓位信息
交易: 可以下单、撤单、调整仓位
提现: 可以发起提现请求 (需要额外验证)
(可选) 配置 IP 白名单
点击 "确认创建"
保存 API 凭证
创建成功后,您将看到:
API Key: pk_live_1234567890abcdef1234567890abcdef
API Secret: sk_live_1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab重要提示:
API Secret 仅在创建时显示一次,请务必妥善保存
如果遗失 Secret,需要删除旧 Key 并创建新的
切勿将 Secret 提交到版本控制系统或公开仓库
API Key 格式
API Key: pk_{env}_{32_hex_chars}
API Secret: sk_{env}_{64_hex_chars}env: 环境标识live- 生产环境test- 测试环境
后续为十六进制字符串
第二步: 获取 JWT Token
使用 API Key 和 Secret 向认证端点请求 JWT Token。
端点信息
POST /v1/auth/token请求参数
{
"api_key": "pk_live_1234567890abcdef1234567890abcdef",
"api_secret": "sk_live_1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab"
}请求示例
cURL
curl -X POST https://api.zanbarax.com/v1/auth/token \
-H "Content-Type: application/json" \
-d '{
"api_key": "pk_live_1234567890abcdef1234567890abcdef",
"api_secret": "sk_live_1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab"
}'JavaScript
const response = await fetch('https://api.zanbarax.com/v1/auth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
api_key: 'pk_live_1234567890abcdef1234567890abcdef',
api_secret: 'sk_live_1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab'
})
});
const data = await response.json();
console.log('JWT Token:', data.data.token);Python
import requests
import json
response = requests.post(
'https://api.zanbarax.com/v1/auth/token',
headers={'Content-Type': 'application/json'},
data=json.dumps({
'api_key': 'pk_live_1234567890abcdef1234567890abcdef',
'api_secret': 'sk_live_1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab'
})
)
data = response.json()
print('JWT Token:', data['data']['token'])Rust
use reqwest;
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = reqwest::Client::new();
let response = client
.post("https://api.zanbarax.com/v1/auth/token")
.json(&json!({
"api_key": "pk_live_1234567890abcdef1234567890abcdef",
"api_secret": "sk_live_1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab"
}))
.send()
.await?;
let data: serde_json::Value = response.json().await?;
println!("JWT Token: {}", data["data"]["token"]);
Ok(())
}响应格式
{
"success": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMzQ1Njc4OTAiLCJleHAiOjE2OTY3NTU2MDAsImlhdCI6MTY5Njc1MjAwMCwiaXNzIjoicGVyc GRleC1leGNoYW5nZSJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
"expires_in": 3600,
"token_type": "Bearer"
},
"timestamp": 1696752000000
}字段说明:
token: JWT Token 字符串expires_in: Token 有效期 (秒),默认 3600 (1 小时)token_type: Token 类型,固定为 "Bearer"
JWT Token 结构
Zanbara 使用标准 JWT (RFC 7519) 格式:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 ← Header (Base64)
.
eyJzdWIiOiJ1c2VyXzEyMzQ1Njc4OTAi... ← Payload (Base64)
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk... ← Signature (HMAC-SHA256)Header:
{
"alg": "HS256",
"typ": "JWT"
}Payload:
{
"sub": "user_1234567890", // User ID
"exp": 1696755600, // 过期时间 (Unix timestamp)
"iat": 1696752000, // 签发时间
"iss": "zanbara-exchange" // 签发者
}Token 刷新
Token 有效期为 1 小时。建议在 Token 过期前主动刷新:
class TokenManager {
constructor(apiKey, apiSecret) {
this.apiKey = apiKey;
this.apiSecret = apiSecret;
this.token = null;
this.expiresAt = 0;
}
async getToken() {
// 如果 Token 还有 5 分钟以上有效期,直接返回
if (this.token && Date.now() < this.expiresAt - 300000) {
return this.token;
}
// 否则获取新 Token
const response = await fetch('https://api.zanbarax.com/v1/auth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
api_key: this.apiKey,
api_secret: this.apiSecret
})
});
const data = await response.json();
this.token = data.data.token;
this.expiresAt = Date.now() + data.data.expires_in * 1000;
return this.token;
}
}
// 使用示例
const tokenManager = new TokenManager(API_KEY, API_SECRET);
const token = await tokenManager.getToken();第三步: 使用 Token 调用 API
在所有私有 API 请求中,通过 Authorization 请求头传递 JWT Token。
请求头格式
Authorization: Bearer {JWT_TOKEN}示例: 查询账户余额
curl https://api.zanbarax.com/v1/account/balance \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."示例: 创建订单
curl -X POST https://api.zanbarax.com/v1/order/place \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{
"symbol": "SOL-PERP",
"side": "buy",
"type": "limit",
"quantity": "10",
"price": "150.5"
}'认证失败处理
当 Token 无效、过期或缺失时,API 返回 401 Unauthorized:
{
"success": false,
"error": {
"code": "AUTHENTICATION_FAILED",
"message": "Invalid or expired JWT token"
},
"timestamp": 1696752000000
}常见原因:
Token 已过期
Token 签名无效
Authorization 头格式错误
API Key 已被禁用或删除
处理建议:
检查 Token 是否过期,如果过期则重新获取
确认 Authorization 头格式为
Bearer {token}确认 API Key 状态正常
请求签名 (高级安全)
对于高安全性要求的操作 (如提现),Zanbara 支持 HMAC-SHA256 请求签名。
何时需要签名
以下操作必须包含签名:
提现请求 (
POST /v1/account/withdraw)修改 API Key 权限
删除 API Key
其他操作签名为可选,但强烈推荐用于:
所有交易操作 (下单、撤单)
修改杠杆、保证金模式
批量操作
签名算法
步骤 1: 构造待签名字符串
{timestamp}{method}{path}{body}timestamp: 当前 Unix 时间戳 (毫秒)method: HTTP 方法,大写 (GET, POST, DELETE 等)path: API 路径,包括查询参数body: 请求体 JSON 字符串 (GET 请求为空字符串)
示例:
对于请求:
POST /v1/order/place
Body: {"symbol":"SOL-PERP","side":"buy","type":"limit","quantity":"10","price":"150.5"}待签名字符串:
1696752000000POST/v1/order/place{"symbol":"SOL-PERP","side":"buy","type":"limit","quantity":"10","price":"150.5"}步骤 2: 使用 HMAC-SHA256 签名
使用 API Secret 作为密钥,对待签名字符串进行 HMAC-SHA256 签名:
signature = HMAC-SHA256(api_secret, sign_string)步骤 3: 将签名转换为十六进制字符串
hex_signature = hex(signature)签名请求头
Authorization: Bearer {JWT_TOKEN}
X-API-Key: {API_KEY}
X-API-Signature: {HEX_SIGNATURE}
X-API-Timestamp: {TIMESTAMP}代码示例
JavaScript
const crypto = require('crypto');
function signRequest(apiSecret, method, path, body, timestamp) {
const bodyStr = body ? JSON.stringify(body) : '';
const signString = `${timestamp}${method}${path}${bodyStr}`;
const signature = crypto
.createHmac('sha256', apiSecret)
.update(signString)
.digest('hex');
return signature;
}
// 使用示例
const timestamp = Date.now();
const method = 'POST';
const path = '/v1/order/place';
const body = {
symbol: 'SOL-PERP',
side: 'buy',
type: 'limit',
quantity: '10',
price: '150.5'
};
const signature = signRequest(API_SECRET, method, path, body, timestamp);
const response = await fetch('https://api.zanbarax.com/v1/order/place', {
method: 'POST',
headers: {
'Authorization': `Bearer ${jwtToken}`,
'X-API-Key': API_KEY,
'X-API-Signature': signature,
'X-API-Timestamp': timestamp.toString(),
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
});Python
import hmac
import hashlib
import time
import json
def sign_request(api_secret, method, path, body, timestamp):
body_str = json.dumps(body) if body else ''
sign_string = f"{timestamp}{method}{path}{body_str}"
signature = hmac.new(
api_secret.encode('utf-8'),
sign_string.encode('utf-8'),
hashlib.sha256
).hexdigest()
return signature
# 使用示例
timestamp = int(time.time() * 1000)
method = 'POST'
path = '/v1/order/place'
body = {
'symbol': 'SOL-PERP',
'side': 'buy',
'type': 'limit',
'quantity': '10',
'price': '150.5'
}
signature = sign_request(API_SECRET, method, path, body, timestamp)
response = requests.post(
'https://api.zanbarax.com/v1/order/place',
headers={
'Authorization': f'Bearer {jwt_token}',
'X-API-Key': API_KEY,
'X-API-Signature': signature,
'X-API-Timestamp': str(timestamp),
'Content-Type': 'application/json'
},
json=body
)Rust
use hmac::{Hmac, Mac};
use sha2::Sha256;
use hex;
type HmacSha256 = Hmac<Sha256>;
fn sign_request(
api_secret: &str,
method: &str,
path: &str,
body: Option<&str>,
timestamp: i64
) -> String {
let body_str = body.unwrap_or("");
let sign_string = format!("{}{}{}{}", timestamp, method, path, body_str);
let mut mac = HmacSha256::new_from_slice(api_secret.as_bytes())
.expect("HMAC can take key of any size");
mac.update(sign_string.as_bytes());
let result = mac.finalize();
hex::encode(result.into_bytes())
}
// 使用示例
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as i64;
let method = "POST";
let path = "/v1/order/place";
let body = r#"{"symbol":"SOL-PERP","side":"buy","type":"limit","quantity":"10","price":"150.5"}"#;
let signature = sign_request(API_SECRET, method, path, Some(body), timestamp);
let response = client
.post("https://api.zanbarax.com/v1/order/place")
.header("Authorization", format!("Bearer {}", jwt_token))
.header("X-API-Key", API_KEY)
.header("X-API-Signature", signature)
.header("X-API-Timestamp", timestamp.to_string())
.header("Content-Type", "application/json")
.body(body)
.send()
.await?;签名验证失败
当签名验证失败时,API 返回 401 Unauthorized:
{
"success": false,
"error": {
"code": "INVALID_SIGNATURE",
"message": "Request signature verification failed"
},
"timestamp": 1696752000000
}常见原因:
时间戳与服务器时间相差超过 5 秒
签名字符串构造错误
使用错误的 API Secret
请求体格式不一致 (空格、换行等)
调试建议:
打印待签名字符串,确认格式正确
确认时间戳单位为毫秒
确认请求体 JSON 格式与签名时一致 (不含额外空格)
使用测试端点验证签名算法
安全最佳实践
1. 保护 API 凭证
推荐做法:
// 使用环境变量
const API_KEY = process.env.ZANBARA_API_KEY;
const API_SECRET = process.env.ZANBARA_API_SECRET;避免做法:
// 切勿硬编码
const API_KEY = 'pk_live_1234567890abcdef1234567890abcdef';
const API_SECRET = 'sk_live_1234567890...'; // 危险!2. 使用不同权限的 API Key
策略机器人: 只读权限
交易机器人: 交易权限
后台管理: 提现权限 (需额外签名)3. 配置 IP 白名单
在 API Key 设置中限制允许的 IP 地址:
服务器 IP: 203.0.113.10
办公室网络: 198.51.100.0/244. 定期轮换 API Key
建议每 90 天轮换一次 API Key:
// 同时支持新旧两个 Key,平滑过渡
const PRIMARY_KEY = process.env.ZANBARA_API_KEY_PRIMARY;
const BACKUP_KEY = process.env.ZANBARA_API_KEY_BACKUP;
async function callAPI(endpoint, data) {
try {
return await callWithKey(PRIMARY_KEY, endpoint, data);
} catch (error) {
if (error.code === 'AUTHENTICATION_FAILED') {
// 主 Key 失败,尝试备用 Key
return await callWithKey(BACKUP_KEY, endpoint, data);
}
throw error;
}
}5. 监控 API Key 使用
定期检查 API Key 使用日志:
异常 IP 访问
异常时间段活动
失败请求激增
6. 及时吊销泄露的 Key
如果怀疑 API Key 泄露:
立即在网页端禁用该 Key
创建新的 API Key
更新应用程序配置
检查账户异常活动
7. 使用 HTTPS
所有 API 请求必须使用 HTTPS:
✓ https://api.zanbarax.com/v1/...
✗ http://api.zanbarax.com/v1/... (将被拒绝)8. 实现请求重放保护
使用 client_order_id 防止订单重复:
function createOrder(params) {
return {
...params,
client_order_id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
};
}9. 限制 Token 作用域
如果可能,为不同环境使用不同的 API Key:
开发环境: 使用测试网 API Key
生产环境: 使用生产网 API Key,严格访问控制故障排查
问题 1: "Invalid API Key"
原因:
API Key 格式错误
API Key 已被删除或禁用
使用了测试环境 Key 访问生产环境
解决方法:
检查 API Key 格式:
pk_{env}_{32_hex_chars}在网页端确认 Key 状态
确认环境匹配 (test/live)
问题 2: "Token Expired"
原因:
JWT Token 超过 1 小时有效期
解决方法:
// 实现自动刷新
if (error.code === 'TOKEN_EXPIRED') {
token = await refreshToken();
return retryRequest();
}问题 3: "Timestamp Out of Range"
原因:
本地时钟与服务器时间相差超过 5 秒
解决方法:
# 同步系统时钟
ntpdate -u time.apple.com
# 或使用服务器时间
curl https://api.zanbarax.com/v1/system/time问题 4: "Signature Verification Failed"
原因:
签名算法实现错误
请求体被修改
时间戳不匹配
解决方法:
打印待签名字符串,对比预期格式
确认签名和请求使用相同的 body
确认时间戳一致
测试工具
签名测试端点
Zanbara 提供签名测试端点,用于验证签名算法:
POST /v1/auth/test-signature请求:
{
"timestamp": 1696752000000,
"method": "POST",
"path": "/v1/order/place",
"body": "{\"symbol\":\"SOL-PERP\"}",
"signature": "abc123..."
}响应:
{
"success": true,
"data": {
"signature_valid": true,
"expected_signature": "abc123...",
"your_signature": "abc123...",
"sign_string": "1696752000000POST/v1/order/place{\"symbol\":\"SOL-PERP\"}"
}
}Postman Collection
我们提供预配置的 Postman Collection,包含签名脚本:
相关文档
文档版本: v1.0.0 最后更新: 2025-10-07 维护: Zanbara API Team
Last updated