請求簽名
為保證用戶日志數(shù)據(jù)的安全,日志服務(wù)API的所有HTTP請求都必須經(jīng)過安全驗證。驗證流程基于阿里云的訪問密鑰安全驗證,使用非對稱加密算法完成。
使用說明
日志服務(wù)SDK封裝了日志服務(wù)的所有API接口,您可以通過日志服務(wù)SDK方便地調(diào)用日志服務(wù)的所有API接口。日志服務(wù)SDK支持自動簽名,并支持多語言版本。推薦使用日志服務(wù)SDK訪問日志服務(wù),更多信息,請參見日志服務(wù)SDK。
安全驗證能達到以下目的:
確認哪位用戶在做API請求。
因為在發(fā)送請求前需要用戶指定生成數(shù)字簽名的密鑰對,在服務(wù)端即可通過該密鑰對確定用戶身份,進而可做訪問權(quán)限管理。
確認用戶請求在網(wǎng)絡(luò)傳輸過程中是否被篡改。
因為服務(wù)端會對接收到的請求內(nèi)容重新計算數(shù)字簽名,若請求內(nèi)容在網(wǎng)絡(luò)上被篡改,則無法通過數(shù)字簽名比對。
為API請求生成簽名,需使用一對訪問密鑰。您可以使用已經(jīng)存在的訪問密鑰對,也可以創(chuàng)建新的訪問密鑰對,但需要保證使用的密鑰對為啟用狀態(tài)。
下文介紹日志服務(wù)請求組成結(jié)構(gòu)、算法流程,并給出各語言簽名算法的示例代碼。
請求組成結(jié)構(gòu)
合法的日志服務(wù)請求需要在HTTP請求頭內(nèi)寫入如下鍵值對(大小寫敏感):
字段名 | 說明 | 示例 |
x-log-signaturemethod | 請求的加密方式。 | hmac-sha1 |
x-log-apiversion | 訪問的API版本。 | 0.6.0 |
Date | HTTP請求中的標準時間戳頭,其遵循RFC822/RFC1123格式,格式化字符串為 | Mon, 3 Jan 2010 08:33:47 GMT |
Content-MD5 | HTTP請求中Body部分的MD5值,必須轉(zhuǎn)換成十六進制大寫英文字母字符串。 | 72A15D7DE7EE9E7BB86461FFEA9499 |
Authorization | 簽名,內(nèi)容格式為 | 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"}
簽名算法流程
簽名結(jié)構(gòu)中最為重要的部分是Authorization
字段,其格式為LOG accessKeyId:Signature
,由用戶密鑰 AccessKeyId
與簽名字符串Signature
拼接得到。這里介紹簽名字符串Signature
的構(gòu)建過程。
簽名消息體message
簽名字符串Signature由簽名消息體message加密編碼得到,簽名消息體message的構(gòu)建過程如下:
名稱 | 內(nèi)容與格式 | 示例 |
method | HTTP請求方法,全大寫英文。取值包括GET、POST、PUT和DELETE。 | GET |
Content-MD5 | HTTP請求消息Body的MD5哈希值,轉(zhuǎn)換為十六進制大寫英文字符串。若Body為空,此項為空字符串。 | 49DFDD54B01CBCD2D2AB5E9E5EE6B9B9 |
Content-Type | HTTP請求中Body部分的類型。若Body為空,此項可為空字符串。 | application/json |
Date | 當(dāng)前時間的格式化字符串。格式為RFC822/RFC1123,格式化字符串為 | Mon, 3 Jan 2010 08:33:47 GMT |
header | header中以 |
|
uri | 請求的路徑,不包括域名與請求參數(shù)部分。 | /logstores/test-logstore |
query參數(shù) | 請求的查詢參數(shù),按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。
請求簽名過程示例
為了幫助您更好地理解整個請求簽名的流程,我們用兩個示例來演示整個過程。首先,假設(shè)您用做日志服務(wù)API簽名的訪問密鑰對如下:
AccessKeyId = "bq2sjzesjmo86kq****"
AccessKeySecret = "4fdO2fTDDnZPU/L7CHNd****"
示例一
您需要發(fā)送如下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****
最后發(fā)送經(jīng)數(shù)字簽名的HTTP請求內(nèi)容如下:
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定義需要構(gòu)建如下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 <日志內(nèi)容序列化成ProtoBuffer格式的字節(jié)流>
在這個HTTP請求中,寫入的日志內(nèi)容首先被序列化成ProtoBuffer格式(請參見數(shù)據(jù)編碼方式了解該格式的更多細節(jié))后作為請求Body。所以該請求的Content-Type頭的值指定為application/x-protobuf。類似,Content-MD5頭的值是請求body對應(yīng)的MD5值。按照上面的簽名字符串構(gòu)造方式,這個請求對應(yīng)的簽名消息體為:
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****
最后發(fā)送經(jīng)數(shù)字簽名的HTTP請求內(nèi)容如下:
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**** <日志內(nèi)容序列化成ProtoBuffer格式的字節(jié)流>
代碼示例
該示例依賴第三方庫
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); } }
以下為請求簽名的代碼示例,僅為參考:
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 // 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"); } ?>
以下為請求簽名的代碼示例,僅為參考:
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]) } }
以下為請求簽名的代碼示例,僅為參考:
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; }
以下為請求簽名的代碼示例,其中涉及的輔助函數(shù),需要自行實現(xiàn)。示例中的adapter.h頭文件來自于日志服務(wù)C++ SDK,建議您參考日志服務(wù)C++ SDK進行簽名,具體操作,請參見C++ SDK快速入門。
CodecTool::CalcMD5(string) : 計算MD5值并轉(zhuǎn)為十六進制大寫字符串。
CodecTool::StartWith(string, string) : 判斷字符串是否含有另一字符串作為前綴。
CodecTool::Base64Encode(string):Base64編碼字符串。
CodecTool::CalcHMACSHA1(string, string): 計算hmac-sha1哈希值。
CodecTool::GetDateString(): 獲取當(dāng)前時間的格式化字符串,格式為
%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; }
該示例依賴第三方庫
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' )
該示例依賴庫名稱及版本如下所示:
[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 }