本小节主要介绍 API 请求中签名 ( signature ) 的生成方法。您可以根据实际情况选择使用 API 密钥IAM 身份 来完成签名。

API 密钥签名

您需要先在控制台创建 API 密钥,获取 accesss_key_id 和 secret_access_key。

假设 access_key_id 和 secret_access_key 如下所示。

access_key_id = 'QYACCESSKEYIDEXAMPLE'
secret_access_key = 'SECRETACCESSKEY'

请求参数如下:

param = {
'access_key_id': 'QYACCESSKEYIDEXAMPLE',
"zone":"jinan1a",
"signature_method": "HmacSHA256",
"signature_version": "1",
"version":"1",
"timestamp": '2021-08-19T16:44:40Z',
}
说明

使用上述的 AccessKey 和 Request 调试您的代码, 当得到跟后面一致的签名结果后(即表示你的代码是正确的), 可再换为您自己的 AccessKey 和其他 API 请求。

这里以请求集群列表为例,若最后计算的结果和示例中一样,只需要换成自己的 access_key_id 和 secret_access_key 以及请求的 path 即可。

步骤 1: 参数排序

按参数名进行升序排列,排序后的参数为:

{
'access_key_id': 'QYACCESSKEYIDEXAMPLE'
'signature_method': 'HmacSHA256',
'signature_version': '1',
'timestamp': '2021-08-19T16:44:40Z',
'version': '1',
'zone': 'jinan1a'
}

步骤 2: 参数 URL 编码

对参数名称和参数值进行 URL 编码,编码后的请求串为:

{
'access_key_id': 'QYACCESSKEYIDEXAMPLE'
'signature_method': 'HmacSHA256',
'signature_version': '1',
'timestamp': '2021-08-19T16%3A44%3A40Z',
'version': '1',
'zone': 'jinan1a'
}
注意
  • 编码时空格要转换成 “%20” , 而不是 “+”。

  • 转码部分的字符要用大写,如 `":`" 应转成 “%3A”,而不是 “%3a”。

步骤 3: 构造 URL 请求

参数名和参数值之间用 “=” 号连接,参数和参数之间用 “&” 号连接。构造后的 URL 请求如下示例:

access_key_id=QYACCESSKEYIDEXAMPLE&signature_method=HmacSHA256&signature_version=1&timestamp=2021-08-19T16%3A44%3A40Z&version=1&zone=jinan1a

步骤 4: 构造被签名串

被签名串的构造规则为: 被签名串 = HTTP 请求方式 + “\n” + uri + “\n” + url 请求串 + “\n” + md5(requests_body)

以请求集群列表为例:

methed : GET

url :/api/cluster/list/

requests_param:
access_key_id=QYACCESSKEYIDEXAMPLE&signature_method=HmacSHA256&signature_version=1&timestamp=2021-08-19T16%3A44%3A40Z&version=1&zone=jinan1a

md5(requests_body):请求体的 md5 值,如果是 get 请求requests_ body 为空字符串’’,注意中间没有空格,计算出的 md5 值固定为:
d41d8cd98f00b204e9800998ecf8427e

如果是 post 请求,requests_ body 是请求 body 的 json 字符串的 MD5 值
string_to_sign  = methed + "\n" + url + "\n"+ requests_param + "\n" + MD5(requests_body)

步骤 5: 计算签名

计算被签名串的签名 signature。

  • 将 API 密钥的私钥 ( secret_access_key ) 作为key,生成被签名串的 HMAC-SHA256 或者 HMAC-SHA1 签名,更多信息可参见 RFC2104

  • 将签名进行 Base64 编码

  • 将 Base64 编码后的结果进行 URL 编码

string_to_sign: = GET\n/api/cluster/list/\naccess_key_id=QYACCESSKEYIDEXAMPLE&signature_method=HmacSHA256&signature_version=1&timestamp=2021-08-19T16%3A44%3A40Z&version=1&zone=jinan1a\nd41d8cd98f00b204e9800998ecf8427e
  • python3 生成签名代码示例*

