我似乎得到了403:
HTTP / 1.1 403服务器无法验证请求。确保正确形成Authorization标头的值,包括签名。
当我在app.config中设置HttpWebRequest.DefaultCachingPolicy
时,会发生这种情况,如下所示:
<system.net>
<requestCaching defaultPolicyLevel="Default" isPrivateCache="false">
<defaultHttpCachePolicy policyLevel="Default"/>
</requestCaching>
</system.net>
我这样做是因为我有遗留的代码,我无法控制它调用我提供的blob存储的API,就好像它是一个文件系统(每个文件最多58个相同的调用)。显然这并不理想。使用默认的HTTP样式缓存是我想要的一种行为,因为它会导致我的应用程序仅在修改文件时下载该文件。
问题似乎发生在每个其他请求中(例如,当请求被缓存并且服务器正在检查服务器内容是否已更改时,它似乎正在发生。)
失败请求与成功请求之间的唯一区别似乎是包含:
If-None-Match: "<a blob etag>"
If-Modified-Since: <a date>
我已经在github for 1.7.1上查看了.net API(我正在使用)的代码,并假设它没有从SDK 1.6(我目前使用的)改变,它应该工作细
非常感谢任何帮助
更新: 我写了一些repro代码来帮助:
使用:.NET 4.0,Windows Azure SDK 1.6
using System;
using System.Net;
using System.Net.Cache;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;
namespace AzureStorageProb
{
class Program
{
static void Main(string[] args)
{
const string accountKey = "<azure storage account key>";
const string account = "<azure storage account name>";
const string testBlob = "<blob path to test file>";
var cloudStorageAccount =
new CloudStorageAccount(
new StorageCredentialsAccountAndKey(account, accountKey),
useHttps: true);
var cloudBlobClient = cloudStorageAccount.CreateCloudBlobClient();
HttpWebRequest.DefaultCachePolicy =
new HttpRequestCachePolicy(HttpRequestCacheLevel.Default);
try
{
var blob = cloudBlobClient.GetBlobReference(testBlob);
blob.FetchAttributes();
blob.DownloadByteArray();
Console.WriteLine("First attempt worked!");
}
catch (StorageClientException ex)
{
Console.WriteLine(ex);
}
try
{
var blob = cloudBlobClient.GetBlobReference(testBlob);
blob.FetchAttributes();
blob.DownloadByteArray();
Console.WriteLine("Second attempt worked!");
}
catch (StorageClientException ex)
{
Console.WriteLine(ex);
}
Console.ReadKey();
}
}
}
答案 0 :(得分:0)
事实证明,罪魁祸首是BlobRequest.SignRequest(request, creds);
快速查看文档表明If-None-Match
和If-Modified-Since
都用作调用添加的Authentication头的计算的一部分。因为在WINInet计算Authentication头之后添加了这些头(假设)。无论哪种方式,最终结果都是Authentication头提供的校验和现在无效。从而导致403 Forbidden
。
解决方法:
我不会说Azure Blob存储已损坏......但我会说它不是真正的RESTful,因为它无法正确遵守HTTP语义。
答案 1 :(得分:0)
实现失败的原因是计算的签名包括请求日期以及条件标头(如果匹配,如果没有匹配,如果没有匹配,等等),以防止重放或人员中间攻击。因此,当发送高速缓存的请求时,认证失败。 目前似乎没有办法挂钩到默认HTTP缓存并更改标头以更新此签名,因此SharedKey和SharedKeyLite身份验证方案将不适用于此方案。 但是,您可以使用SharedAccessSignatures(http://msdn.microsoft.com/en-us/library/windowsazure/ee395415.aspx)在给定URI上预先验证一段时间。这将不再需要每个请求唯一的签名。
SharedAccessBlobPolicy policy = new SharedAccessBlobPolicy()
{
Permissions = SharedAccessBlobPermissions.Read,
// Add delta to account for clock skew
SharedAccessStartTime = DateTime.Now.AddMinutes(-5),
SharedAccessExpiryTime = DateTime.Now.AddMinutes(15)
};
HttpWebRequest.DefaultCachePolicy = new HttpRequestCachePolicy(HttpCacheAgeControl.MaxAge, TimeSpan.FromSeconds(10));
CloudBlockBlob sasdBlob = new CloudBlockBlob(new Uri(rootBlob.Uri.ToString() + rootBlob.GetSharedAccessSignature(policy)));
OperationContext cacheCtx = new OperationContext();
cacheCtx.ResponseReceived += (o, a) => Console.WriteLine("{0} : {1}", a.RequestInformation.HttpStatusCode, a.Response.IsFromCache);
for (int m = 0; m < 100; m++)
{
sasdBlob.DownloadToStream(Stream.Null, null, null, cacheCtx);
Thread.Sleep(1000);
if (m == 10)
{
// invalidate data updating properties
rootBlob.Metadata.Add("hello", "cache");
rootBlob.SetMetadata();
}
}
这将输出:
200:错误
200:是的
200:是的
200:是的
200:是的
200:是的
200:是的
200:是的
200:是的
200:是的
200:是的
200:错误
200:是的
...
我们将在没有这些变通方法的情况下寻找更好的方法来公开此功能。
每当您使用SharedAccessSignatures时,您实际上是在URI内部发布密钥。因此,应遵循一些安全最佳实践。
我希望这有帮助,如果您有任何其他问题,请告诉我,
/乔