azure - 对 Azure 表的 POST 请求中的授权 header

标签 azure header authorization azure-table-storage

为了将实体插入 Azure 表,我已经完成并尝试了所有方法,但到目前为止,我仍然收到相同的错误“StatusCode:403,ReasonPhrase:'服务器无法验证请求。请确保该值授权 header 的格式正确,包括签名。'"

现在,我尝试使用 ShareKey 和 SharedKeyLite(Azure 存储资源管理器使用 SharedKeyLite)

public static async Task<string> InsertEntityAsync(string tableName, Position position)
    {
        string uri = @"https://" + Utilities.Account + ".table.core.windows.net/" + tableName;
        return await Utilities.UploadEntityAsync(tableName,uri,position);
    }
public static async Task<string> UploadEntityAsync(string urlPath, string uri, Position position)
    {
        string body = buildBodyForInsertOperation(position);

        HttpClient request = new HttpClient();
        string formatedTime = Authentication.FormatedTime();
        request.DefaultRequestHeaders.Add("x-ms-date", formatedTime);

        //Adding the Authorization header to the request
        string authorization = Authentication.GetSignedString("POST",formatedTime, urlPath, Utilities.Account, Utilities.Key);
        request.DefaultRequestHeaders.Add("Authorization", authorization);

        request.DefaultRequestHeaders.TryAddWithoutValidation("Content-Length", body.Length.ToString());


        HttpResponseMessage messageResult = await request.PostAsync(uri, new StringContent(body, UTF8Encoding.UTF8, "application/atom+xml"));
        return messageResult.ToString();
    }

 public static string GetSignedString(string httpMethod, string time, string urlPath, string account, string key)
    {
           String contentMD5 = String.Empty;
String contentType = "application/atom+xml";
String canonicalizedResource = String.Format("/{0}/{1}", account, urlPath);
String stringToSign = String.Format(
      "{0}\n{1}\n{2}\n{3}\n{4}",
      httpMethod,
      contentMD5,
      contentType,
      time,
      canonicalizedResource);

        string signedKey = SignThis(stringToSign, key, account);
        return signedKey;
    }
    private static String SignThis(String canonicalizedString,string Key, string Account)
    {
        String signature = string.Empty;
        byte[] unicodeKey = Convert.FromBase64String(Key);
        using (HMACSHA256 hmacSha256 = new HMACSHA256(unicodeKey))
        {
            Byte[] dataToHmac = System.Text.Encoding.UTF8.GetBytes(canonicalizedString);
            signature = Convert.ToBase64String(hmacSha256.ComputeHash(dataToHmac));
        }

        String authorizationHeader = String.Format(
              CultureInfo.InvariantCulture,
              "{0} {1}:{2}",
              "SharedKeyLite",
              Account,
              signature);

        return authorizationHeader;
    }

时间参数的格式是根据Azure要求的,除此之外我不知道还能做什么或尝试什么。我尝试在没有 httpMethod、没有 contentMD5、没有内容类型和各种组合的情况下发出请求,但仍然如此。

我非常确定 SignThis(...) 方法有效,因为我还使用它来签署查询实体的 GET 请求,因此任何帮助或文字都会对我有很大帮助。谢谢

/已编辑/ 我附加了 UploadEntityAsync 方法,在我的例子中,我在 Azure 中有一个名为 Position 的表,所以我正在构建 XML,无论如何,这不是错误的原因,因为我已将我构建的 XML 与 Azure 存储资源管理器的 XML 进行了比较Fiddler,没问题。唯一的问题是签名

最佳答案

所以我发现代码有一些问题:

  • 您已选择使用 SharedKeyLite,但您在代码中用于创建 stringToSign 的格式适用于 <强>SharedKey。如果您想使用 SharedKeyLite,请尝试使用如下内容创建 stringToSign:

        stringToSign = String.Format("{0}\n{1}", time, canonicalizedResource);
    

欲了解更多详情,请参阅此处:http://msdn.microsoft.com/en-us/library/windowsazure/dd179428.aspx

  • 我发现您创建 StringContent 的方式存在问题。由于某种原因,如果我使用你的代码,我总是会收到 403 错误。试试这个:

        var stringContent = new StringContent(body);
        stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/atom+xml");
        HttpResponseMessage messageResult = await request.PostAsync(uri, stringContent);
    

