You need to enable JavaScript to run this app.
文档中心
函数服务

函数服务

复制全文
下载 pdf
调用方式
签名机制
复制全文
下载 pdf
签名机制
火山引擎对于每一次的 HTTPS 协议访问请求,会通过访问签名信息中的访问密钥(包括 Access Key ID 和 Secret Access Key),验证访问请求者身份。​
获取访问密钥​
账户和有权限的用户可以新建访问密钥,操作如下:​
  1. 使用主账号或拥有密钥管理权限的 IAM 用户登录 访问控制控制台。​
  1. 在左侧导航栏选择 资源管理 > 密钥管理。​
  1. 查看您账号的访问密钥列表。​
  • 每个账号最多同时拥有 2 个访问密钥。​
  • 如果当前账号的访问密钥数量未达到上限,则可单击 新建密钥,创建新的访问密钥。创建完成后单击 查看AccessKey详情,查看新的访问密钥信息。​
创建一个正规化请求​
说明​
  • Hash 代指 SHA256 算法。​
  • HexEncode 代指转 16 进制编码。​
​
CanonicalRequest = HTTPRequestMethod + '\n' + CanonicalURI + '\n' + CanonicalQueryString + '\n' + CanonicalHeaders + '\n' + SignedHeaders + '\n' + HexEncode(Hash(RequestPayload))​
​
在签名之前,要将请求正规化,使得签名计算过程无异议,其主要过程及伪代码如下:​
​
字段​
说明​
HTTPRequestMethod​
HTTP 请求方法,例如 POST。​
CanonicalURI​
正规化后的 URI。如果 URI 为空,那么使用/作为绝对路径。​
CanonicalQueryString​
正规化后的 QueryString。QueryString 正规化流程如下:​
  1. URL 编码(UrlEncode)每个 QueryString 参数名称和参数值。​
  1. 按照 ASCII 字节顺序对参数名称严格排序。​
  1. 将排序好的参数名称和参数值用=连接;按照排序结果将参数对用&连接。​
​
CanonicalQueryString = "Action=ListUsers&Version=2018-01-01"​
​
CanonicalHeaders​
正规化后的Header。其中伪代码如下:​
​
CanonicalHeaders = CanonicalHeadersEntry0 + CanonicalHeadersEntry1 + ... + CanonicalHeadersEntryN​
​
​
CanonicalHeadersEntry = Lowercase(HeaderName) + ':' + Trimall(HeaderValue) + '\n'​
​
其中,Lowercase表示将 Header 的名称全部转化成小写,Trimall表示去掉 Header 的值的前后多余空格。​
注意​
  • 结尾需要添加换行符:`\n`。​
  • Header 的顺序由headerName的小写后 ASCII 排序。​
SignedHeaders​
参与签名的 Header 名称。​
签名 Header 需包含在正规化 Headers 名称列表中,用于指明哪些 Header 参与签名计算,从而忽略请求被 Proxy 添加的额外 Header。如果存在host、x-date两个 Header,则必须添加到正规化 Headers 名称列表中。​
伪代码如下:​
​
SignedHeaders = Lowercase(HeaderName0) + ';' + Lowercase(HeaderName1) + ";" + ... + Lowercase(HeaderNameN)​
​
RequestPayload​
完整的请求的 Body。​
  • URL 编码(同 RFC3986 方法) 每个 QueryString 参数名称和参数值。​
  • 其中 GET 方法需要包含哈希算法、信任状、签名日期和签名 Header 等全部参数。​
  • 按照 ASCII 字节顺序对参数名称严格排序。​
  • 将排序好的参数名称和参数值用=连接;按照排序结果将参数对用&连接。​
​
创建签名字符串​
签名字符串主要包含请求以及正规化请求的元数据信息,由签名算法、请求日期、信任状和正规化请求哈希值连接组成,伪代码如下:​
​
StringToSign = Algorithm + '\n' + RequestDate + '\n' + CredentialScope + '\n' + HexEncode(Hash(CanonicalRequest))​
​
字段说明如下。​
​
字段​
说明​
Algorithm​
签名的算法。目前火山引擎仅支持 HMAC-SHA256 签名算法。​
RequestDate​
请求 UTC 时间。请使用YYYYMMDD'T'HHMMSS'Z'格式,例如:20201103T104027Z。​
CredentialScope​
凭证范围。格式为YYYYMMDD/region/service/request。​
CanonicalRequest​
前序正规化请求的结果。​
​
计算签名秘钥​
  1. 计算签名前,首先从 Secret Access Key 派生出签名密钥(Signing Key),而不是直接使用 Secret Access Key。具体计算过程如下:​
  • ​
    kSecret = *Your Secret Access Key*​
    kDate = HMAC(kSecret, Date)​
    kRegion = HMAC(kDate, Region)​
    kService = HMAC(kRegion, Service)​
    kSigning = HMAC(kService, "request")​
    ​
  1. 计算签名。​
  • ​
    Signature = HexEncode(HMAC(kSigning, StringToSign))​
    ​
  1. 构建 Header。​
说明​
表达式中用{}内的内容,代表上文计算出的中间过程。​
  • ​
    Authorization: HMAC-SHA256 Credential={AccessKeyId}/{CredentialScope}, SignedHeaders={SignedHeaders}, Signature={Signature}​
    ​
