使用说明
日志服务SDK封装了日志服务的所有API接口,您可以通过日志服务SDK方便地调用日志服务的所有API接口。日志服务SDK支持自动签名,并支持多语言版本。推荐使用日志服务SDK访问日志服务,更多信息,请参见日志服务SDK。
为API请求生成签名,需使用一对访问密钥。您可以使用已经存在的访问密钥对,也可以创建新的访问密钥对,但需要保证使用的密钥对为启用状态。
下文介绍日志服务请求组成结构、算法流程,并给出各语言签名算法的示例代码。
请求组成结构
合法的日志服务请求需要在HTTP请求头内写入如下键值对(大小写敏感):
| 字段名 | 说明 | 示例 | 
| x-log-signaturemethod | 请求的加密方式。 | hmac-sha1 | 
| x-log-apiversion | 访问的API版本。 | 0.6.0 | 
| Date | HTTP请求中的标准时间戳头,其遵循RFC822/RFC1123格式,格式化字符串为%a, %d %b %Y %H:%M:%S GMT,时区为GMT标准时间。 | Mon, 3 Jan 2010 08:33:47 GMT | 
| Content-MD5 | HTTP请求中Body部分的MD5值,必须转换成十六进制大写英文字母字符串。 | 72A15D7DE7EE9E7BB86461FFEA9499 | 
| Authorization | 签名,内容格式为LOG accessKeyId:Signature。其中 accessKeyId为您的AccessKey密钥ID,Signature 为下文计算得到的签名字符串,请参见签名算法流程。 | LOG testAccessKeyId:RLETq4u7sWb3cssZIhsun**** | 
| Content-Type | 请求中Body部分的类型。若无 Body,可省略此字段。 | application/json | 
完成签名后的HTTP请求示例如下所示:
POST /logstores/test-logstore/shards/0?action=split HTTP/1.1
Host: ali-test-project.cn-hangzhou.log.aliyuncs.com
Date: Tue, 23 Aug 2022 12:12:03 GMT
x-log-apiversion: 0.6.0
x-log-signaturemethod: hmac-sha1
Content-Length: 18
Content-Type: application/json
Content-MD5: 49DFDD54B01CBCD2D2AB5E9E5EE6B9B9
Authorization: LOG testAccessKeyId:RLETq4u7sWb3cssZIhsun****
{"hello": "world"}
签名算法流程
签名结构中最为重要的部分是Authorization字段,其格式为LOG accessKeyId:Signature,由用户密钥 AccessKeyId与签名字符串Signature拼接得到。这里介绍签名字符串Signature的构建过程。
签名消息体message
签名字符串Signature由签名消息体message加密编码得到,签名消息体message的构建过程如下:

| 名称 | 内容与格式 | 示例 | 
| method | HTTP请求方法,全大写英文。取值包括GET、POST、PUT和DELETE。 | GET | 
| Content-MD5 | HTTP请求消息Body的MD5哈希值,转换为十六进制大写英文字符串。若Body为空,此项为空字符串。 | 49DFDD54B01CBCD2D2AB5E9E5EE6B9B9 | 
| Content-Type | HTTP请求中Body部分的类型。若Body为空,此项可为空字符串。 | application/json | 
| Date | 当前时间的格式化字符串。格式为RFC822/RFC1123,格式化字符串为%a, %d %b %Y %H:%M:%S GMT,时区为GMT标准时间。 | Mon, 3 Jan 2010 08:33:47 GMT | 
| header | header中以 x-log-或x-acs-为前缀的键值对,按 key升序排序后,以 : 连接键值,以换行符分隔键值对。 |  | 
| uri | 请求的路径,不包括域名与请求参数部分。 | /logstores/test-logstore | 
| query参数 | 请求的查询参数,按key升序排序后,以=连接键值,以&分隔键值对。 | offset=1&size=10 | 
完整的message如下所示,注意除最后一行外,每行行末都存在一个换行符。
POST
1572A15D7DE7EE9E7BB86461FFEA9499
application/json
Tue, 23 Aug 2022 12:12:03 GMT
x-log-apiversion:0.6.0
x-log-bodyrawsize:0
x-log-signaturemethod:hmac-sha1
/logstores?offset=1&size=10
签名字符串Signature
签名字符串Signature的获取流程如下:
- 获取签名消息体message。 
- 将message消息体使用hmac-sha1算法加密,密钥为AccessKeySecret,获取加密后的哈希值。 
- 将哈希值使用标准base64进行编码,获得签名字符串Signature。 
请求签名过程示例
为了帮助您更好地理解整个请求签名的流程,我们用两个示例来演示整个过程。首先,假设您用做日志服务API签名的访问密钥对如下: 
AccessKeyId = "bq2sjzesjmo86kq****"
AccessKeySecret = "4fdO2fTDDnZPU/L7CHNd****"
- 示例一  - 您需要发送如下GET请求列出ali-test-project项目下的所有Logstore,其待签名的HTTP请求如下:  - GET /logstores?logstoreName=&offset=0&size=1000 HTTP/1.1
Date: Mon, 09 Nov 2015 06:11:16 GMT
Host: ali-test-project.cn-hangzhou.log.aliyuncs.com
x-log-apiversion: 0.6.0
x-log-bodyrawsize:0
x-log-signaturemethod: hmac-sha1
 
