1

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

现在,我尝试使用 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、没有 content-type 和各种组合的情况下发出请求,但仍然如此。

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

/已编辑/ 我正在附加 UploadEntityAsync 方法,在我的情况下,我在 Azure 中有一个名为 Position 的表,所以我正在构建 XML,无论如何,这不是什么问题,因为我已经将我构建的 XML 与 Azure 的一个进行了比较使用 Fiddler 的存储资源管理器,一切正常。唯一的问题是签名

4

1 回答 1

1

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

  • 但是,您选择使用用于在代码中SharedKeyLite创建的格式用于. 如果您想使用,请尝试使用以下内容创建:stringToSignSharedKeySharedKeyLitestringToSign

        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;
        }

    }
}
于 2013-08-30T15:46:54.920 回答