2

我似乎得到了403:

HTTP/1.1 403 服务器未能验证请求。确保 Authorization 标头的值正确形成,包括签名。

当我HttpWebRequest.DefaultCachingPolicy在 app.config 中使用如下设置时会发生这种情况:

<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 上查看了 1.7.1 的 .net API(我正在使用)的代码,并假设它没有从 SDK 1.6(我目前使用的)更改,它应该可以正常工作。

非常感谢任何帮助

更新:我写了一些重现代码来帮助:

用途:.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();
        }
    }
}
4

2 回答 2

0

所以事实证明罪魁祸首是BlobRequest.SignRequest(request, creds);快速浏览文档表明If-None-MatchIf-Modified-Since都用作调用添加的 Authentication 标头计算的一部分。因为这些标头是在 Authentication 标头由 WINInet 计算之后添加的(假设)。无论哪种方式,最终结果都是 Authentication 标头提供的校验和现在无效。从而导致403 Forbidden.

解决方法:

  • 在本地手动缓存项目
  • 修复调用代码(可能是最好的长期解决方案)
  • 非常小心地使用其余接口(实验表明这很危险,出于同样的原因,在某些情况下使用“Shared Key Lite”可以解决这个问题)
  • 使用常规 WebRequest 获取文件(这意味着您必须通过共享密钥或其他方式将它们公开到 Internet)

我不会说 Azure Blob 存储已损坏......但我会说它不是真正的 RESTful,因为它无法正确遵守 HTTP 语义。

于 2012-11-15T20:23:18.487 回答
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 本身内发布密钥。因此,应该遵循一些安全最佳实践。

  1. 如果您在 DC 之外或通过可能被监控的链接运行,请使用 HTTPS 来防止恶意行为者滥用签名。
  2. 上面的示例计算了一个不绑定到容器 ACL 的简单策略,因此撤销它的唯一方法是轮换存储密钥。请尽可能将 SAS 签名与容器策略相关联。在这种情况下,如果 SAS Uri 曾经被泄露/滥用,您可以通过简单地删除相关容器上的策略来撤销它。有关如何执行此操作的更多信息,请参阅:http: //msdn.microsoft.com/en-us/library/windowsazure/jj721951.aspx
  3. 所有 SAS 流量都计入给定帐户 SLA 和计费,因此仅与受信任的客户端共享 SAS URI。

我希望这会有所帮助,如果您有任何其他问题,请告诉我,

/乔

于 2013-04-09T19:08:10.957 回答