- 如上API请求生成的签名消息体为:  - GET
Mon, 09 Nov 2015 06:11:16 GMT
x-log-apiversion:0.6.0
x-log-bodyrawsize:0
x-log-signaturemethod:hmac-sha1
/logstores?logstoreName=&offset=0&size=1000
 
- 由于是GET请求,该请求无任何HTTP Body,所以生成的签名字符串中Content-Type与Content-MD5域为空字符串。如果以前面指定的AccessKeySecret做签名运算后得到的签名Signature为:  - jEYOTCJs2e88o+y5F4/S5I****
 
- 最后发送经数字签名的HTTP请求内容如下:  - GET /logstores?logstoreName=&offset=0&size=1000 HTTP/1.1
Date:Mon, 09 Nov 2015 06:11:16 GMT
Host: ali-test-project.cn-hangzhou.log.aliyuncs.com
x-log-apiversion: 0.6.0
x-log-bodyrawsize:0
x-log-signaturemethod: hmac-sha1
Authorization: LOG bq2sjzesjmo86kq35behupbq:jEYOTCJs2e88o+y5F4/S5I****
 
- 示例二  - 您需要给ali-test-project项目中名为test-logstore的Logstore写入下面的日志:  - topic=""
time=1447048976
source="10.10.10.1"
"TestKey": "TestContent"
 
- 为此,按照API定义需要构建如下HTTP请求:  - POST /logstores/test-logstore HTTP/1.1
Date: Mon, 09 Nov 2015 06:03:03 GMT
Host: ali-test-project.cn-hangzhou.log.aliyuncs.com
x-log-apiversion:0.6.0
x-log-bodyrawsize:50
x-log-signaturemethod:hmac-sha1
Content-MD5: 1DD45FA4A70A9300CC9FE7305AF2C494
Content-Length: 52
<日志内容序列化成ProtoBuffer格式的字节流>
 
- 在这个HTTP请求中,写入的日志内容首先被序列化成ProtoBuffer格式(请参见数据编码方式了解该格式的更多细节)后作为请求Body。所以该请求的Content-Type头的值指定为application/x-protobuf。类似,Content-MD5头的值是请求body对应的MD5值。按照上面的签名字符串构造方式,这个请求对应的签名消息体为:  - POST
1DD45FA4A70A9300CC9FE7305AF2C494
application/x-protobuf
Mon, 09 Nov 2015 06:03:03 GMT
x-log-apiversion:0.6.0
x-log-bodyrawsize:50
x-log-compresstype:lz4
x-log-signaturemethod:hmac-sha1
/logstores/ali-test-logstore
 
- 同样,以前面示例中的AccessKeySecret做签名运算,得到的最终签名为:  - XWLGYHGg2F2hcfxWxMLiNk****
 
- 最后发送经数字签名的HTTP请求内容如下:  - POST /logstores/ali-test-logstore HTTP/1.1
Date: Mon, 09 Nov 2015 06:03:03 GMT
Host: ali-test-project.cn-hangzhou.log.aliyuncs.com
x-log-apiversion:0.6.0
x-log-bodyrawsize:50
x-log-compresstype:lz4
x-log-signaturemethod:hmac-sha1
Content-MD5: 1DD45FA4A70A9300CC9FE7305AF2C494
Content-Length: 52
Authorization: LOG bq2sjzesjmo86kq35behupbq:XWLGYHGg2F2hcfxWxMLiNk****
<日志内容序列化成ProtoBuffer格式的字节流>
 
代码示例
- Java签名示例代码 - 该示例依赖第三方库- commons-codec,请在- pom.xml下添加Maven依赖:
 - <dependency>
  <groupId>commons-codec</groupId>
  <artifactId>commons-codec</artifactId>
  <version>1.15</version>
</dependency>
 - 以下为请求签名的代码示例,仅为参考: - package com.aliyun.openservices.log.http.signer;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
public class v1 {
    public static String md5(byte[] bs) throws Exception {
        MessageDigest digest = MessageDigest.getInstance("MD5");
        digest.update(bs);
        String hex = new BigInteger(1, digest.digest()).toString(16).toUpperCase();
        return new String(new char[32 - hex.length()]).replace("\0", "0") + hex;
    }
    public static String getDateString() {
        DateFormat df = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
        df.setTimeZone(new SimpleTimeZone(0, "GMT"));
        return df.format(new Date());
    }
    public static void sign(String method, String uri, String accessKeyId, String accessKeySecret,
                            Map<String, String> params,
                            Map<String, String> headers,
                            byte[] body) throws Exception {
        int contentLength = 0;
        String contentMD5 = "", message = "";
        headers.put("x-log-apiversion", "0.6.0");
        headers.put("x-log-signaturemethod", "hmac-sha1");
        if (body != null && body.length > 0) {
            contentLength = body.length;
            contentMD5 = md5(body);
            headers.put("Content-MD5", contentMD5);
        }
        String date = getDateString();
        headers.put("Date", date);
        headers.put("Content-Length", String.valueOf(contentLength));
        message += method + "\n"
            + contentMD5 + "\n"
            + headers.getOrDefault("Content-Type", "") + "\n"
            + date + "\n";
        // header
        String headerStr = headers.entrySet().stream()
            .filter(e -> e.getKey().startsWith("x-log-") || e.getKey().startsWith("x-acs-"))
            .sorted(Map.Entry.comparingByKey())
            .map(e -> String.format("%s:%s\n", e.getKey(), e.getValue()))
            .collect(Collectors.joining(""));
        message += headerStr;
        // uri & params
        message += uri;
        if (params.size() > 0) {
            message += "?";
        }
        message += params.entrySet().stream()
            .sorted(Map.Entry.comparingByKey())
            .map(e -> String.format("%s=%s", e.getKey(), e.getValue()))
            .collect(Collectors.joining("&"));
        // signature & authorization
        Mac mac = Mac.getInstance("HmacSHA1");
        mac.init(new SecretKeySpec(accessKeySecret.getBytes(StandardCharsets.UTF_8), "HmacSHA1"));
        String signature = new String(Base64.encodeBase64(mac.doFinal(message.getBytes(StandardCharsets.UTF_8))));
        String auth = "LOG " + accessKeyId + ":" + signature;
        headers.put("Authorization", auth);
    }
}
                            
 
- Python签名示例代码 - 以下为请求签名的代码示例,仅为参考: - import base64
import hashlib
import hmac
import locale
from datetime import datetime
from typing import Dict, Tuple
def get_date():
    try:
        locale.setlocale(locale.LC_TIME, "C")
    except Exception as ex:
        pass
    return datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
