当用于 Blob 存储的 Azure REST API 使用具有前缀或标记的查询字符串时获取 403

Posted

技术标签:

【中文标题】当用于 Blob 存储的 Azure REST API 使用具有前缀或标记的查询字符串时获取 403【英文标题】:Getting 403 when Azure REST API for blob storage with query strings having prefix or marker 【发布时间】:2020-05-29 08:57:40 【问题描述】:

我正在尝试使用 Azure REST API 进行 Blob 存储,并且正在使用来自 https://github.com/Azure-Samples/storage-dotnet-rest-api-with-auth 的 AzureStorageAuthenticationHelper 来制作授权标头。

如果我这样做:

private static void DoItViaRest(string containerName, ILogger log)

    try
    
        string uri = string.Format("https://0.blob.core.windows.net/1?restype=container&comp=list", STORAGE_ACCOUNT_NAME, containerName);
        byte[] requestPayload = null;
        string xmlString = CallStorageRESTAPI(uri, requestPayload, log).Result;
    
    catch (Exception e)
    
        // handle exception
    

    private static async Task<string> CallStorageRESTAPI(string uri, byte[] requestPayload, ILogger log)
    
        string response = string.Empty;

        using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, uri)  Content = (requestPayload == null) ? null : new ByteArrayContent(requestPayload) )
        
            DateTime now = DateTime.UtcNow;
            httpRequestMessage.Headers.Add("x-ms-date", now.ToString("R"));
            httpRequestMessage.Headers.Add("x-ms-version", "2017-07-29");
            httpRequestMessage.Headers.Authorization = AzureStorageAuthenticationHelper.GetAuthorizationHeader(STORAGE_ACCOUNT_NAME, STORAGE_ACCOUNT_KEY, now, httpRequestMessage);
            using (HttpResponseMessage httpResponseMessage = await new HttpClient().SendAsync(httpRequestMessage))
            
                if (httpResponseMessage.StatusCode == HttpStatusCode.OK)
                
                    response = await httpResponseMessage.Content.ReadAsStringAsync();
                
                else
                
                    log.LogInformation(string.Format("REST returned 0", httpResponseMessage.StatusCode.ToString()));
                
            
        
        return response;
    

然后由 AzureStorageAuthenticationHelper.GetAuthorizationHeader(STORAGE_ACCOUNT_NAME, STORAGE_ACCOUNT_KEY, now, httpRequestMessage) 创建的授权工作正常。

但是,如果我在 URI 的查询字符串中添加前缀或标记,那么我最终会得到 403。 带前缀的例子: https://0.blob.core.windows.net/1?restype=container&comp=list&prefix=2 https://0.blob.core.windows.net/1?restype=container&prefix=2&comp=list 带有标记的示例: https://0.blob.core.windows.net/1?restype=container&comp=list&marker=2 https://0.blob.core.windows.net/1?restype=container&marker=2&comp=list

上面的标记值是原始调用返回的 NextMarker。

我不知道出了什么问题或如何解决它。有什么建议吗?

【问题讨论】:

【参考方案1】:

我相信您已经发现 Github 存储库中的代码存在问题。

基本上问题是由于以下代码行:

    foreach (var item in values.AllKeys.OrderBy(k => k))
    
        sb.Append('\n').Append(item).Append(':').Append(values[item]);
    

    return sb.ToString().ToLower();

基本上这段代码所做的是获取所有查询字符串参数(名称和值)并以name:value 格式附加它们,然后最终将整个字符串转换为小写(这就是问题所在)。

根据documentation,应该只将键名转换为小写而不是值部分。

要解决此问题,请使用以下代码:

    /// <summary>
    /// This part of the signature string represents the storage account 
    ///   targeted by the request. Will also include any additional query parameters/values.
    /// For ListContainers, this will return something like this:
    ///   /storageaccountname/\ncomp:list
    /// </summary>
    /// <param name="address">The URI of the storage service.</param>
    /// <param name="accountName">The storage account name.</param>
    /// <returns>String representing the canonicalized resource.</returns>
    private static string GetCanonicalizedResource(Uri address, string storageAccountName)
    
        // The absolute path is "/" because for we're getting a list of containers.
        StringBuilder sb = new StringBuilder("/").Append(storageAccountName).Append(address.AbsolutePath);

        // Address.Query is the resource, such as "?comp=list".
        // This ends up with a NameValueCollection with 1 entry having key=comp, value=list.
        // It will have more entries if you have more query parameters.
        NameValueCollection values = HttpUtility.ParseQueryString(address.Query);

        foreach (var item in values.AllKeys.OrderBy(k => k))
        
            sb.Append('\n').Append(item.ToLower()).Append(':').Append(values[item]);
        

        return sb.ToString();//We should not be converting entire thing to lower case.
    

但是,请注意,这仍然不是 100% 正确,因为它没有考虑上面屏幕截图中提到的第 9 点,但为了您的目的,它应该可以工作。

【讨论】:

解决了。非常感谢。我不知道我盯着输出看了多少小时,也没注意到标记值是小写的。

以上是关于当用于 Blob 存储的 Azure REST API 使用具有前缀或标记的查询字符串时获取 403的主要内容,如果未能解决你的问题,请参考以下文章

REST Api 使用访问密钥到 Azure Blob 存储

使用 REST API 上传到 Azure Blob 存储时,Zip 档案损坏

请求令牌时如何在 Azure 存储 Blob 中为 REST 请求指定范围? [AZURE-BLOB][REST API]

仅从 Azure 存储 [Azure-Blob][REST] 中的 Blob 列表获取特定元数据

在 Azure 存储 [REST] [Azure Blob] 中对 PUT Blob 的 REST api 调用中的身份验证失败

向 Azure Blob 存储发出 GET 请求时授权失败 [REST API][Azure Blob 存储]