BlobSasBuilder生成的Azure SAS URI签名无效问题排查
我编写了一个方法来返回Azure存储容器的SAS URI,代码运行正常且生成的URI格式看似有效,但使用时出现如下错误:
<?xml version="1.0" encoding="utf-8"?> <Error> <Code>AuthenticationFailed</Code> <Message> Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. RequestId:bdfae000-901e-00e2-093a-87e256000000 Time:2024-04-05T09:19:45.7818447Z </Message> <AuthenticationErrorDetail> Signature did not match. String to sign used was rl 2024-04-06T09:19:24Z /blob/tmfootballdev/$root b066bc3e-48c7-42d6-a041-9b900744166f eb272a1e-e767-4041-9363-f8736579f898 2024-04-05T09:04:15Z 2024-04-06T09:19:15Z b 2023-11-03 https 2023-11-03 c </AuthenticationErrorDetail> </Error>
我通过以下代码生成User Delegation SAS:
public async Task<Uri> GetContainerSas(BlobContainerClient containerClient) { // Construct the blob endpoint from the account name. string endpoint = $"https://{_accountName}.blob.core.windows.net"; // Create a blob service client object using DefaultAzureCredential BlobServiceClient blobServiceClient = new( new Uri(endpoint), new DefaultAzureCredential()); // Get a user delegation key for the Blob service that's valid for 1 day UserDelegationKey userDelegationKey = await blobServiceClient.GetUserDelegationKeyAsync( DateTimeOffset.UtcNow.AddMinutes(-15), DateTimeOffset.UtcNow.AddDays(1)); // Create a SAS token for the blob resource that's also valid for 1 day BlobSasBuilder sasBuilder = new() { BlobContainerName = containerClient.Name, Resource = "c", ExpiresOn = DateTimeOffset.UtcNow.AddDays(1), Protocol = SasProtocol.Https }; // Specify the necessary permissions sasBuilder.SetPermissions( BlobSasPermissions.List | BlobSasPermissions.Read); // Add the SAS token to the blob URI BlobUriBuilder uriBuilder = new(containerClient.Uri) { // Specify the user delegation key Sas = sasBuilder.ToSasQueryParameters( userDelegationKey, containerClient.AccountName) }; return uriBuilder.ToUri(); }
代码基于微软官方文档修改,适配容器级而非单个Blob。生成的无效SAS URI示例(签名已隐藏):
https://<account>.blob.core.windows.net/test ?skoid=b066bc3e-48c7-42d6-a041-9b900744166f &sktid=eb272a1e-e767-4041-9363-f8736579f898 &skt=2024-04-05T09%3A04%3A15Z &ske=2024-04-06T09%3A19%3A15Z &sks=b &skv=2023-11-03 &sv=2023-11-03 &spr=https &se=2024-04-06T09%3A19%3A24Z &sr=c &sp=rl &sig=xxxxxx
对比Azure门户生成的有效SAS URI(账户密钥生成):
https://<account>.blob.core.windows.net/test ?sp=rl &st=2024-04-05T08:52:05Z&se=2024-04-05T16:52:05Z &spr=https &sv=2022-11-02 &sr=c &sig=xxxxxx
我遗漏了什么?
更新:问题可能与生成User Delegation密钥的RBAC权限不足有关,测试用的是账户密钥生成的。我会修复RBAC后验证,但疑惑为何权限不足却能生成URI而非抛出异常。
核心问题:签名字符串中的容器不匹配
从错误信息的AuthenticationErrorDetail可以看到,服务端用于验证的签名字符串中,目标容器是/blob/tmfootballdev/$root,但你实际要访问的是test容器。这说明生成SAS时,代码使用的容器信息与目标容器不一致,请检查传入的containerClient是否确实指向test容器,而非$root容器。User Delegation Key权限问题
生成User Delegation SAS要求当前身份具备以下权限:- 至少拥有
Microsoft.Storage/storageAccounts/blobServices/generateUserDelegationKey/action权限 - 或具备Storage Blob Data Contributor等内置角色
如果权限不足,
GetUserDelegationKeyAsync不会直接抛出异常,但返回的密钥是无效的,导致后续生成的SAS无法通过签名验证。- 至少拥有
其他需要检查的点
- 确认
_accountName与containerClient.AccountName是同一个存储账户,避免因账户不一致导致签名错误 - 检查本地时钟与Azure服务时钟是否同步,时间偏差可能导致SAS有效期验证失败
- 验证
BlobSasBuilder的参数:Resource = "c"是容器级SAS的正确取值,权限rl(List+Read)也符合需求
- 确认
关于权限不足却能生成URI的疑问
Azure存储服务在调用GetUserDelegationKeyAsync时,不会实时校验权限的有效性,仅会返回格式合法的密钥。只有在使用SAS发起请求时,服务端才会验证签名的合法性,因此会出现“能生成URI但无法使用”的情况。
内容的提问来源于stack exchange,提问作者Jakob Busk Sørensen