def sign(method: str, uri: str, access_key_id: str,
         access_key_secret: str, params: Dict[str, str],
         headers: Dict[str, str], body: bytes):
    content_length = 0
    content_md5, message = '', ''
    headers["x-log-apiversion"] = "0.6.0"
    headers["x-log-signaturemethod"] = "hmac-sha1"
    if body is not None and len(body) > 0:
        content_length = str(len(body))
        content_md5 = hashlib.md5(body).hexdigest().upper()
        headers['Content-MD5'] = content_md5
    date = get_date()
    headers['Date'] = date
    headers['Content-Length'] = content_length
    content_type = headers.get('Content-Type', '')
    message += method + "\n" + content_md5 + \
        "\n" + content_type + "\n" + date + "\n"
    # header
    filter_by_prefix = lambda t: t[0].startswith('x-log-') or t[0].startswith('x-acs-')
    slsHeaders = list(filter(filter_by_prefix, headers.items()))
    sort_by_key = lambda k: k[0]
    for [k, v] in sorted(slsHeaders, key=sort_by_key):
        message += k + ':' + v + "\n"
    # uri and params
    message += uri
    message += '?' if len(params) > 0 else ''
    sep = ''
    for [k, v] in sorted(params.items(), key=sort_by_key):
        message += sep + k + '=' + v
        sep = '&'
    # signature and authorization
    hashed = hmac.new(access_key_secret.encode('utf8'),
                      message.encode('utf8'), hashlib.sha1).digest()
    signature = base64.encodebytes(hashed).decode('utf8').rstrip()
    auth = f'LOG {access_key_id}:{signature}'
    headers['Authorization'] = auth
                            
 
- PHP签名示例代码 - 以下为请求签名的代码示例,仅为参考: - <?php
// returns new headers array
function sign($method, $uri, $accessKeyId, $accessKeySecret, $params, $headers, $body)
{
  $contentLength = 0;
  $headers["x-log-apiversion"] = "0.6.0";
  $headers["x-log-signaturemethod"] = "hmac-sha1";
  if (!is_null($body) && strlen($body) > 0) {
    $contentLength = strlen($body);
    $contentMd5 = strtoupper(md5($body));
    $headers["Content-MD5"] = $contentMd5;
  }
  // date
  setLocale(LC_TIME, 'en_US');
  $date = gmdate('D, d M Y H:i:s \G\M\T', time());
  $headers["Date"] = $date;
  $headers["Content-Length"] = (string)$contentLength;
  $contentType = isset($headers['Content-Type']) ? $headers['Content-Type'] : '';
  $message = $method . "\n" . $contentMd5 . "\n" . $contentType . "\n" . $date . "\n";
  // header
  $filterHeaders = [];
  foreach ($headers as $key => $val) {
    if (str_starts_with($key, 'x-log-') || str_starts_with($key, 'x-acs-')) {
      $filterHeaders[$key] = $val;
    }
  }
  ksort($filterHeaders);
  foreach ($filterHeaders as $key => $val) {
    $message .= $key . ':' . $val . "\n";
  }
  // uri and params
  $message .= $uri;
  if (sizeof($params) > 0) {
    $message .= '?';
  }
  ksort($params);
  $sep = '';
  foreach ($params as $key => $val) {
    $message .= $sep . $key . '=' . $val;
    $sep = '&';
  }
  // signature & authorization
  $signature = base64_encode(hash_hmac('sha1', $message, $accessKeySecret, TRUE));
  $auth = 'LOG ' . $accessKeyId . ':' . $signature;
  $headers['Authorization'] = $auth;
  return $headers;
};
// example call
$headers = sign(
  'POST',
  '/logstores/test-logstore',
  'testAccessId',
  'testAccessKey',
  array(
    "test" => "test",
    "hello" => "world"
  ),
  array(
    "x-log-signaturemethod" => "hmac-sha1",
    "x-log-bodyrawsize" => "0",
    "x-log-apiversion" => "0.6.0"
  ),
  'hello, world'
);
echo ($headers['Authorization']);
foreach ($headers as $key => $val) {
  echo ($key . '=' . $val . "\n");
}
?>
 
- Go签名示例代码 - 以下为请求签名的代码示例,仅为参考: - package sls
import (
    "crypto/hmac"
    "crypto/md5"
    "crypto/sha1"
    "encoding/base64"
    "fmt"
    "sort"
    "strconv"
    "strings"
    "time"
)
/*
* @param uri The uri of http request, exclude host and query param, eg: /logstores/test-logstore
* @param headers This function modifies headers, headers must not be a nil map
* @param method Http method in uppercase , eg: GET, POST, PUT, DELETE
*/
func Sign(method, uri, accessKeyID, accessKeySecret string, headers, queryParams map[string]string, body []byte) error {
    var message, signature string
    var contentMD5, contentType, date string
    headers["x-log-apiversion"] = "0.6.0";
    headers["x-log-signaturemethod"] = "hmac-sha1";
    if len(body) > 0 {
        contentMD5 = fmt.Sprintf("%X", md5.Sum(body))
        headers["Content-MD5"] = contentMD5
    }
    date = time.Now().In(time.FixedZone("GMT", 0)).Format(time.RFC1123)
    headers["Date"] = date
    headers["Content-Length"] = strconv.Itoa(len(body))
    if val, ok := headers["Content-Type"]; ok {
        contentType = val
    }
    message += method + "\n" + contentMD5 + "\n" + contentType + "\n" + date + "\n"
    // header
    slsHeaders := make(map[string]string)
    for k, v := range headers {
        if strings.HasPrefix(k, "x-log-") || strings.HasPrefix(k, "x-acs-") {
            slsHeaders[k] = v
        }
    }
    forEachInOrder(slsHeaders, func(k, v string) {
        message += k + ":" + v + "\n"
    })
    message += uri
    if len(queryParams) > 0 {
        message += "?"
    }
    sep := ""
    forEachInOrder(queryParams, func(k, v string) {
        message += sep + k + "=" + v
        sep = "&"
    })
    // Signature = base64(hmac-sha1(UTF8-Encoding-Of(SignString),AccessKeySecret))
    mac := hmac.New(sha1.New, []byte(accessKeySecret))
    _, err := mac.Write([]byte(message))
    if err != nil {
        return err
    }
    signature = base64.StdEncoding.EncodeToString(mac.Sum(nil))
    auth := fmt.Sprintf("LOG %v:%v", accessKeyID, signature)
    headers["Authorization"] = auth
    return nil
}
func forEachInOrder(m map[string]string, f func(k, v string)) {
    var ss sort.StringSlice
    for k := range m {
        ss = append(ss, k)
    }
    ss.Sort()
    for _, k := range ss {
        f(k, m[k])
    }
}
 
- C#签名示例代码 - 以下为请求签名的代码示例,仅为参考: - using System.Security.Cryptography;
using System.Text;
using StringMap = System.Collections.Generic.Dictionary<string, string>;
void sign(string method, string uri,
string accessKeyId, string accessKeySecret,
StringMap queryParams,
StringMap headers,
byte[] body
)
{
  int contentLength = 0;
  string contentMd5 = "", message = "";
  headers["x-log-apiversion"] = "0.6.0";
  headers["x-log-signaturemethod"] = "hmac-sha1";
  if (body != null && body.Length > 0)
  {
    contentLength = body.Length;
    contentMd5 = BitConverter.ToString(MD5.Create().ComputeHash(body)).Replace("-", "");
    Console.WriteLine(contentMd5);
    headers["Content-MD5"] = contentMd5;
  }
  string date = DateTime.Now.ToUniversalTime().ToString("R");
  headers["Date"] = date;
  headers["Content-Length"] = contentLength.ToString();
  message += method + "\n"
  + contentMd5 + "\n"
  + headers.GetValueOrDefault("Content-Type", "") + "\n"
  + date + "\n";
  var sortedHeader = from entry in headers orderby entry.Key ascending select entry;
  // header
  foreach (var entry in sortedHeader)
  {
    if (entry.Key.StartsWith("x-log-") || entry.Key.StartsWith("x-acs-"))
    {
      message += entry.Key + ":" + entry.Value + "\n";
    }
  }
  // url & params
  message += uri;
  if (queryParams.Count() > 0)
  {
    message += "?";
  }
  var sortedParam = from entry in queryParams orderby entry.Key ascending select entry;
  string sep = "";
  foreach (var entry in sortedParam)
  {
    message += sep + entry.Key + "=" + entry.Value;
    sep = "&";
  }
  var hmac = new HMACSHA1(Encoding.ASCII.GetBytes(accessKeySecret));
  var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(message)));
  var auth = "LOG " + accessKeyId + ":" + signature;
  headers["Authorization"] = auth;
}
 
