Получение 403 при Azure REST API для хранения больших двоичных объектов со строками запроса, имеющими префикс или маркер - PullRequest
1 голос
/ 13 февраля 2020

Я пытаюсь использовать Azure REST API для хранения больших двоичных объектов и использую AzureStorageAuthenticationHelper из https://github.com/Azure-Samples/storage-dotnet-rest-api-with-auth для создания заголовка авторизации.

Если я сделаю это:

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, теперь httpRequestMessage), работает нормально.

Однако, если я добавлю тогда префикс IRI или маркер запроса к маркеру в итоге получим 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 Ответ

0 голосов
/ 13 февраля 2020

Я полагаю, что вы обнаружили проблему с кодом в репозитории 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, следует преобразовывать только имя ключа в нижний регистр, а не часть значения.

enter image description here

Чтобы устранить эту проблему, используйте следующий код:

    /// <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, упомянутый на скриншоте выше, но для вашей цели это должно работать.

...