代码示例​
以下是签名机制的 Golang 实现,签名后,调用 veFaaS OpenAPI ListFunctions 接口。Python、Java、NodeJS、PHP 等更多语言实现的签名算法和示例,请参见 volcengine/volc-openapi-demos。​
​
package main​
​
import (​
"bytes"​
"crypto/hmac"​
"crypto/sha256"​
"encoding/hex"​
"fmt"​
"log"​
"net/http"​
"net/http/httputil"​
"net/url"​
"strings"​
"time"​
)​
​
const (​
AccessKeyID = "AKxxx"​
SecretAccessKey = "yyyyyy"​
​
Addr = "https://iam.volcengineapi.com"​
Path = "/" // 路径,不包含 Query​
​
Service = "vefaas"​
Region = "cn-beijing"​
Action = "ListFunctions"​
Version = "2024-06-06"​
)​
​
func hmacSHA256(key []byte, content string) []byte {​
mac := hmac.New(sha256.New, key)​
mac.Write([]byte(content))​
return mac.Sum(nil)​
}​
​
func getSignedKey(secretKey, date, region, service string) []byte {​
kDate := hmacSHA256([]byte(secretKey), date)​
kRegion := hmacSHA256(kDate, region)​
kService := hmacSHA256(kRegion, service)​
kSigning := hmacSHA256(kService, "request")​
​
return kSigning​
}​
​
func hashSHA256(data []byte) []byte {​
hash := sha256.New()​
if _, err := hash.Write(data); err != nil {​
log.Printf("input hash err:%s", err.Error())​
}​
​
return hash.Sum(nil)​
}​
​
func doRequest(method string, queries url.Values, body []byte) error {​
// 1. 构建请求​
queries.Set("Action", Action)​
queries.Set("Version", Version)​
requestAddr := fmt.Sprintf("%s%s?%s", Addr, Path, queries.Encode())​
log.Printf("request addr: %s\n", requestAddr)​
​
request, err := http.NewRequest(method, requestAddr, bytes.NewBuffer(body))​
if err != nil {​
return fmt.Errorf("bad request: %w", err)​
}​
​
// 2. 构建签名材料​
now := time.Now()​
date := now.UTC().Format("20060102T150405Z")​
authDate := date[:8]​
request.Header.Set("X-Date", date)​
​
payload := hex.EncodeToString(hashSHA256(body))​
request.Header.Set("X-Content-Sha256", payload)​
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")​
​
queryString := strings.Replace(queries.Encode(), "+", "%20", -1)​
signedHeaders := []string{"host", "x-date", "x-content-sha256", "content-type"}​
var headerList []string​
for _, header := range signedHeaders {​
if header == "host" {​
headerList = append(headerList, header+":"+request.Host)​
} else {​
v := request.Header.Get(header)​
headerList = append(headerList, header+":"+strings.TrimSpace(v))​
}​
}​
headerString := strings.Join(headerList, "\n")​
​
canonicalString := strings.Join([]string{​
method,​
Path,​
queryString,​
headerString + "\n",​
strings.Join(signedHeaders, ";"),​
payload,​
}, "\n")​
log.Printf("canonical string:\n%s\n", canonicalString)​
​
hashedCanonicalString := hex.EncodeToString(hashSHA256([]byte(canonicalString)))​
log.Printf("hashed canonical string: %s\n", hashedCanonicalString)​
​
credentialScope := authDate + "/" + Region + "/" + Service + "/request"​
signString := strings.Join([]string{​
"HMAC-SHA256",​
date,​
credentialScope,​
hashedCanonicalString,​
}, "\n")​
log.Printf("sign string:\n%s\n", signString)​
​
// 3. 构建认证请求头​
signedKey := getSignedKey(SecretAccessKey, authDate, Region, Service)​
signature := hex.EncodeToString(hmacSHA256(signedKey, signString))​
log.Printf("signature: %s\n", signature)​
​
authorization := "HMAC-SHA256" +​
" Credential=" + AccessKeyID + "/" + credentialScope +​
", SignedHeaders=" + strings.Join(signedHeaders, ";") +​
", Signature=" + signature​
request.Header.Set("Authorization", authorization)​
log.Printf("authorization: %s\n", authorization)​
​
// 4. 打印请求,发起请求​
requestRaw, err := httputil.DumpRequest(request, true)​
if err != nil {​
return fmt.Errorf("dump request err: %w", err)​
}​
​
log.Printf("request:\n%s\n", string(requestRaw))​
​
response, err := http.DefaultClient.Do(request)​
if err != nil {​
return fmt.Errorf("do request err: %w", err)​
}​
​
// 5. 打印响应​
responseRaw, err := httputil.DumpResponse(response, true)​
if err != nil {​
return fmt.Errorf("dump response err: %w", err)​
}​
​
log.Printf("response:\n%s\n", string(responseRaw))​
​
if response.StatusCode == 200 {​
log.Printf("请求成功")​
} else {​
log.Printf("请求失败")​
}​
​
return nil​
}​
​
func main() {​
// GET 请求例子​
query1 := make(url.Values)​
doRequest(http.MethodGet, query1, nil)​
​
}​
​
​
最近更新时间:2025.06.12 14:59:20
这个页面对您有帮助吗?
有用
有用
无用
无用