- C++签名示例代码 - 以下为请求签名的代码示例,其中涉及的辅助函数,需要自行实现。示例中的adapter.h头文件来自于日志服务C++ SDK,建议您参考日志服务C++ SDK进行签名,具体操作,请参见C++ SDK快速入门。 - CodecTool::CalcMD5(string) : 计算MD5值并转为十六进制大写字符串。 
- CodecTool::StartWith(string, string) : 判断字符串是否含有另一字符串作为前缀。 
- CodecTool::Base64Encode(string):Base64编码字符串。 
- CodecTool::CalcHMACSHA1(string, string): 计算hmac-sha1哈希值。 
- CodecTool::GetDateString(): 获取当前时间的格式化字符串,格式为- %a, %d %b %Y %H:%M:%S GMT,例如Mon,3 Jan 2010 08:33:47 GMT。
 
 - #include <map>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "adapter.h"
using namespace std;
using aliyun_log_sdk_v6::CodecTool;
void Sign(const string& httpMethod, const string& uri,
          map<string, string>& httpHeaders,
          const map<string, string>& urlParams, const string& body,
          const string& accessKeyId, const string& accessKeySecret)
{
    string message;
    string contentMd5, signature, contentType;
    httpHeaders["x-log-apiversion"] = "0.6.0";
    httpHeaders["x-log-signaturemethod"] = "hmac-sha1";
    // 1. Content-Md5
    if (!body.empty())
    {
        contentMd5 = CodecTool::CalcMD5(body);
        httpHeaders["Content-Md5"] = contentMd5;
    }
    // 2. Date
    string dateTime = CodecTool::GetDateString();
    httpHeaders["Date"] = dateTime;
    // 3. Content-Length
    string contentLength = std::to_string(body.size());
    httpHeaders["Content-Length"] = contentLength;
    // 4. Content-Type
    if (httpHeaders.find("Content-Type") != httpHeaders.end())
    {
        contentType = httpHeaders["Content-Type"];
    }
    message.append(httpMethod).append("\n");
    message.append(contentMd5).append("\n");
    message.append(contentType).append("\n");
    message.append(dateTime).append("\n");
    // 5. header
    map<string, string> filteredHeaders;
    for (auto it : httpHeaders)
    {
        string key = it.first, value = it.second;
        if (CodecTool::StartWith(key, "x-log-") ||
                 CodecTool::StartWith(key, "x-acs-"))
        {
            filteredHeaders[key] = value;
        }
    }
    for (auto it : filteredHeaders)
    {
        message.append(it.first).append(":").append(it.second);
        message.append("\n");
    }
    // 6. uri and url params
    message.append(uri);
    if (urlParams.size() > 0) message.append("?");
    for (auto it = urlParams.begin(); it != urlParams.end(); ++it)
    {
        if (it != urlParams.begin())
        {
            message.append("&");
        }
        message.append(it->first).append("=").append(it->second);
    }
    // 7.Signature
    signature = CodecTool::Base64Enconde(
        CodecTool::CalcHMACSHA1(message, accessKeySecret));
    // 8. authorization
    httpHeaders["authorization"] =
        "LOG " + accessKeyId + ':' + signature;
}
 
- TypeScript签名示例代码 - 该示例依赖第三方库- crypto-js,安装依赖命令如下:
 - npm install crypto-js --save
 - 以下为请求签名的代码示例,仅为参考: - import CryptoJS from 'crypto-js'
export function sign(
  method: string,
  uri: string,
  access_key_id: string,
  access_key_secret: string,
  params: Map<string, string>,
  headers: Map<string, string>,
  body: string | undefined
) {
  let content_length = 0
  let content_md5 = '',
    message = ''
   headers.set("x-log-apiversion", "0.6.0")
   headers.set("x-log-signaturemethod", "hmac-sha1")
  if (body !== undefined && body.length > 0) {
    content_length = body.length
    content_md5 = CryptoJS.MD5(body).toString(CryptoJS.enc.Hex).toUpperCase()
    headers.set('Content-MD5', content_md5)
  }
  const date = new Date().toUTCString()
  headers.set('Date', date)
  headers.set('Content-Length', content_length.toString())
  message +=
    method +
    '\n' +
    content_md5 +
    '\n' +
    (headers.get('Content-Type') ?? '') +
    '\n' +
    date +
    '\n'
  // headers
  const sort_by_key = (a: [string, string], b: [string, string]) =>
    a[0].localeCompare(b[0])
  const filter_by_prefix = (e: [string, string]) =>
    e[0].startsWith('x-log-') || e[0].startsWith('x-acs-')
  const header_str = [...headers.entries()]
    .filter(filter_by_prefix)
    .sort(sort_by_key)
    .map((e) => e[0] + ':' + e[1] + '\n')
    .join('')
  message += header_str
  // uri & query params
  message += uri
  if (params.size > 0) {
    message += '?'
  }
  message += [...params.entries()]
    .sort(sort_by_key)
    .map((e) => e[0] + '=' + e[1])
    .join('&')
  // signature & authorization
  const signature = CryptoJS.HmacSHA1(message, access_key_secret).toString(
    CryptoJS.enc.Base64
  )
  const auth = 'LOG ' + access_key_id + ':' + signature
  headers.set('Authorization', auth)
}
// example call
sign(
  'POST',
  '/logstores/test-logstore',
  'testAccessId',
  'testAccessKey',
  new Map<string, string>([
    ['test', 'test'],
    ['hello', 'world'],
  ]),
  new Map<string, string>([
    ['x-log-signaturemethod', 'hmac-sha1'],
    ['x-log-bodyrawsize', '0'],
    ['x-log-apiversion', '0.6.0'],
    ['Content-Type', 'application/json'],
  ]),
  'hello, world'
)
                            
 
- Rust签名示例代码 - 该示例依赖库名称及版本如下所示: - [dependencies]
chrono = "0.4.19"
md5 = "0.7.0"
base64 = "0.13.0"
hmac-sha1 = "0.1.3"
 - 以下为请求签名的代码示例,仅为参考: - extern crate base64;
extern crate hmacsha1;
extern crate md5;
use chrono::Utc;
use std::collections::*;
pub fn sign(
    method: &str,
    uri: &str,
    access_key_id: &str,
    access_key_secret: &str,
    params: &HashMap<String, String>,
    headers: &mut HashMap<String, String>,
    body: Option<Vec<u8>>,
) -> String {
    let mut content_length = 0;
    let mut content_md5 = String::from("");
    headers.insert("x-log-apiversion".to_owned(), "0.6.0".to_owned());
    headers.insert("x-log-signaturemethod".to_owned(), "hmac-sha1".to_owned());
    if let Some(content) = body {
        if content.len() > 0 {
            content_length = content.len();
            content_md5 = format!("{:X}", md5::compute(content));
            headers.insert("Content-MD5".to_owned(), content_md5.clone());
        }
    }
    headers.insert("Content-Length".to_owned(), content_length.to_string());
    // date
    let date = Utc::now().format("%a, %d %b %Y %H:%M:%S GMT").to_string();
    headers.insert("Date".to_owned(), date.clone());
    let mut message = "".to_owned();
    let content_type = headers
        .get(&"Content-Type".to_owned())
        .cloned()
        .unwrap_or(String::from(""));
    message += format!("{}\n{}\n{}\n{}\n", method, content_md5, content_type, date).as_str();
    // header
    let mut sorted_header: Vec<_> = headers.iter().collect();
    sorted_header.sort_by_key(|x| x.0);
    for (k, v) in sorted_header {
        if k.starts_with("x-log-") || k.starts_with("x-acs-") {
            message += format!("{}:{}\n", k, v).as_str();
        }
    }
    // url & params
    message += uri;
    if params.len() > 0 {
        message += "?";
    }
    let mut sorted_params: Vec<_> = params.iter().collect();
    sorted_params.sort_by_key(|x| x.0);
    let mut sep = "";
    for (k, v) in sorted_params {
        message += format!("{}{}={}", sep, k, v).as_str();
        sep = "&";
    }
    let signature = base64::encode(hmacsha1::hmac_sha1(
        access_key_secret.as_bytes(),
        message.as_bytes(),
    ));
    let auth = format!("LOG {}:{}", access_key_id, signature);
    headers.insert("Authorization".to_owned(), auth.to_owned());
    auth
}