你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
对 HTTP 请求进行签名
在本教程中,你将了解如何使用 HMAC 签名对 HTTP 请求进行签名。
注意
强烈建议使用 Azure SDK。 此处介绍的方法适用于出于任何原因无法使用 Azure SDK 的情况的回退选项。
先决条件
在开始之前,请务必:
- 创建活动订阅的 Azure 帐户。 有关详细信息,请参阅创建免费账户。
- 安装 Visual Studio。
- 创建 Azure 通信服务资源。 有关详细信息,请参阅创建 Azure 通信服务资源。 对于本教程,你需要记录 resourceEndpoint 和 resourceAccessKey 。
使用 C# 对 HTTP 请求进行签名
访问密钥身份验证会使用共享密钥为每个 HTTP 请求生成一个 HMAC 签名。 此签名是使用 SHA256 算法生成的,通过 HMAC-SHA256
方案在 Authorization
标头中发送。 例如:
Authorization: "HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature=<hmac-sha256-signature>"
hmac-sha256-signature
包括:
- HTTP 谓词(例如
GET
或PUT
) - HTTP 请求路径
- x-ms-date
- 主机
- x-ms-content-sha256
设置
以下步骤介绍如何构造授权标头。
新建 C# 应用程序
在控制台窗口(例如 cmd、PowerShell 或 Bash)中,使用 dotnet new
命令创建名为 SignHmacTutorial
的新控制台应用。 此命令将创建包含单个源文件的简单“Hello World”C# 项目:Program.cs。
dotnet new console -o SignHmacTutorial
将目录更改为新创建的应用文件夹。 使用 dotnet build
命令来编译应用程序。
cd SignHmacTutorial
dotnet build
安装包
安装用于正文序列化的 Newtonsoft.Json
包。
dotnet add package Newtonsoft.Json
更新 Main
方法声明以支持异步代码。 使用以下代码开始操作。
using System;
using System.Globalization;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace SignHmacTutorial
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Azure Communication Services - Sign an HTTP request Tutorial");
// Tutorial code goes here.
}
}
}
创建请求消息
在此示例中,我们将使用通信服务身份验证 API(版本 2021-03-07
)对请求进行签名,以创建新的标识。
将以下代码添加到 Main
方法中。
string resourceEndpoint = "resourceEndpoint";
// Create a uri you are going to call.
var requestUri = new Uri($"{resourceEndpoint}/identities?api-version=2021-03-07");
// Endpoint identities?api-version=2021-03-07 accepts list of scopes as a body
var body = new
{
createTokenWithScopes = new[] { "chat" }
};
var serializedBody = JsonConvert.SerializeObject(body);
var requestMessage = new HttpRequestMessage(HttpMethod.Post, requestUri)
{
Content = new StringContent(serializedBody, Encoding.UTF8, "application/json")
};
将 resourceEndpoint
替换为实际的资源终结点值。
创建内容哈希
内容哈希是 HMAC 签名的一部分。 使用以下代码来计算内容哈希。 可将此方法添加到 Main
方法下的 Program.cs
。
static string ComputeContentHash(string content)
{
using var sha256 = SHA256.Create();
byte[] hashedBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(content));
return Convert.ToBase64String(hashedBytes);
}
计算签名
使用以下代码来创建方法,以便计算 HMAC 签名。
static string ComputeSignature(string stringToSign)
{
string secret = "resourceAccessKey";
using var hmacsha256 = new HMACSHA256(Convert.FromBase64String(secret));
var bytes = Encoding.UTF8.GetBytes(stringToSign);
var hashedBytes = hmacsha256.ComputeHash(bytes);
return Convert.ToBase64String(hashedBytes);
}
将 resourceAccessKey
替换为实际的通信服务资源的访问密钥。
创建授权标头字符串
现在,我们将构造要添加到授权标头的字符串。
- 为要签名的标头准备值。
- 使用协调世界时 (UTC) 时区指定当前时间戳。
- 获取请求机构(DNS 主机名或 IP 地址以及端口号)。
- 计算内容哈希。
- 准备要签名的字符串。
- 计算该签名。
- 连接将要在授权标头中使用的该字符串。
将以下代码添加到 Main
方法中。
// Specify the 'x-ms-date' header as the current UTC timestamp according to the RFC1123 standard
var date = DateTimeOffset.UtcNow.ToString("r", CultureInfo.InvariantCulture);
// Get the host name corresponding with the 'host' header.
var host = requestUri.Authority;
// Compute a content hash for the 'x-ms-content-sha256' header.
var contentHash = ComputeContentHash(serializedBody);
// Prepare a string to sign.
var stringToSign = $"POST\n{requestUri.PathAndQuery}\n{date};{host};{contentHash}";
// Compute the signature.
var signature = ComputeSignature(stringToSign);
// Concatenate the string, which will be used in the authorization header.
var authorizationHeader = $"HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature={signature}";
向 requestMessage 添加标头
使用以下代码将所需标头添加到 requestMessage
。
// Add a date header.
requestMessage.Headers.Add("x-ms-date", date);
// Add a host header.
// In C#, the 'host' header is added automatically by the 'HttpClient'. However, this step may be required on other platforms such as Node.js.
// Add a content hash header.
requestMessage.Headers.Add("x-ms-content-sha256", contentHash);
// Add an authorization header.
requestMessage.Headers.Add("Authorization", authorizationHeader);
测试客户端
通过使用 HttpClient
来调用终结点,并检查响应。
HttpClient httpClient = new HttpClient
{
BaseAddress = requestUri
};
var response = await httpClient.SendAsync(requestMessage);
var responseString = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseString);
先决条件
在开始之前,请务必:
- 创建活动订阅的 Azure 帐户。 有关详细信息,请参阅创建免费账户。
- 下载并安装 Python。
- 下载并安装 Visual Studio Code 或其他支持 Python 的 IDE。
- 创建 Azure 通信服务资源。 有关详细信息,请参阅创建 Azure 通信服务资源。 本教程需要 resource_endpoint_name 和 resource_endpoint_secret。
使用 Python 对 HTTP 请求进行签名
访问密钥身份验证会使用共享密钥为每个 HTTP 请求生成一个 HMAC 签名。 此签名是使用 SHA256 算法生成的,通过 HMAC-SHA256
方案在 Authorization
标头中发送。 例如:
Authorization: "HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature=<hmac-sha256-signature>"
hmac-sha256-signature
包括:
- HTTP 谓词(例如
GET
或PUT
) - HTTP 请求路径
- x-ms-date
- 主机
- x-ms-content-sha256
设置
以下步骤介绍如何构造授权标头。
创建新 Python 脚本
打开 Visual Studio Code 或其他 IDE 或所选编辑器,并创建一个名为 sign_hmac_tutorial.py
的新文件。 将此文件保存到已知文件夹。
添加所需的导入
首先使用以下代码更新 sign_hmac_tutorial.py
脚本。
import base64
import hashlib
import hmac
import json
from datetime import datetime, timezone
from urllib import request
准备请求的数据
在此示例中,我们将使用通信服务身份验证 API(版本 2021-03-07
)对请求进行签名,以创建新的标识。
将以下代码添加到 sign_hmac_tutorial.py
脚本中。
- 将
resource_endpoint_name
替换为实际的资源终结点名称值。 可在 Azure 通信服务资源的“概述”部分找到此值。 它是“https://”后面的“Endpoint”的值。 - 将
resource_endpoint_secret
替换为实际的资源终结点密码值。 可在 Azure 通信服务资源的“密钥”部分找到此值。 它是“Key”的值 - 主要或辅助密钥。
host = "resource_endpoint_name"
resource_endpoint = f"https://{host}"
path_and_query = "/identities?api-version=2021-03-07"
secret = "resource_endpoint_secret"
# Create a uri you are going to call.
request_uri = f"{resource_endpoint}{path_and_query}"
# Endpoint identities?api-version=2021-03-07 accepts list of scopes as a body.
body = { "createTokenWithScopes": ["chat"] }
serialized_body = json.dumps(body)
content = serialized_body.encode("utf-8")
创建内容哈希
内容哈希是 HMAC 签名的一部分。 使用以下代码来计算内容哈希。 可将此方法添加到 sign_hmac_tutorial.py
脚本。
def compute_content_hash(content):
sha_256 = hashlib.sha256()
sha_256.update(content)
hashed_bytes = sha_256.digest()
base64_encoded_bytes = base64.b64encode(hashed_bytes)
content_hash = base64_encoded_bytes.decode('utf-8')
return content_hash
计算签名
使用以下代码来创建方法,以便计算 HMAC 签名。
def compute_signature(string_to_sign, secret):
decoded_secret = base64.b64decode(secret)
encoded_string_to_sign = string_to_sign.encode('utf-8')
hashed_bytes = hmac.digest(decoded_secret, encoded_string_to_sign, digest=hashlib.sha256)
encoded_signature = base64.b64encode(hashed_bytes)
signature = encoded_signature.decode('utf-8')
return signature
根据 RFC1123 标准获取当前的 UTC 时间戳
使用以下代码获取独立于区域设置的所需日期格式。
def format_date(dt):
days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
utc = dt.utctimetuple()
return "{}, {:02} {} {:04} {:02}:{:02}:{:02} GMT".format(
days[utc.tm_wday],
utc.tm_mday,
months[utc.tm_mon-1],
utc.tm_year,
utc.tm_hour,
utc.tm_min,
utc.tm_sec)
创建授权标头字符串
现在,我们将构造要添加到授权标头的字符串。
- 为要签名的标头准备值。
- 使用协调世界时 (UTC) 时区指定当前时间戳。
- 获取请求机构(DNS 主机名或 IP 地址以及端口号)。
- 计算内容哈希。
- 准备要签名的字符串。
- 计算该签名。
- 连接将要在授权标头中使用的该字符串。
将以下代码添加到 sign_hmac_tutorial.py
脚本中。
# Specify the 'x-ms-date' header as the current UTC timestamp according to the RFC1123 standard
utc_now = datetime.now(timezone.utc)
date = format_date(utc_now)
# Compute a content hash for the 'x-ms-content-sha256' header.
content_hash = compute_content_hash(content)
# Prepare a string to sign.
string_to_sign = f"POST\n{path_and_query}\n{date};{host};{content_hash}"
# Compute the signature.
signature = compute_signature(string_to_sign, secret)
# Concatenate the string, which will be used in the authorization header.
authorization_header = f"HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature={signature}"
添加标头
使用以下代码添加所需标头。
request_headers = {}
# Add a date header.
request_headers["x-ms-date"] = date
# Add content hash header.
request_headers["x-ms-content-sha256"] = content_hash
# Add authorization header.
request_headers["Authorization"] = authorization_header
# Add content type header.
request_headers["Content-Type"] = "application/json"
测试客户端
调用终结点并检查响应。
req = request.Request(request_uri, content, request_headers, method='POST')
with request.urlopen(req) as response:
response_string = json.load(response)
print(response_string)
清理资源
若要清理并删除通信服务订阅,请删除资源或资源组。 删除资源组同时也会删除与之相关联的任何其他资源。 你可以深入了解如何清理 Azure 通信服务资源以及如何清理 Azure Functions 资源。
后续步骤
你可能还需要: