C#为含特殊字符的Azure Blob路径生成SAS令牌报错排查
签名校验失败的核心原因是旧版Azure Storage SDK(经典版Microsoft.WindowsAzure.Storage,即v11及更早版本)的SAS签名计算逻辑,和你手动拼接SAS URI时的路径编码逻辑不一致。
你代码里直接用字符串相加的方式拼接cloudBlockBlob.Uri + sasUri时,cloudBlockBlob.Uri返回的是带原始特殊字符的未编码路径(比如空格、#都保留原字符),但SDK计算签名时用的是符合Azure鉴权规范的标准编码后路径,两份用于校验的路径字符串不一致,自然会返回Signature did not match错误。
你之前尝试手动预编码路径再传入CloudBlockBlob触发二次编码的问题,本质是编码环节搞错了:CloudBlockBlob构造时要求传入原始未编码的Blob名称,预编码后的字符串会被SDK当做普通字符再次编码,才会出现%20被转成%2520的问题。
补充说明里提到的容器名规则是Azure存储的硬限制:容器名仅支持小写字母、数字、短横线,不能包含特殊字符和大写字母,但Blob名称(含虚拟文件夹路径)本身支持特殊字符,只要签名环节编码正确就可正常访问,不需要做命名限制。
方案1:兼容现有旧版SDK
不需要重构现有SDK引用,只要把手动字符串拼接SAS URI的逻辑改掉,用.NET内置Uri类的构造方法做路径拼接即可,SDK内部的编码逻辑会自动对齐:
public static Uri GetServiceSasUriForCloudBlockBlob(CloudBlockBlob cloudBlockBlob, string permissions = "r") { var sasBuilder = new SharedAccessBlobPolicy() { SharedAccessStartTime = DateTimeOffset.UtcNow.AddMinutes(-5), SharedAccessExpiryTime = DateTimeOffset.UtcNow.AddMinutes(5), Permissions = SharedAccessBlobPolicy.PermissionsFromString(permissions) }; var sasToken = cloudBlockBlob.GetSharedAccessSignature(sasBuilder); // 用Uri构造函数自动处理路径编码,不要直接字符串拼接 return new Uri(cloudBlockBlob.Uri, sasToken); }
注意:初始化
CloudBlockBlob实例时,传入的Blob路径必须是原始未编码的字符串(例如直接传Folder #1/file_1.txt,不要传预编码的Folder%20%231/file_1.txt),否则仍会触发二次编码问题。
方案2:升级到新版SDK(长期维护推荐)
你目前用的旧版WindowsAzure.Storage SDK已经停止官方维护,推荐升级到正式发布的v12+版本Azure.Storage.Blobs,新版SDK从接口设计上规避了手动拼接路径导致的编码问题,SAS生成逻辑更简洁:
public static Uri GetServiceSasUriForBlob(BlobClient blobClient, string permissions = "r") { var sasBuilder = new BlobSasBuilder { StartsOn = DateTimeOffset.UtcNow.AddMinutes(-5), ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(5), BlobContainerName = blobClient.BlobContainerName, BlobName = blobClient.Name, Resource = "b" }; // 后续扩展权限直接修改permissions参数即可,例如"rw"对应读+写 sasBuilder.SetPermissions(BlobSasPermissions.Parse(permissions)); return blobClient.GenerateSasUri(sasBuilder); }
新版SDK的GenerateSasUri方法会自动完成路径编码、签名计算全流程,不需要手动处理任何路径拼接逻辑,从根源上避免签名不匹配问题。
修复后,包含空格、#、中文等任意合法Blob字符的路径都可以正常生成可访问的SAS链接,和Azure门户、Storage Explorer生成的SAS效果完全一致,不需要额外限制文件夹、文件的命名规则。
内容的提问来源于stack exchange,提问作者Jim de Vries

