为了将实体插入 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/