# HMAC 签名认证
# 认证方式
包车对外标准接口采用 HMAC-SHA256 签名认证方式,保证请求来源可信,防止重放攻击。
# 认证凭证
接入前需要向嘟嘟巴士申请以下凭证:
- partner_id:合作伙伴标识(如
hellobike) - access_key:访问密钥标识
- secret_key:签名密钥(请妥善保管,不要泄露)
# 签名流程
# 1. 构造请求头
每个请求必须包含以下 HTTP 请求头:
Content-Type: application/json
Authorization: HMAC-SHA256 Credential={access_key}, SignedHeaders={signed_headers}, Signature={signature}
X-Dudu-Partner-Id: {partner_id}
X-Dudu-Timestamp: {timestamp}
X-Dudu-Nonce: {nonce}
X-Dudu-Content-SHA256: {content_sha256}
字段说明:
| 字段 | 说明 | 示例 |
|---|---|---|
| Authorization | 签名认证信息 | HMAC-SHA256 Credential=abc123, SignedHeaders=x-dudu-content-sha256;x-dudu-nonce;x-dudu-partner-id;x-dudu-timestamp, Signature=xxx |
| X-Dudu-Partner-Id | 合作伙伴标识 | hellobike |
| X-Dudu-Timestamp | 请求时间戳(ISO8601 格式) | 2026-05-20T09:00:00Z |
| X-Dudu-Nonce | 32位随机字符串(防重放) | a1b2c3d4e5f6... |
| X-Dudu-Content-SHA256 | 请求体的 SHA256 哈希值 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 |
# 2. 计算 Content-SHA256
const crypto = require('crypto');
const body = JSON.stringify(requestBody);
const contentSha256 = crypto.createHash('sha256').update(body).digest('hex');
# 3. 构造规范化请求(Canonical Request)
POST
{uri_path}
{query_string}
{canonical_headers}
{signed_headers}
{content_sha256}
示例:
POST
/api/charter/partner_create_order
x-dudu-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-dudu-nonce:a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
x-dudu-partner-id:hellobike
x-dudu-timestamp:2026-05-20T09:00:00Z
x-dudu-content-sha256;x-dudu-nonce;x-dudu-partner-id;x-dudu-timestamp
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
注意事项:
- 每行以
\n换行 - canonical_headers 按字母序排列,格式为
header-name:header-value\n - signed_headers 是参与签名的 header 名称列表,用
;分隔,按字母序排列 - query_string 为空时留空行
# 4. 构造待签字符串(String to Sign)
HMAC-SHA256
{timestamp}
{sha256(canonical_request)}
示例:
HMAC-SHA256
2026-05-20T09:00:00Z
7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b
# 5. 计算签名
const crypto = require('crypto');
const signature = crypto
.createHmac('sha256', secret_key)
.update(stringToSign)
.digest('hex');
# 6. 组装 Authorization 头
HMAC-SHA256 Credential={access_key}, SignedHeaders={signed_headers}, Signature={signature}
# 完整示例(Node.js)
const crypto = require('crypto');
const axios = require('axios');
// 认证凭证
const partner_id = 'hellobike';
const access_key = 'your_access_key';
const secret_key = 'your_secret_key';
// 请求参数
const url = 'http://apitest.dudubashi.com/api/charter/partner_car_type_list';
const body = {
city_name: '深圳市'
};
// 1. 生成时间戳和随机数
const timestamp = new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
const nonce = crypto.randomBytes(16).toString('hex');
// 2. 计算 Content-SHA256
const bodyStr = JSON.stringify(body);
const contentSha256 = crypto.createHash('sha256').update(bodyStr).digest('hex');
// 3. 构造规范化请求
const path = new URL(url).pathname;
const canonicalHeaders = [
`x-dudu-content-sha256:${contentSha256}`,
`x-dudu-nonce:${nonce}`,
`x-dudu-partner-id:${partner_id}`,
`x-dudu-timestamp:${timestamp}`
].join('\n');
const signedHeaders = 'x-dudu-content-sha256;x-dudu-nonce;x-dudu-partner-id;x-dudu-timestamp';
const canonicalRequest = [
'POST',
path,
'',
canonicalHeaders,
'',
signedHeaders,
contentSha256
].join('\n');
// 4. 构造待签字符串
const canonicalRequestHash = crypto.createHash('sha256').update(canonicalRequest).digest('hex');
const stringToSign = [
'HMAC-SHA256',
timestamp,
canonicalRequestHash
].join('\n');
// 5. 计算签名
const signature = crypto.createHmac('sha256', secret_key).update(stringToSign).digest('hex');
// 6. 发送请求
const authorization = `HMAC-SHA256 Credential=${access_key}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
axios.post(url, body, {
headers: {
'Content-Type': 'application/json',
'Authorization': authorization,
'X-Dudu-Partner-Id': partner_id,
'X-Dudu-Timestamp': timestamp,
'X-Dudu-Nonce': nonce,
'X-Dudu-Content-SHA256': contentSha256
}
}).then(response => {
console.log(response.data);
}).catch(error => {
console.error(error.response?.data || error.message);
});
# 安全建议
- 妥善保管密钥:
secret_key不要硬编码在客户端代码中,应存储在服务端环境变量 - 时间同步:确保服务器时间准确,签名有效期为 5 分钟
- Nonce 唯一性:每次请求使用不同的随机数,防止重放攻击
- HTTPS 传输:生产环境建议使用 HTTPS 加密传输
# 常见错误
| 错误码 | 错误信息 | 解决方案 |
|---|---|---|
| 1001 | 缺少 Authorization 请求头 | 检查请求头是否完整 |
| 1001 | 签名验证失败 | 检查签名计算流程,确认 secret_key 正确 |
| 1001 | 请求已过期或时间偏差过大 | 同步服务器时间,确保时间戳准确 |
| 1001 | nonce 已使用,疑似重放攻击 | 每次请求使用新的随机数 |
| 1001 | 请求体哈希不匹配 | 检查 Content-SHA256 计算是否正确 |