更新时间:2024-07-02 06:13:08下载pdf
为保证数据安全,在发起 API 调用请求时,您需要提供签名进行账号验证。本文主要介绍如何生成 API 签名。
API 签名采用 HMAC SHA256 方法创建摘要。对于 令牌管理 API 和 普通业务 API,签名算法稍有不同。
API 类型 | 令牌管理 API | 普通业务 API |
---|---|---|
适用范围 | 获取令牌和刷新令牌 API | 除令牌管理接口以外的其他 API |
签名算法 |
str = client_id + t + nonce + identifier + stringToSign sign = HMAC-SHA256(str, secret).toUpperCase() |
str = client_id + access_token + t + nonce + identifier + stringToSign sign = HMAC-SHA256(str, secret).toUpperCase() |
字段说明 |
|
|
字段说明 | 将 client_id 与当前请求的 13 位标准时间戳( t )、nonce、identifier、stringToSign 拼接成待签名字符串 str。再将 str 与 secret 进行 HMAC-SHA256 哈希摘要后转成大写,即可获取签名。 |
令牌管理 API 和普通业务 API 签名算法仅仅是在 str
生成逻辑上有所不同,后者比前者多了一个访问令牌(access_token
)参数的拼接。
stringToSign
签名字符串stringToSign
签名字符串结构如下:
String stringToSign=
HTTPMethod + "\n" +
Content-SHA256 + "\n" +
Optional_Signature_key + "\n" +
URL
总共包含四部分组成:
stringToSign
签名字符串是将这四部分通过换行符(\n)进行拼接得到。
HTTPMethod 指接口请求方法。如 GET、POST、PUT、DELETE 等。
Content-SHA256 指请求 Body 的 SHA256 值。只有当 Body 非 Form 表单时,才计算 SHA256。
计算方式如下:
String content-SHA256 = SHA256(bodyStream.getbytes("UTF-8")); //bodyStream 为字节数组
当 Body 为空时,也会进行加密,加密结果为 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
。
Optional_Signature_key 是指在 请求结构 中添加的、参与签名计算的字段(Key)和字段取值(Value)拼接的字符串。例如:
现有请求结构如下:
client_id: 1KAD46OrT9HafiKd****
sign: C5EFD19AD45E33A060C0BE47AEF65D975D54B2D70CBAA7A1ACA1A7D0E5C0****
sign_method: HMAC-SHA256
t: 1588925778000
access_token: 3f4eda2bdec17232f67c0b188af3****
nonce: 5138cc3a9033d69856923fd07b49****
...
为了提高安全性,指定两个开发者自定义的新字段(area_id
和 req_id
)参与验签:
area_id: 29a33e8796834b1****
req_id: 8afdb70ab2ed11eb85290242ac13****
则新的请求结构如下:
client_id: 1KAD46OrT9HafiKd****
sign: C5EFD19AD45E33A060C0BE47AEF65D975D54B2D70CBAA7A1ACA1A7D0E5C0****
sign_method: HMAC-SHA256
t: 1588925778000
access_token: 3f4eda2bdec17232f67c0b188af3****
nonce: 5138cc3a9033d69856923fd07b49****
...
// 新增
Signature-Headers: area_id:req_id
area_id: 29a33e8796834b1**** // 开发者自定义
req_id: 8afdb70ab2ed11eb85290242ac13**** // 开发者自定义
可以发现,以上示例将参与签名的请求结构中的字段拼接到一起,用半角冒号(:
)分隔,即代码中的 area_id:req_id
,作为 Signature-Headers
的值。
而 Optional_Signature_key
的值就是对 Signature-Headers
值包含的所有的 Key 和 这些 Key 对应的值进行拼接得到的,拼接结构如下:
String Optional_Signature_key =
Signature-Headers-Key1 + ":" + Signature-Headers-Value1 + "\n" +
Signature-Headers-Key2 + ":" + Signature-Headers-Value2 + "\n" +
...
Signature-Headers-KeyN + ":" + Signature-Headers-ValueN + "\n"
对应上示例中的即是:
String Optional_Signature_key =
area_id + ":" + 29a33e8796834b1**** + "\n" +
req_id + ":" + 8afdb70ab2ed11eb85290242ac13**** + "\n"
URL 指请求 Path 和表单参数拼接而成的请求路径。
URL 拼接:对表单参数按照字典对参数的 Key 进行按字母自然升序排序后,按照如下方法拼接。如果 Query 或 Form 参数为空,则 URL=Path,不需要添加半角问号(?
)。
String url =
Path +
"?" +
Key1 + "=" + Value1 +
"&" + Key2 + "=" + Value2 +
"&" + Key3 +
...
"&" + KeyN + "=" + ValueN
例如:
String url = /v1.0/iot-03/devices/87707085bcddc23a5fa3/logs?end_time=1657263936000&event_types=1&start_time=1657160836000
URL 参数排序:按参数的字母自然升序排序,字母相同时比对下一位。
例如,有以下参数:
排序后的规则为:
end_time=1657263936000
event_types=1
start_time=1657160836000
end_time
和 event_types
首字母相同,event_types
的第二个字段 v
大于 end_time
的第二个字母 n
。所以 event_types
排在 end_time
后面。
start_time
的首字段大于 end_time
和 event_types
的首字母,所以排在 end_time
和 event_types
之后。
以获取 Token 接口为例,参数 grant_type
的值为 2
,body 参数为空。
GET:/v1.0/token?grant_type=2
请求头结构如下:
参数 | 取值 |
---|---|
method | GET |
client_id | 1KAD46OrT9HafiKdsXeg |
secret | 4OHBOnWOqaEC1mWXOpVL3yV50s0qGSRC |
t | 1588925778000 |
sign_method | HMAC-SHA256 |
nonce | 5138cc3a9033d69856923fd07b491173 |
Signature-Headers | area_id:call_id |
area_id (参与签名的开发者自定义字段) | 29a33e8796834b1efa6 |
call_id (参与签名的开发者自定义字段) | 8afdb70ab2ed11eb85290242ac130003 |
stringToSign
签名字符串首先,拼接 stringToSign
签名字符串:
GET
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
area_id:29a33e8796834b1efa6
call_id:8afdb70ab2ed11eb85290242ac130003
/v1.0/token?grant_type=2
结构解析:
拿到 stringToSign
之后,就可以拼接待签名字符串:
1KAD46OrT9HafiKdsXeg15889257780005138cc3a9033d69856923fd07b491173GET
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
area_id:29a33e8796834b1efa6
call_id:8afdb70ab2ed11eb85290242ac130003
/v1.0/token?grant_type=2
结构解析:
哈希摘要:
HMAC-SHA256(1KAD46OrT9HafiKdsXeg15889257780005138cc3a9033d69856923fd07b491173GET
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
area_id:29a33e8796834b1efa6
call_id:8afdb70ab2ed11eb85290242ac130003
/v1.0/token?grant_type=2,4OHBOnWOqaEC1mWXOpVL3yV50s0qGSRC)
哈希摘要后得到新字符串:
9e48a3e93b302eeecc803c7241985d0a34eb944f40fb573c7b5c2a82158af13e
转换成大写后得到签名:
9E48A3E93B302EEECC803C7241985D0A34EB944F40FB573C7B5C2A82158AF13E
以获取用户列表 API 为例。参数 schema 的值为 apps,body 参数为空。
参数 | 取值 |
---|---|
URL | /v2.0/apps/schema/users |
method | GET |
client_id | 1KAD46OrT9HafiKdsXeg |
secret | 4OHBOnWOqaEC1mWXOpVL3yV50s0qGSRC |
t | 1588925778000 |
access_token | 3f4eda2bdec17232f67c0b188af3eec1 |
sign_method | HMAC-SHA256 |
nonce | 5138cc3a9033d69856923fd07b491173 |
Signature-Headers | area_id:call_id |
area_id (参与签名的开发者自定义字段) | 29a33e8796834b1efa6 |
call_id (参与签名的开发者自定义字段) | 8afdb70ab2ed11eb85290242ac130003 |
page_no | 1 |
page_size | 50 |
stringToSign
签名字符串首先,拼接 stringToSign
签名字符串:
GET
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
area_id:29a33e8796834b1efa6
call_id:8afdb70ab2ed11eb85290242ac130003
/v2.0/apps/schema/users?page_no=1&page_size=50
结构解析:
拿到 stringToSign
之后,就可以拼接待签名字符串了:
1KAD46OrT9HafiKdsXeg3f4eda2bdec17232f67c0b188af3eec115889257780005138cc3a9033d69856923fd07b491173GET
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
area_id:29a33e8796834b1efa6
call_id:8afdb70ab2ed11eb85290242ac130003
/v2.0/apps/schema/users?page_no=1&page_size=50
结构解析:
哈希摘要:
HMAC-SHA256(1KAD46OrT9HafiKdsXeg3f4eda2bdec17232f67c0b188af3eec115889257780005138cc3a9033d69856923fd07b491173GET
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
area_id:29a33e8796834b1efa6
call_id:8afdb70ab2ed11eb85290242ac130003
/v2.0/apps/schema/users?page_no=1&page_size=50,4OHBOnWOqaEC1mWXOpVL3yV50s0qGSRC)
哈希摘要后得到新字符串:
ae4481c692aa80b25f3a7e12c3a5fd9bbf6251539dd78e565a1a72a508a88784
转换成大写后得到签名:
AE4481C692AA80B25F3A7E12C3A5FD9BBF6251539DD78E565A1A72A508A88784
下载 Java 代码示例。
下载 Golang 代码示例。
下载 Node.js 代码示例。
/**
Run the code online with this jsfiddle. Dependent upon an open source js library calledhttp://code.google.com/p/crypto-js/.
**/
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/enc-base64.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/hmac-sha256.min.js"></script>
<script>
var hash = CryptoJS.HmacSHA256("str", "secret");
var hashInBase64 = hash.toString().toUpperCase();
document.write(hashInBase64);
</script>
/**
PHP has built in methods for hash_hmac (PHP 5) and base64_encode (PHP 4, PHP 5) resulting in no outside dependencies. Say what you want about PHP but they have the cleanest code for this example.
**/
$s = strtoupper(hash_hmac("sha256", "str", 'secret'));
echo var_dump($s);
using System;
using System.Security.Cryptography;
namespace Test
{
public class MyHmac
{
public static string Encrypt(string str, string secret)
{
secret = secret ?? "";
var encoding = new System.Text.UTF8Encoding();
byte[] keyByte = encoding.GetBytes(secret);
byte[] messageBytes = encoding.GetBytes(str);
using (var hmacsha256 = new HMACSHA256(keyByte))
{
byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
StringBuilder builder = new StringBuilder();
for (int i = 0; i < hashmessage.Length; i++)
{
builder.Append(hashmessage[i].ToString("x2"));
}
return builder.ToString().ToUpper();
}
}
}
}
本地开发时,可基于 Postman 软件预先查看加密后的签名结果。具体查询方法,请参考 验证签名结果。
stringToSign
签名字符串中间会有一行空行?因为 stringToSign
结构导致。stringToSign
是由 HTTPMethod、Content-SHA256、Headers 和 URL 中间用换行符连接而成。其结构如下:
String stringToSign=
HTTPMethod + "\n" +
Content-SHA256 + "\n" +
Headers + "\n" +
URL
Headers
的结构如下,结尾也有一个换行符:
String Headers =
HeaderKey1 + ":" + HeaderValue1 + "\n" +
HeaderKey2 + ":" + HeaderValue2 + "\n" +
...
HeaderKeyN + ":" + HeaderValueN + "\n"
所以,Headers
的结尾的换行符再加上 stringToSign
的拼接换行符,两个换行符拼接在一起,就会导致中间空出一行。
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