请尝试使用此代码。它使用 SharedKey:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace SOPostToAzureTable
{
    static class Utilities
    {
        internal static string Account = "account name";

        internal static string Key = "account key";
    }

    class Program
    {
        static void Main(string[] args)
        {
            var task = Task.Factory.StartNew(() => InsertEntityAsync("SOTest"));
            Task[] tasks = new Task[1];
            tasks[0] = task;
            Task.WaitAll(tasks);
            Console.WriteLine("Press any key to continue");
            Console.ReadLine();
        }

        public static async Task<string> InsertEntityAsync(string tableName)
        {
            string uri = @"https://" + Utilities.Account + ".table.core.windows.net/" + tableName;
            return await UploadEntityAsync(tableName, uri);
        }
        public static async Task<string> UploadEntityAsync(string urlPath, string uri)
        {
            string body = @"<?xml version=""1.0"" encoding=""utf-8"" standalone=""yes""?>
                                <entry 
                                        xmlns:d=""http://schemas.microsoft.com/ado/2007/08/dataservices"" 
                                        xmlns:m=""http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"" 
                                        xmlns=""http://www.w3.org/2005/Atom"">
                                    <title />
                                    <updated>2013-08-30T21:03:45.4991966Z</updated>
                                    <author>
                                        <name />
                                    </author>
                                    <id />
                                    <content type=""application/xml"">
                                      <m:properties>
                                      <d:PartitionKey>SOTest</d:PartitionKey>
                                      <d:RowKey>{0}</d:RowKey>
                                      <d:A m:type=""Edm.String"">A</d:A><d:B m:type=""Edm.String"">B</d:B>
                                  </m:properties>
                                    </content>
                                </entry>";
            body = string.Format(body, Guid.NewGuid());

            HttpClient request = new HttpClient();
            string formatedTime = DateTime.UtcNow.ToString("R");
            request.DefaultRequestHeaders.Add("x-ms-date", formatedTime);

            //Adding the Authorization header to the request
            string authorization = GetSignedString("POST", formatedTime, urlPath, Utilities.Account, Utilities.Key);
            request.DefaultRequestHeaders.Add("Authorization", authorization);

            request.DefaultRequestHeaders.TryAddWithoutValidation("Content-Length", body.Length.ToString());
            var stringContent = new StringContent(body);
            stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/atom+xml");
            HttpResponseMessage messageResult = await request.PostAsync(uri, stringContent);
            return messageResult.ToString();
        }

        public static string GetSignedString(string httpMethod, string time, string urlPath, string account, string key)
        {
            String contentMD5 = String.Empty;
            String contentType = "application/atom+xml";
            String canonicalizedResource = String.Format("/{0}/{1}", account, urlPath);
            //stringToSign format for SharedKey
            String stringToSign = String.Format(
                  "{0}\n{1}\n{2}\n{3}\n{4}",
                  httpMethod,
                  contentMD5,
                  contentType,
                  time,
                  canonicalizedResource);
            //stringToSign format for SharedKeyLite
            //stringToSign = String.Format("{0}\n{1}", time, canonicalizedResource);
            string signedKey = SignThis(stringToSign, key, account);
            return signedKey;
        }

        private static String SignThis(String canonicalizedString, string Key, string Account)
        {
            String signature = string.Empty;
            byte[] unicodeKey = Convert.FromBase64String(Key);
            using (HMACSHA256 hmacSha256 = new HMACSHA256(unicodeKey))
            {
                Byte[] dataToHmac = System.Text.Encoding.UTF8.GetBytes(canonicalizedString);
                signature = Convert.ToBase64String(hmacSha256.ComputeHash(dataToHmac));
            }

            String authorizationHeader = String.Format(
                  CultureInfo.InvariantCulture,
                  "{0} {1}:{2}",
                  "SharedKey",
                  Account,
                  signature);

            return authorizationHeader;
        }

    }
}

关于azure - 对 Azure 表的 POST 请求中的授权 header ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18536581/

相关文章:

url - Blob 当前是否有 SAS URL?

git - 如何更改 Git 提交的编码 header ?

header - cuda 5.0 与 Visual Studio 2010 中的示例 .h 链接

node.js - Passport.JS - 无需 session 即可授权?

oauth-2.0 - jwtBearerMiddleware 中的安全戳验证器

java - 我们可以使用支持 Client_Crentials 和密码授予类型的 Spring Boot 实现 Oauth2 授权服务器吗?

angularjs - 使用 Azure Cloud 作为 AngularJS 应用程序的后端

c# - 如何通过 webAPI 传递\用户 azure continue token

Reactjs 路由导致页面渲染问题

php - 调整目录结构,同时重命名所有文件以表示其原始路径