通过PowerShell REST API生成Azure Blob SAS令牌及认证报错解决
Azure存储PowerShell REST API生成SAS/认证报错解决方案
核心错误点
- 你混淆了Shared Key认证和SAS令牌认证两种认证逻辑:代码里构造的是SAS的签名字段,最终请求头却用了
SharedKey的授权格式,两种认证的StringToSign规则完全不同 - 换行符使用错误:PowerShell里的换行符要用
`n而不是\n,\n在PowerShell字符串里会被当成普通字符处理,不会解析为换行 - StringToSign不符合规范:从服务端返回的待签名字符串可以看到,2012-02-12版本的Blob服务Get操作的Shared Key待签名字符串需要包含HTTP动词、内容长度、请求头、资源路径等多个字段,空字段也要留空占位,你自行构造的StringToSign缺失了大量必填字段
- 变量大小写不一致:代码里
signedpermissions和signedPermissions、signedstart和signedStart大小写不统一,拼接时可能取到空值 - 你最终需求是生成SAS令牌,不需要用Shared Key请求头,直接把签名后的参数拼接成查询字符串附加到资源URL即可
修复后的可运行代码(纯原生PowerShell,无需Az模块)
Function Get-MyBlob { param( [string]$StorageAccountName, [string]$StorageContainerName, [string]$BlobName, [string]$StorageAccountKey, [string]$Permissions = "r", [int]$ValidityHours = 24 ) # 时间必须转换为UTC时间,格式符合ISO 8601标准 $startTime = (Get-Date).ToUniversalTime() $expiryTime = $startTime.AddHours($ValidityHours) $signedStart = $startTime.ToString("yyyy-MM-ddTHH:mm:ssZ") $signedExpiry = $expiryTime.ToString("yyyy-MM-ddTHH:mm:ssZ") $signedVersion = "2021-06-08" # 可替换为你需要的API版本 $signedResource = "b" # b代表Blob级别,c代表容器级别 $canonicalizedResource = "/blob/$StorageAccountName/$StorageContainerName/$BlobName" # 严格按SAS规范构造待签名字符串,空字段必须留空占位,顺序不能调换 $StringToSign = @( $Permissions, $signedStart, $signedExpiry, $canonicalizedResource, "", "", "https", $signedVersion, $signedResource, "", "", "", "", "", "" ) -join "`n" # 计算HMACSHA256签名 $sharedKey = [System.Convert]::FromBase64String($StorageAccountKey) $hasher = [System.Security.Cryptography.HMACSHA256]::new($sharedKey) $signature = [System.Convert]::ToBase64String($hasher.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($StringToSign))) # 拼接SAS查询参数,特殊字符需要URL编码 $sasParams = [ordered]@{ sv = $signedVersion st = [System.Web.HttpUtility]::UrlEncode($signedStart) se = [System.Web.HttpUtility]::UrlEncode($signedExpiry) sr = $signedResource sp = $Permissions spr = "https" sig = [System.Web.HttpUtility]::UrlEncode($signature) } $sasToken = ($sasParams.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join "&" $blobUrl = "https://$StorageAccountName.blob.core.windows.net/$StorageContainerName/$BlobName?$sasToken" # 直接用带SAS的URL请求,不需要额外添加授权头 return Invoke-RestMethod -Uri $blobUrl -Method Get } # 调用示例 Get-MyBlob -StorageAccountName "MYSTORAGEACCOUNT" -StorageContainerName "MYCONTAINER" -BlobName "coloured-paper.jpg" -StorageAccountKey "MYSTORAGEACCOUNTACCESSKEY"
额外说明
- 如果需要生成容器级别的SAS,只需把
signedResource改为c,canonicalizedResource改为/blob/$StorageAccountName/$StorageContainerName即可 - 签名参数的顺序不能随意调换,必须严格按规范顺序拼接,否则会出现签名不匹配的错误
- 所有时间必须使用UTC时间,否则会出现时间不在有效期内的报错
内容的提问来源于stack exchange,提问作者Azhar
相关产品推荐
相关产品推荐

