您通过 HTTP 请求来调用火山引擎私网解析(PrivateZone)的 API。
在发送 HTTP 请求前,您需要理解以下内容:
API 请求的结构包含以下内容:
私网解析 PrivateZone 的 API 服务域名是 open.volcengineapi.com
。
您可以使用 HTTP
协议或 HTTPS
协议发送请求。推荐您使用 HTTPS
协议,其安全性更高。
关于 API 所使用的方法,参见每个 API 的说明。对于 POST 请求,您必须在请求头中指定 Content-Type: application/json
。
请求参数包括公共参数和每个 API 所特有的参数。
公共参数是每个 API 请求必须包含的参数。如果一个 API 请求缺失公共参数,请求会失败。以下表格中的公共参数必须包含在查询字符串(query string)中。
参数名称 | 数据类型 | 是否必选 | 说明 | 示例 |
---|---|---|---|---|
Action | string | 是 | 表示 API 名称。格式为 [a-zA-Z]+ 。 | CreatePrivateZone |
Version | string | 是 | 表示 API 版本。该参数的取值是 2022-06-01 。 | 2022-06-01 |
X-Expires | Integer | 否 | 签名的有效时间,单位为秒。默认值为 900。 | 900 |
每个请求中必须包含鉴权信息。该鉴权信息用以验证请求者的身份。
下面的示例代码演示了如何基于 Golang 在调用 UpdatePrivateZone API 和 ListPrivateZones API 时对请求进行鉴权。
在运行示例代码之前,您需要 获取 Access Key ID 和 Secret Access Key。然后,您需要分别将 Access Key ID 和 Secret Access Key 的值传入示例代码中的 AccessKey
常量和 SecretAccessKey
常量。
package main import ( "bytes" "crypto/hmac" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "io/ioutil" "net/http" "net/url" "strings" "time" ) const Version = "2022-06-01" const Service = "private_zone" const Region = "cn-north-1" const Host = "open.volcengineapi.com" // 第一步:准备辅助函数。 // sha256非对称加密 func hmacSHA256(key []byte, content string) []byte { mac := hmac.New(sha256.New, key) mac.Write([]byte(content)) return mac.Sum(nil) } // sha256 hash算法 func hashSHA256(content []byte) string { h := sha256.New() h.Write(content) return hex.EncodeToString(h.Sum(nil)) } // 第二步:准备需要用到的结构体定义。 // 签算请求结构体 type RequestParam struct { Body []byte Method string Date time.Time Path string Host string QueryList url.Values } // 身份证明结构体 type Credentials struct { AccessKeyID string SecretAccessKey string Service string Region string } // 签算结果结构体 type SignRequest struct { XDate string Host string ContentType string XContentSha256 string Authorization string } // go http client var httpClient = &http.Client{ Timeout: time.Second * 60, Transport: &http.Transport{ MaxIdleConns: 100, MaxConnsPerHost: 10, IdleConnTimeout: time.Second * 15, }, } // 第三步:创建一个私网域名解析的 API 请求函数。签名计算的过程包含在该函数中。 func requestPrivateZone(method string, query map[string][]string, header map[string]string, ak string, sk string, action string, body []byte) ([]byte, error) { // 第四步:在 requestPrivateZone 中,创建一个 HTTP 请求实例。 // 创建 HTTP 请求实例。该实例会在后续用到。 request, _ := http.NewRequest(method, "https://"+Host+"/", bytes.NewReader(body)) urlVales := url.Values{} for k, v := range query { urlVales[k] = v } urlVales["Action"] = []string{action} urlVales["Version"] = []string{Version} request.URL.RawQuery = urlVales.Encode() for k, v := range header { request.Header.Set(k, v) } // 第五步:创建身份证明。其中的 Service 和 Region 字段是固定的。ak 和 sk 分别代表 AccessKeyID 和 SecretAccessKey。同时需要初始化签名结构体。一些签名计算时需要的属性也在这里处理。 // 初始化身份证明 credential := Credentials{ AccessKeyID: ak, SecretAccessKey: sk, Service: Service, Region: Region, } // 初始化签名结构体 requestParam := RequestParam{ Body: body, Host: request.Host, Path: "/", Method: request.Method, Date: time.Now().UTC(), QueryList: request.URL.Query(), } // 第六步:接下来开始计算签名。在计算签名前,先准备好用于接收签算结果的 signResult 变量,并设置一些参数。 // 初始化签名结果的结构体 xDate := requestParam.Date.Format("20060102T150405Z") shortXDate := xDate[:8] XContentSha256 := hashSHA256(requestParam.Body) contentType := "application/json" signResult := SignRequest{ Host: requestParam.Host, // 设置Host XContentSha256: XContentSha256, // 加密body XDate: xDate, // 设置标准化时间 ContentType: contentType, // 设置Content-Type 为 application/json } // 第七步:计算 Signature 签名。 signedHeadersStr := strings.Join([]string{"content-type", "host", "x-content-sha256", "x-date"}, ";") canonicalRequestStr := strings.Join([]string{ requestParam.Method, requestParam.Path, request.URL.RawQuery, strings.Join([]string{"content-type:" + contentType, "host:" + requestParam.Host, "x-content-sha256:" + XContentSha256, "x-date:" + xDate}, "\n"), "", signedHeadersStr, XContentSha256, }, "\n") hashedCanonicalRequest := hashSHA256([]byte(canonicalRequestStr)) credentialScope := strings.Join([]string{shortXDate, credential.Region, credential.Service, "request"}, "/") stringToSign := strings.Join([]string{ "HMAC-SHA256", xDate, credentialScope, hashedCanonicalRequest, }, "\n") kDate := hmacSHA256([]byte(credential.SecretAccessKey), shortXDate) kRegion := hmacSHA256(kDate, credential.Region) kService := hmacSHA256(kRegion, credential.Service) kSigning := hmacSHA256(kService, "request") signature := hex.EncodeToString(hmacSHA256(kSigning, stringToSign)) signResult.Authorization = fmt.Sprintf("HMAC-SHA256 Credential=%s, SignedHeaders=%s, Signature=%s", credential.AccessKeyID+"/"+credentialScope, signedHeadersStr, signature) // 第八步:将 Signature 签名写入HTTP Header 中,并发送 HTTP 请求。 // 设置经过签名的5个HTTP Header request.Header.Set("Host", signResult.Host) request.Header.Set("Content-Type", signResult.ContentType) request.Header.Set("X-Date", signResult.XDate) request.Header.Set("X-Content-Sha256", signResult.XContentSha256) request.Header.Set("Authorization", signResult.Authorization) resp, err := httpClient.Do(request) if err != nil { return nil, err } body, err = ioutil.ReadAll(resp.Body) if err != nil { return nil, err } return body, nil } /* main.go */ // 定义请求参数结构体 type UpdateZoneParam struct { ZID uint64 `json:"ZID"` Remark string `json:"Remark"` } func main() { // (POST 请求)调用 UpdatePrivateZone API bodyParam := UpdateZoneParam{ ZID: 100, Remark: "example", } body, err := json.Marshal(&bodyParam) if err != nil { fmt.Printf("marshal failed: %v", err) return } // 分别将 Access Key ID 和 Secret Access Key 的值传入 AK 常量和 SK 常量 AccessKey := "ak" SecretAccessKey := "sk" updatePrivateZoneResult, err := requestPrivateZone("POST", map[string][]string{}, map[string]string{}, AccessKey, SecretAccessKey, "UpdatePrivateZone", body) if err != nil { fmt.Printf("do request failed: %v", err) return } // 打印输出的结果 fmt.Println(string(updatePrivateZoneResult)) // (GET 请求)调用 ListPrivateZones API QueryParam := map[string][]string{"KeyWord": []string{"example.com"}} ListPrivateZonesResult, err := requestPrivateZone("GET", QueryParam, map[string]string{}, AccessKey, SecretAccessKey, "ListPrivateZones", []byte{}) if err != nil { fmt.Printf("do request failed: %v", err) return } // 打印输出的结果 fmt.Println(string(ListPrivateZonesResult)) }
下面的示例代码演示了如何基于 Python 3 在调用 UpdatePrivateZone API 和 ListPrivateZones API 时对请求进行鉴权。
在运行示例代码之前,您需要 获取 Access Key ID 和 Secret Access Key。然后,您需要分别将 Access Key ID 和 Secret Access Key 的值传入示例代码中的 AK
变量和 SK
变量。另外,示例代码使用了 requests 库,您可以通过 python3 -m pip install requests
命令安装 requests 库。
import datetime import hashlib import hmac import json from urllib.parse import quote from collections import OrderedDict import requests Service = "private_zone" Version = "2022-06-01" Region = "cn-north-1" Host = "open.volcengineapi.com" # 分别将 Access Key ID 和 Secret Access Key 的值传入 AK 变量和 SK 变量 AK = "ak" SK = "sk" def norm_query(params): query = "" for key in sorted(params.keys()): if type(params[key]) == list: for k in params[key]: query = ( query + quote(key, safe="-_.~") + "=" + quote(k, safe="-_.~") + "&" ) else: query = (query + quote(key, safe="-_.~") + "=" + quote(params[key], safe="-_.~") + "&") query = query[:-1] return query.replace("+", "%20") # 第一步:准备辅助函数。 # sha256 非对称加密 def hmac_sha256(key: bytes, content: str): return hmac.new(key, content.encode("utf-8"), hashlib.sha256).digest() # sha256 hash算法 def hash_sha256(content: str): return hashlib.sha256(content.encode("utf-8")).hexdigest() # 第二步:创建一个 API 请求函数。签名计算的过程包含在该函数中。 def request(method, query, header, ak, sk, action, body): # 第三步:创建身份证明。其中的 Service 和 Region 字段是固定的。ak 和 sk 分别代表 # AccessKeyID 和 SecretAccessKey。同时需要初始化签名结构体。一些签名计算时需要的属性也在这里处理。 # 初始化身份证明结构体 credential = { "access_key_id": ak, "secret_access_key": sk, "service": Service, "region": Region, } # 初始化签名结构体 query = {"Action": action, "Version": Version, **query} sorted_query = OrderedDict(sorted(query.items())) request_param = { "body":"", "host": Host, "path": "/", "method": method, "content_type": "application/json", "date": datetime.datetime.utcnow(), "query": sorted_query, } if method == "POST": request_param["body"] = json.dumps(body) # 第四步:接下来开始计算签名。在计算签名前,先准备好用于接收签算结果的 signResult 变量,并设置一些参数。 # 初始化签名结果的结构体 x_date = request_param["date"].strftime("%Y%m%dT%H%M%SZ") short_x_date = x_date[:8] x_content_sha256 = hash_sha256(request_param["body"]) sign_result = { "Host": request_param["host"], "X-Content-Sha256": x_content_sha256, "X-Date": x_date, "Content-Type": request_param["content_type"], } # 第五步:计算 Signature 签名。 signed_headers_str = ";".join( ["content-type", "host", "x-content-sha256", "x-date"] ) canonical_request_str = "\n".join( [request_param["method"], request_param["path"], norm_query(request_param["query"]), "\n".join( [ "content-type:" + request_param["content_type"], "host:" + request_param["host"], "x-content-sha256:" + x_content_sha256, "x-date:" + x_date, ] ), "", signed_headers_str, x_content_sha256, ] ) hashed_canonical_request = hash_sha256(canonical_request_str) credential_scope = "/".join([short_x_date, credential["region"], credential["service"], "request"]) string_to_sign = "\n".join(["HMAC-SHA256", x_date, credential_scope, hashed_canonical_request]) k_date = hmac_sha256(credential["secret_access_key"].encode("utf-8"), short_x_date) k_region = hmac_sha256(k_date, credential["region"]) k_service = hmac_sha256(k_region, credential["service"]) k_signing = hmac_sha256(k_service, "request") signature = hmac_sha256(k_signing, string_to_sign).hex() sign_result["Authorization"] = "HMAC-SHA256 Credential={}, SignedHeaders={}, Signature={}".format( credential["access_key_id"] + "/" + credential_scope, signed_headers_str, signature, ) header = {**header, **sign_result} # 第六步:将 Signature 签名写入 HTTP Header 中,并发送 HTTP 请求。 if method == "POST": r = requests.post("https://{}{}".format(request_param["host"], request_param["path"]), headers=header, params=request_param["query"], data=request_param["body"], ) return r.json() if method == "GET": r = requests.get("https://{}{}".format(request_param["host"], request_param["path"]), headers=header, params=request_param["query"], data=request_param["body"], ) return r.json() if __name__ == "__main__": # (POST 请求)调用 UpdatePrivateZone API request_body = { "ZID": 100, "Remark": "example", } update_private_zone_result = request("POST", {}, {}, AK, SK, "UpdatePrivateZone", request_body) print(update_private_zone_result) # (GET 请求)调用 ListPrivateZones API request_query = {"KeyWord": "example.com"} list_private_zones_result = request("GET", request_query, {}, AK, SK, "ListPrivateZones", {}) print(list_private_zones_result)
您可以使用以下任一方法提供鉴权信息。推荐您使用方法一。
您需要在请求头中包含以下参数:
参数名称 | 数据类型 | 是否必选 | 参数说明 | 示例 |
---|---|---|---|---|
X-Date | string | 是 | 表示签名计算的时间,以 UTC 表示。时间精度是秒。 | 20201103T104027Z |
Authorization | string | 是 | 该参数表示鉴权字符串。
| 该参数表示一个结构体。结构体中包含了 |
X-Security-Token | string | 否 | 如果您使用火山引擎账号的 Access Key ID 和 Secret Access Key 来计算 Authorization,则无需指定该参数。如果您使用的 Access Key ID 和 Secret Access Key 是安全令牌服务(STS)提供的,则需要指定该参数。该参数值就是 STS 颁发的临时安全凭证中的 SessionToken。参见 获取临时安全令牌。 | STSeyJBY2NvdW50SW |
请求示例
GET https://open.volcengineapi.com/?Action=ListPrivateZones&Version=2022-06-01 X-Date: 20230116T073702Z Authorization: HMAC-SHA256 Credential=AKLTMjYxYTZmYWU4ZWYzNGI2NDg8NTUxODE1ZGVhNmIxZmQ/20230116/cn-north-1/private_zone/request, SignedHeaders=x-content-sha256;x-date, Signature=5a394ce80456c7cdf989c28bd638807c8ead386eb15dd36e39952f405380aef2 ServiceName: private_zone
您需要在查询字符串中包含以下查询参数。
参数名称 | 数据类型 | 是否必选 | 参数说明 | 示例 |
---|---|---|---|---|
X-Date | string | 是 | 签名计算的时间,以 UTC 表示。时间精度是秒。 | 20201103T104027Z |
X-Algorithm | string | 是 | 签名计算所使用的算法。该参数的值是 HMAC-SHA256 。 | HMAC-SHA256 |
X-Credential | string | 是 | X-Credential 的伪代码结构如下:
关于 |
|
X-SignedHeaders | string | 是 | 参与签名计算的请求头参数。多个请求头参数使用分号(;)分隔。这些请求头参数是根据参数名称升序排序的。 |
|
X-Signature | string | 是 | 一个经过计算得到的签名。关于签名的计算步骤,参见签名计算方式。 |
本章节主要介绍签名是如何计算的。
HMAC
方法使用的是 HMAC-SHA256 算法。HexEncode
方法将字符串转换为十六进制编码格式的字符串。Signature
是基于 kSigning
和 StringToSign
参数计算而来的。Signature
的伪代码如下:
Signature = HexEncode(HMAC(kSigning, StringToSign))
kSigning
表示用来计算签名的密钥。要计算 kSigning
,您必须先获取您账号的 Access Key Secret,然后使用以下伪代码生成 kSigning
。
kSecret = <Your Access Key Secret> kDate = HMAC(kSecret, ShortDate) kRegion = HMAC(kDate, Region) kService = HMAC(kRegion, Service) kSigning = HMAC(kService, "request")
关于 kSigning
伪代码中参数的说明,参见伪代码中参数的说明。
StringToSign
表示用来计算签名的签名字符串。StringToSign
的伪代码如下:
// Hash 函数使用 SHA256 算法 StringToSign = Algorithm + '\n' + RequestDate + '\n' + CredentialScope + '\n' + HexEncode(Hash(CanonicalRequest))
关于 StringToSign
伪代码中 Algorithm
和 RequestDate
参数的说明,参见伪代码中参数的说明。StringToSign
伪代码中其他参数的说明如下。
CredentialScope
CredentialScope
表示凭证范围。CredentialScope
的伪代码如下:
{ShortDate}/{Region}/{Service}/{Request}
关于 CredentialScope
伪代码中参数的说明,参见伪代码中参数的说明。
CanonicalRequest
CanonicalRequest
表示规范的请求。CanonicalRequest
的伪代码如下:
HTTPRequestMethod + '\n' + CanonicalURI + '\n' + CanonicalQueryString + '\n' + CanonicalHeaders + '\n' + SignedHeaders + '\n' + HexEncode(Hash(RequestPayload))
CanonicalRequest
伪代码中参数的说明如下:
HTTPRequestMethod
:请求方法。CanonicalURI
:请求的 URI。CanonicalQueryString
:规范的查询字符串。CanonicalQueryString
的值是通过以下规则生成的:
CanonicalHeaders
:规范的请求头。CanonicalHeaders
是通过以下步骤生成的:
\n
)。SignedHeaders
:参见伪代码中参数的说明。HexEncode(Hash(RequestPayload))
:一个计算值。该值是通过对请求正文中 payload
的值应用 SHA256 哈希算法计算得到的。以下表格包含了本文中多个伪代码中参数的说明。
参数名称 | 数据类型 | 参数说明 | 示例 |
---|---|---|---|
AccessKey | string | 您账号的 Access Key ID。参考获取 Access Key。 | AKLTMjI2ODVlYzI3ZGY1NGU4ZjhjYWRjMTlmNTM5OTZkYzE |
RequestDate | string | 该参数与 X-Date 的定义相同。 | 20210913T081805Z |
ShortDate | string | 该参数与 RequestDate 的定义相同,只不过时间精度是日。 | 20210913 |
Region | string | 私网解析 PrivateZone 所在的地域。该参数的取值是 cn-north-1 。 | cn-north-1 |
Service | string | 私网解析 PrivateZone 的服务名称。该参数的取值是 private_zone 。 | private_zone |
SignedHeaders | string | 参与签名计算的请求头参数。多个请求头参数使用分号(;)分隔。这些请求头参数是根据参数名称升序排序的。 一般来说, SignedHeaders 的值是 host;x-content-sha256;x-date 。您也可以指定任意请求头参数作为 SignedHeaders 的值。 | host;x-content-sha256;x-date |
Algorithm | string | 表示签名计算所使用的算法。该参数的取值为 HMAC-SHA256 。 | HMAC-SHA256 |
Request | string | 该参数是一个常量,值是 request 。 | request |