Azure CloudBlobContainer.CreateIfNotExists 返回 403 禁止访问错误。

11

我在Web API服务中间接地调用CloudBlobContainer.CreateIfNotExist(请参见下面的FindOrCreatePrivateBlobContainer方法),但它返回以下403禁止错误消息:

<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
The remote server returned an error: (403) Forbidden.
</ExceptionMessage>
<ExceptionType>Microsoft.WindowsAzure.Storage.StorageException</ExceptionType>
<StackTrace>
at Microsoft.WindowsAzure.Storage.Core.Executor.Executor.ExecuteSync[T](RESTCommand`1 cmd, IRetryPolicy policy, OperationContext operationContext) at Microsoft.WindowsAzure.Storage.Blob.CloudBlobContainer.Exists(Boolean primaryOnly, BlobRequestOptions requestOptions, OperationContext operationContext) at Microsoft.WindowsAzure.Storage.Blob.CloudBlobContainer.CreateIfNotExists(BlobContainerPublicAccessType accessType, BlobRequestOptions requestOptions, OperationContext operationContext) at Microsoft.WindowsAzure.Storage.Blob.CloudBlobContainer.CreateIfNotExists(BlobRequestOptions requestOptions, OperationContext operationContext) at [Obfuscated].DocumentManagement.BlobStorage.BlobHelper.FindOrCreatePrivateBlobContainer(String ContainerName, String AccountConnectionString) in c:\Users\[Obfuscated]\Desktop\[ProjectNameObfuscated]Online\[Obfuscated].DocumentManagement.BlobStorage\BlobHelper.cs:line 25 at [Obfuscated].DocumentManagement.BlobStorage.BlobFileItemHandler.GetStream(Int64 FileItemId) in c:\Users\[Obfuscated]\Desktop\[ProjectNameObfuscated]Online\[Obfuscated].DocumentManagement.BlobStorage\BlobFileItemHandler.cs:line 114 at [Obfuscated].DocumentManagement.Service.Controllers.FileItemController.Get(String ServiceAuthKey, Int64 FileItemId) in c:\Users\[Obfuscated]\Desktop\[ProjectNameObfuscated]Online\[Obfuscated].DocumentManagement.Service\Controllers\FileItemController.cs:line 148 at lambda_method(Closure , Object , Object[] ) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.<GetExecutor>b__9(Object instance, Object[] methodParameters) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__0.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()
</StackTrace>
<InnerException>
<Message>An error has occurred.</Message>
<ExceptionMessage>
The remote server returned an error: (403) Forbidden.
</ExceptionMessage>
<ExceptionType>System.Net.WebException</ExceptionType>
<StackTrace>
at System.Net.HttpWebRequest.GetResponse() at Microsoft.WindowsAzure.Storage.Core.Executor.Executor.ExecuteSync[T](RESTCommand`1 cmd, IRetryPolicy policy, OperationContext operationContext)
</StackTrace>
</InnerException>
</Error>

以下是生成错误的代码:

     public HttpResponseMessage Get(string ServiceAuthKey, Int64 FileItemId)
        {
            if (!CheckServiceAuthKey(ServiceAuthKey).IsSuccessStatusCode)
                return new HttpResponseMessage(HttpStatusCode.Unauthorized);

            HttpRequest request = HttpContext.Current.Request;

            FileItem fi = null;
            using (DocumentDbContext db = new DocumentDbContext())
            {
                fi = db.FileItems.Find(FileItemId);
            }


            BlobFileItemHandler fih = new BlobFileItemHandler();
            Stream s = fih.GetStream(FileItemId);


            // -------- DOWNLOAD FILE TO CLIENT -------- 

            HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
            response.Content = new StreamContent(s);
            //a text file is actually an octet-stream (pdf, etc)
            response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
            //we used attachment to force download
            response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
            response.Content.Headers.ContentDisposition.FileName = fi.PublicFileName;


            return response;
        }

 public Stream GetStream(Int64 FileItemId)
        {
            CloudBlobContainer c = BlobHelper.FindOrCreatePrivateBlobContainer("[Obfuscated]-dms", AccountConnectionString);

            using (DocumentDbContext db = new DocumentDbContext())
            {
                FileItem fi = db.FileItems.Find(FileItemId);

                CloudBlockBlob blob = c.GetDirectoryReference(fi.FilePathOnServer).GetBlockBlobReference(fi.PrivateFileName);
                bool blobExists = blob.Exists();

                if (!blobExists)
                    throw new System.IO.FileNotFoundException();

                Stream stream = new MemoryStream();
                blob.DownloadToStream(stream);
                long streamlen = stream.Length;
                stream.Position = 0;
                return stream;
            }

        }

public static CloudBlobContainer FindOrCreatePrivateBlobContainer(string ContainerName, string AccountConnectionString)
        {
            Trace.TraceInformation("FindOrCreatePrivateBlobContainer '" + ContainerName + "' with connectionstring '" + AccountConnectionString + "'");
            CloudStorageAccount account = CloudStorageAccount.Parse(AccountConnectionString);
            CloudBlobClient blobClient = account.CreateCloudBlobClient();
            CloudBlobContainer container = blobClient.GetContainerReference(ContainerName);
            container.CreateIfNotExists();
            return container;
        }