h = hmac.new(sk.encode(encoding="utf-8"), digestmod=sha256)
h.update(string_to_sign.encode(encoding="utf-8"))
sign = base64.b64encode(h.digest()).strip()
signature = parse.quote_plus(sign.decode())
signature = parse.quote_plus(signature)
requests_param = requests_param + "&signature=%s"%signature

最终生成待签名的字符串

GET\n/api/cluster/list/\naccess_key_id=QYACCESSKEYIDEXAMPLE&signature_method=HmacSHA256&signature_version=1&timestamp=2021-08-19T16%3A44%3A40Z&version=1&zone=jinan1a\nd41d8cd98f00b204e9800998ecf8427e

使用上面的签名串得到的 signature 为:

W68niby0THV%252BXsKcRxuGblFm2a4XbTdto129JkSH2%252FM%253D

将得到的签名 signature 参数附在原有的请求串的最后面:

access_key_id=QYACCESSKEYIDEXAMPLE&signature_method=HmacSHA256&signature_version=1&timestamp=2021-08-19T16%3A44%3A40Z&version=1&zone=jinan1a&signature=W68niby0THV%252BXsKcRxuGblFm2a4XbTdto129JkSH2%252FM%253D

假如请求的 host 是https://hpc-api.shanhe.com:443,将生成的最终签名结果的 URL 加到请求的路径下

https://hpc-api.shanhe.com:443/api/cluster/list,中间用?连接。

最终得到的完整的 URL 请求如下:

https://hpc-api.shanhe.com:443/api/cluster/list?access_key_id=QYACCESSKEYIDEXAMPLE&signature_method=HmacSHA256&signature_version=1&timestamp=2021-08-19T16%3A44%3A40Z&version=1&zone=jinan1a&signature=W68niby0THV%252BXsKcRxuGblFm2a4XbTdto129JkSH2%252FM%253D

测试代码

将签名参数附在原有请求串的最后面。最终的 HTTP 请求串示例如下(为了查看方便,可将参数之间用回车分隔开)。

# !/usr/bin/python3

import requests,json,time,datetime
from urllib import parse
from hashlib import sha256
import hashlib
import hmac
import base64,json
import collections


def hex_encode_md5_hash(data):
    if not data:
        data = "".encode("utf-8")
    else:
        data = data.encode("utf-8")
    md5 = hashlib.md5()
    md5.update(data)
    return md5.hexdigest()

def get_signature(url="",ak="",sk="",params="",requests_body=""):

    params["access_key_id"] = ak
    keys = sorted(params.keys())
    print(keys)

    # sorted_param = {key:params[key] for key in keys}
    # print(sorted_param)
    sorted_param = collections.OrderedDict()
    for key in keys:
        sorted_param[key] = params[key]

    requests_param = parse.urlencode(sorted_param)
    print(requests_param)

    if requests_body:
        method = "POST"
        body = hex_encode_md5_hash(json.dumps(requests_body))
    else:
        method = "GET"
        body = hex_encode_md5_hash("")
    string_to_sign  = method + "\n" + url + "\n"+ requests_param + "\n" + body
    print(string_to_sign)
    # string_to_sign  = "GET" + "\n" + url + "\n"+ requests_param + "\n"
    h = hmac.new(sk.encode(encoding="utf-8"), digestmod=sha256)
    h.update(string_to_sign.encode(encoding="utf-8"))
    sign = base64.b64encode(h.digest()).strip()
    signature = parse.quote_plus(sign.decode())
    signature = parse.quote_plus(signature)
    requests_param = requests_param + "&signature=%s"%signature
    return requests_param

def list_cluster():
    secret_access_key = "SECRETACCESSKEYSECRETACCESSKEY"
    access_key_id = "QYACCESSKEYIDEXAMPLE"

    url = 'https://test.hpc.qingcloud.com/api/cluster/list'
    param = {
        "zone":"jinan1a",
        "signature_method": "HmacSHA256",
        "signature_version": "1",
        "version":"1",
        "timestamp":datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ")
    }
    signature = get_signature(url="/api/cluster/list/",ak=access_key_id,sk=secret_access_key,params=param)
    url = url + "?" + signature
    print(url)
    headers={'Content-Type': 'application/json','User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'}
    r = requests.get(url)
    print(r.text)

list_cluster()