【问题标题】:Can't implement azure web app service access to azure storage container (blob) using MSI无法使用 MSI 实现对 azure 存储容器 (blob) 的 azure Web 应用服务访问
【发布时间】:2019-11-05 16:38:54
【问题描述】:

我有一个 azure 资源组,其中包含带有 BLOB 容器的 Web 应用服务和存储。我的 Web 应用程序 (.NET Core) 尝试从容器中检索和显示图像。容器对内容没有公共访问权限(访问级别为私有)。我为我的应用程序创建了系统分配的身份,并在存储访问控制 (IAM) 中为其赋予了读者角色。

这是我在应用程序代码中访问 blob 的方式:

const string blobName = "https://storagename.blob.core.windows.net/img/Coast.jpg";
string storageAccessToken = await GetStorageAccessTokenAsync();
var tokenCredential = new TokenCredential(storageAccessToken);
var storageCredentials = new StorageCredentials(tokenCredential);
var blob = new CloudBlockBlob(new Uri(blobName), storageCredentials);
ImageBlob = blob.Uri;

GetStorageAccessTokenAsync() 这样做:

var tokenProvider = new AzureServiceTokenProvider();
return await tokenProvider.GetAccessTokenAsync("https://storage.azure.com/");

然后图片由

显示
<img src="@Model.ImageBlob" />

我的代码中没有任何异常,但是来自 BLOB 容器的图像未在浏览器控制台中显示 404 错误(指定的资源不存在)。 当我将容器的访问级别更改为“blob”(公共访问)时,应用程序工作正常并显示图像。 显然,获取凭据部分有问题,但我找不到任何工作示例,也找不到它实际应该如何工作的详细解释。 非常感谢任何帮助。

UDPATE: 感谢所有回复的人。所以,我这里似乎有两个问题。

1) 我没有正确获取凭据。 我可以看到我创建的“AzureServiceTokenProvider”对象(Microsoft.Azure.Services.AppAuthentication)在运行时具有空属性 PrincipalUsed。

我的应用程序部署到 Azure App Service,它具有系统托管标识,并且该标识(服务主体)在 Azure 存储中被授予权限(我按照建议将权限从帐户读取器更改为存储 Blob 数据读取器)。

它不应该从当前上下文中获取所有需要的数据吗?如果没有,我可以在这里做什么?

2) 我使用了错误的方法来显示图像,但是由于该应用程序无论如何都无法访问存储空间,因此我还无法修复它。

但是 - 在我的情况下,常见的方法是什么?我的意思是没有对存储的公共访问,我使用“CloudBlockBlob”来访问图像。

【问题讨论】:

  • 在您的 GetAccessTokenAsync() 方法中,请尝试添加第二个参数:TenantId
  • 这不起作用,因为您将 url 返回到浏览器。然后从浏览器完成对 blob 的请求,并且您的客户端未经过身份验证。您可以以其他方式流式传输图像或在 url 中返回 sas 令牌,但您需要连接字符串来生成 sas 令牌
  • 你有进步吗?
  • 谢谢@Thomas,但我的印象是,如果我正在处理本机 azure 应用程序和系统标识,我不需要 SAS 和其他密钥来访问 Azure 资源中的数据。 Azure AD 和 RBAC 应该可以解决问题。如果不是,这是怎么回事?
  • @IvanYang,我从文档中得到 AzureServiceTokenProvider 只能作为输入连接字符串获取,例如“RunAs=App;AppId=AppId;TenantId=TenantId;AppKey=Secret”,我没有 AppKey因为它是系统管理的身份。如果我错了,请纠正我。再说一次,因为我尝试使用 AAD 和 RBAC,所以不需要所有这些。

标签: azure-web-app-service azure-blob-storage azure-managed-identity


【解决方案1】:

Reader 允许读取控制平面,但不能读取数据平面。您需要的角色是存储 Blob 数据读取器,它提供读取 blob 内容的访问权限。

有关此的更多详细信息,请查看:https://docs.microsoft.com/en-us/azure/role-based-access-control/role-definitions#data-operations-example

【讨论】:

    【解决方案2】:

    当您使用&lt;img src="@Model.ImageBlob" /&gt; 时,浏览器在请求中不会发送任何授权标头。在您的代码中,您正在获取令牌,但在获取图像时,令牌并未在授权标头中发送。因此,存储 API 认为这是一个匿名请求。这就是你得到 404 的原因。

    获取图片时需要发送验证码。这段代码对我有用

    public async Task<ActionResult> Image()
            {
                const string blobName = "https://storage.blob.core.windows.net/images/image.png";
                string storageAccessToken = await GetStorageAccessTokenAsync().ConfigureAwait(false);
    
                var tokenCredential = new TokenCredential(storageAccessToken);
                var storageCredentials = new StorageCredentials(tokenCredential);
    
                var blob = new CloudBlockBlob(new Uri(blobName), storageCredentials);
                Stream blobStream = blob.OpenRead();
                return File(blobStream, blob.Properties.ContentType, "image.png");
            }
    

    在视图中,我使用

    <img src="/Home/Image" />
    

    【讨论】:

    • 谢谢@Varun,但恐怕我需要在这里解释一下。这个 Image() 应该在哪里以及如何调用?
    • 做了一些研究并将图像标签更改为&lt;img src='@Url.Action("Image")' /&gt;。检索图像过程似乎工作正常(至少我可以看到 blob 具有非零长度,并且相应的请求 (/ModelName?action=Image) 得到 200 响应。但图像仍然没有显示。内容类型设置正确。
    • 如果你设置了 ,你应该会看到图像。就我而言,它在 HomeController 中,所以我将 src 设置为 /Home/Image
    • 所以,如果 Razor 页面 public async Task&lt;ActionResult&gt; Image() 是“关于”模型的一部分,我可以使用 &lt;img src="/About/Image" /&gt;?但它不起作用,我还能做错什么吗? @Varun,您能否解释一下或提供一些参考资料,这个任务到底是如何工作的?
    • 看看这些链接 - codeproject.com/Tips/1028915/How-To-Download-a-File-in-MVC-2c-sharpcorner.com/UploadFile/db2972/… 用于在 MVC 中下载文件。解决问题的一种方法是直接浏览到图像并查看响应。如果您从存储中正确获取图像 blob,则可能需要修复 MVC 部分。
    【解决方案3】:

    最后,我得到了它的工作。首先,关于从 Azure 存储中获取令牌和图像的部分代码是可以的。我解决了在 RazorPages 应用程序中显示图像的第二个问题,在视图中使用此代码:

    <form asp-page-handler="GetImage" method="get">
        <img src="/MyPageName?handler=GetImage" />
    </form>
    

    以及模型中对应的代码:

    public async Task<ActionResult> OnGetGetImageAsync()
    {
       //getting image code and returning FileContentResult
    }
    

    但我仍在思考:是否有更简单的方法来做到这一点?像向模型添加图像集合,使用“OnGet ...”处理程序填充它,然后使用视图显示其内容。我没有找到在&lt;img&gt; 标签中使用模型属性的方法。有人有什么建议吗?

    【讨论】:

      猜你喜欢
      • 2016-08-23
      • 2017-12-21
      • 2019-05-03
      • 1970-01-01
      • 2020-10-12
      • 2018-02-28
      • 1970-01-01
      • 2020-10-25
      • 2019-03-13
      相关资源
      最近更新 更多