我需要一些帮助来排查此错误的原因。我已经尝试了以下操作:
  1. 检查要创建的容器名称是否有效,特别是只包含小写字母(没有特殊字符或大写字母)。
  2. 据说Azure服务器和调用服务器之间的时区差异可能导致403禁止访问错误消息。无论我是从个人电脑(使用UTC时区)还是从Azure部署运行服务,都会出现此错误。
  3. 检查了连接字符串和帐户密钥,似乎都是正确的。它的格式如下:<add key="MyStuff.DocumentManagement.ConnectionString" value="DefaultEndpointsProtocol=http;AccountName=MyStuffAccount;AccountKey=[obfuscated]" />
  4. 尝试在http和https之间切换,但结果没有任何区别。
  5. 我能够通过VS 2013 server explorer连接到Azure存储并创建新的容器。
请帮忙解决问题!

更新

在启用跟踪后,以下是错误输出: 应用程序: 2014-07-13T19:08:03 PID[6888] Error
Microsoft.WindowsAzure.Storage.StorageException: 远程服务器返回错误:(403)Forbidden. ---> System.Net.WebException: 远程服务器返回错误:(403) Forbidden. 应用程序: 在 System.Net.HttpWebRequest.GetResponse() 中应用程序: 在 Microsoft.WindowsAzure.Storage.Core.Executor.Executor.ExecuteSync[T](RESTCommand1 cmd, IRetryPolicy policy, OperationContext operationContext) 中应用程序: --- 内部异常堆栈跟踪的结尾 --- 在 Microsoft.WindowsAzure.Storage.Core.Executor.Executor.ExecuteSync[T](RESTCommand1 cmd, IRetryPolicy policy, OperationContext operationContext) 中应用程序: 在 Microsoft.WindowsAzure.Storage.Blob.CloudBlobContainer.Exists(Boolean primaryOnly, BlobRequestOptions requestOptions, OperationContext operationContext) 中应用程序: 在 Microsoft.WindowsAzure.Storage.Blob.CloudBlobContainer.CreateIfNotExists(BlobContainerPublicAccessType accessType, BlobRequestOptions requestOptions, OperationContext operationContext) 中应用程序: 在 [Obfuscated].DocumentManagement.BlobStorage.BlobHelper.FindOrCreatePrivateBlobContainer(String ContainerName, String AccountConnectionString) 中应用程序: 在 [Obfuscated].DocumentManagement.BlobStorage.BlobFileItemHandler.GetStream(Int64 FileItemId) 中应用程序:请求信息应用程序: RequestID:fce980ad-a673-4ef1-b55d-d017a49845c8 应用程序: RequestDate:Sun, 13 Jul 2014 19:08:02 GMT 应用程序: StatusMessage:服务器无法验证请求。请确保Authorization标头的值正确地形成,包括签名。


你能使用上面代码中相同的密钥从VS 2013资源管理器访问存储帐户吗?如果不能,请登录门户并确认您的密钥未被重新生成。否则,为了进一步调试,您可以运行Fiddler并分享请求失败时的Fiddler跟踪。 - Vinay Shah - Microsoft
@VinayShah-Microsoft 谢谢,那正是问题所在 - 不过我已经在几天前修复了它(只是还没有时间在这里更新答案)。这个密钥生成是自动发生的吗? - CShark
很高兴知道您的问题已经解决。密钥重新生成不应该自动发生,需要通过门户或服务管理 API 手动调用。 - Vinay Shah - Microsoft
@VinayShah-Microsoft 谢谢。在凌晨2点仍在工作时,奇怪的事情往往会发生。 - CShark
7个回答

27

这可能不是解决特定问题的方法,但它可能会帮助别人。

我一直收到403错误并且很难找到解决方法。最终我发现我的开发机器的时间差了2个小时。当我把时间设置正确后,403错误就消失了。

Azure要求UTC时间戳在请求时间之内的15分钟范围内。


这种情况也发生在我身上——我的Web服务器的国际日期时间服务器同步出现了问题。我使用了一个叫做原子钟同步的工具,以更频繁地与国际日期时间服务器同步来解决这个问题。我觉得有些人可能会因为这个问题而来到这里,所以我把这个方法分享在这里。 - CShark

6

正如我们在上面的评论中讨论的那样,请确保在从服务端收到403状态码时,您的密钥是有效的。如果通过门户或使用服务管理API重新生成密钥,则密钥可能会更改。


2

1
当我尝试连接到本地 Azure 存储仿真器时,遇到了类似的问题。通过 Azure 存储资源管理器,连接成功,但是通过使用 REST API 的自定义工具时,会抛出 403 错误。我不得不手动在配置中包含端点,并使用 http 而不是 https。
更多信息请参见此处(https://learn.microsoft.com/en-us/azure/storage/common/storage-configure-connection-string)。
DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;
AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;
BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;
TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;
QueueEndpoint=http://127.0.0.1:10001/devstoreaccount1;

1
尝试以下操作: 1. 生成新的访问密钥,用于在您的App.config文件中使用。
或者 2. 登录到您的Azure门户网站,在存储帐户下选择“防火墙和虚拟网络”,然后在选项下启用“允许来自所有网络的访问”。
我希望这对有需要的人有所帮助! 输入图像描述

1
对我来说,问题在于存储账户有IP限制导致403错误。解决方法是进入Azure门户,然后前往:
存储账户 → "存储账户名称" → 防火墙和虚拟网络
接着,在允许的部分确认您的IP是否在其中或者勾选"允许所有网络访问"。

0

当我调用时,看到了相同的错误信息。

Microsoft.WindowsAzure.Storage.Blob.CloudBlobClient.GetBlobReferenceFromServerAsync(Uri blobUri)

不小心使用了一个指向与 CloudBlobClient.BaseUri 不同 URL 的 blobUri 值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接