使用新的基于AAD的访问控制通过Web浏览器通过URI访问Blob文件 [英] Access a blob file via URI over a web browser using new AAD based access control

查看:98
本文介绍了使用新的基于AAD的访问控制通过Web浏览器通过URI访问Blob文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

With the announcement of Azure Storage support for Azure Active Directory based access control, is it possible to serve a blob (a specific file) over a web browser just by it's URI?

我要简化的用例是使一些人可以访问blob上的文件,而不必将SAS令牌附加到URI.相反,当尝试在其Web浏览器中打开普通URI时启动典型的OAuth流程将是很不错的选择.

The use case I want to simplify is giving a few people access to files on the blob without the need of having to append a SAS token to the URI. Instead it would be brilliant to have the typical OAuth flow started when trying to open the plain URI in his/her web browser.

在我的情况下,我们希望授予用户通过基于Microsoft Bot框架的支持机器人上载到blob存储的文件的权限.支持人员应该可以在他们选择的Web浏览器中访问我们支持系统中的链接.

In my case we want to give access to files that have been uploaded to the blob storage by users through our support bot, build on Microsoft Bot framework. Links in our support system should be accessible by a support agent in their web browser of choice.

此公告支持的该用例,还是仅适用于编码的OAuth流,这意味着我们仍然必须实现一些代码?

It this use case supported by this announcement or does this only work for coded OAuth flows, meaning we still have to implement some code?

如果是这样,是否有一个很好的示例说明如何从Azure Function应用启动OAuth流程并使用生成的令牌下载文件(通过Azure Storage REST终结点)?

If so, is there a good sample on how to start the OAuth flow from a Azure Function app and use the resulting token to download the file (over Azure Storage REST endpoint)?

推荐答案

此答案在技术上是正确的,但当时不能直接回答我最初的问题.

While this answer is technically correct, it wasn't a direct response to my initial question.

我一直在寻找一种向业务用户提供任何Blob的直接uri的方法,因此他们可以简单地在任何Web浏览器中打开它并查看文件.

I was looking for a way to provide the direct uri of any blob to business users, so they can simply open it in any web browser and see the file.

就我而言,我们希望授予用户通过基于Microsoft Bot框架的支持机器人上载到blob存储的文件的权限.例如.将该附件作为我们支持系统中的链接,以供支持代理访问.

In my case we wanted to give access to files that have been uploaded to the blob storage by users through our support bot, build on Microsoft Bot framework. E.g. serving the attachment as a link in our support system to be accessed by a support agent.

深入研究之后,我可以回答自己的问题:

After digging into this, I can answer the question my self:

随着宣布Azure存储支持基于Azure Active Directory的访问控制,是否可以仅通过URI通过Web浏览器提供Blob(特定文件)?

With the announcement of Azure Storage support for Azure Active Directory based access control, is it possible to serve a blob (a specific file) over a web browser just by it's URI?

,这是不可能的.更具体地说,仅在浏览器中打开直接uri到blob不会触发OAuth流.相反,除非您提供SAS查询令牌或将Blob设置为public,否则它将始终为您提供ResourceNotFound响应.从安全角度来看(当涉及普通用户时)这两种解决方案都是不好的,而UX显然很糟糕.

No, this is not possible. More specifically, simply opening the direct uri to a blob in the browser doesn't trigger the OAuth flow. Instead it will always give you ResourceNotFound response unless you provide a SAS query token or set the blob to public. Both solutions are bad from security perspective (when normal users involved) and obviously bad UX.

寻找一种精确实现我想要的方法的方法,我想到了通过将fileName作为url参数传递并使用路由模板构造路径的,将Azure服务提供给任何业务用户的附件的功能.

Looking for a way to achieve exactly what I want, I came up with the idea of a azure function serving the attachment to any business user by passing the fileName as url parameter and constructing the path using a route template.

无论如何考虑安全性和访问令牌的需求,您都可以通过平台身份验证(也称为easyAuth)来保护功能应用程序.

Thinking of security and the need for an access token anyway, you could protect the function app through platform authentication (a.k.a. easyAuth).

但是,这还不够,并且无法直接配置解决方案的所有部分.这就是为什么我要分享它.

However, this is not enough and configuring all parts of the solution is not straight forward. That is why I'm sharing it.

TL; DR高级步骤:

  1. 创建一个新的功能应用程序(建议使用v2)
  2. 身份验证(easyAuth)
  3. 为功能应用程序创建服务主体(也称为应用程序注册)(在步骤2中隐含)
  4. 在应用程序注册上添加其他允许的令牌访问者 https://storage.microsoft.com >
  5. 编辑应用程序注册清单以包括Azure存储API权限(请参阅下面的特殊说明)
  6. 在Azure资源浏览器中修改authSettings以包括additionalLoginParams用于令牌响应和resourceId
  7. 至少提供存储Blob数据读取器对Blob的访问权限授予所有访问文件的用户
  8. 部署功能应用程序,调用它,访问用户令牌,调用blob存储并将结果呈现给用户(请参见下面的代码示例)
  1. Create a new Function App (v2 recommended)
  2. Enable the function App for authentication (easyAuth)
  3. Create a service principal (a.k.a. app registration) for the function app (implicit by step 2)
  4. Add additional allowed token audience https://storage.microsoft.com on the app registration
  5. Edit the manifest of the app registration to include Azure Storage API permission (see special remarks below)
  6. Modify authSettings in Azure Resource explorer to include additionalLoginParams for token response and resourceId
  7. Give at least the Storage Blob Data Reader permission on the blob to all users accessing the files
  8. Deploy your function app, call it, access the user token, call the blob storage and present the result to the user (see code samples below)

关于Azure存储API权限和访问令牌的注释(第5步和第6步)

如最新的

As stated in the latest documentation for AAD authentication support on azure storage, the app must grand user_impersonation permission scope for resourceId https://storage.azure.com/. Unfortunately the documentation did not state on how to set this API permission as it is not visible in the portal (at least I did not find it).

因此,唯一的方法是直接在azure门户中编辑应用程序注册清单,以通过其全局GUID(可在Internet上找到)进行设置.

So the only way is to set it through its global GUID (can be found on the internet) by editing the app registration manifest directly in the azure portal.

更新: 事实证明,在门户网站中找不到正确的权限是一个错误.在此处看到我的答案.手动修改清单会产生相同的结果,但是直接在门户网站中进行修改会更加方便.

Update: As it turned out, not finding the right permission in the portal is a bug. See my answer here. Modifying the manifest manually results in the same, but directly doing it in the portal is much more convenient.

"requiredResourceAccess": [
    {
        "resourceAppId": "e406a681-f3d4-42a8-90b6-c2b029497af1",
        "resourceAccess": [
            {
                "id": "03e0da56-190b-40ad-a80c-ea378c433f7f",
                "type": "Scope"
            }
        ]
    },
    {
        "resourceAppId": "00000002-0000-0000-c000-000000000000",
        "resourceAccess": [
            {
                "id": "311a71cc-e848-46a1-bdf8-97ff7156d8e6",
                "type": "Scope"
            }
        ]
    }
]

第一个是Azure存储上的user_impersonation范围,第二个是User.Read的图形权限,这在大多数情况下是有帮助或需要的.

The first one is the user_impersonation scope on Azure Storage and the second is the graph permission for User.Read, which in most cases is helpful or needed.

上传修改后的清单后,您可以在应用程序注册的 API权限标签上对其进行验证.

After you uploaded your modified manifest, you can verify it on the API Permissions tab on your app registration.

由于easyAuth使用的是AAD的v1端点,因此您的应用需要在触发OAuth流时通过传递resource=https://storage.azure.com/来静态地请求这些权限.

As easyAuth is using the v1 endpoint of AAD, your app needs to request those permission statically by passing resource=https://storage.azure.com/ when triggering the OAuth flow.

此外,Azure存储需要用于身份验证标头的承载架构,因此需要JWT令牌.要从端点获取JWT令牌,我们需要传递response_type=code id_token作为附加的登录参数.

Additionally Azure Storage requires the bearer schema for authentication header and therefore a JWT token is needed. To get a JWT token from the endpoint, we need to pass response_type=code id_token as an additional login parameter.

这两者只能通过 Azure资源浏览器或powershell来完成.

Both can only be done through Azure Resource explorer or powershell.

使用Azure资源浏览器,您必须一直导航到功能应用程序上的authSettings并相应地设置additionalLoginParams.

Using Azure Resource explorer you have to navigate all your way down to the authSettings on your function app and set the additionalLoginParams accordingly.

"additionalLoginParams": [
  "response_type=code id_token",
  "resource=https://storage.azure.com/"
]

代码示例

这是使用上述所有机制的简单Azure功能的完整代码示例.

Here is a complete code sample for an easy azure function using all aboves mechanisms.

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;

namespace Controller.Api.v1.Org
{
    public static class GetAttachment
    {
        private const string defaultContentType = "application/octet-stream";

        [FunctionName("GetAttachment")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "v1/attachments")] HttpRequest req,
            ILogger log)    
        {
            if (!req.Query.ContainsKey("fileName"))
                return new BadRequestResult();

            // Set the file name from query parameter
            string fileName = req.Query["fileName"];

            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            dynamic data = JsonConvert.DeserializeObject(requestBody);

            fileName = fileName ?? data?.name;

            // Construct the final uri. In this sample we have a applicaiton setting BLOB_URL
            // set on the function app to store the target blob
            var blobUri = Environment.GetEnvironmentVariable("BLOB_URL") + $"/{fileName}";

            // The access token is provided as this special header by easyAuth.
            var accessToken = req.Headers.FirstOrDefault(p => p.Key.Equals("x-ms-token-aad-access-token", StringComparison.OrdinalIgnoreCase));

            // Construct the call against azure storage and pass the user token we got from easyAuth as bearer
            using (var client = new HttpClient())
            {
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.Value.FirstOrDefault());
                client.DefaultRequestHeaders.Add("Accept-Encoding", "gzip, deflate");
                client.DefaultRequestHeaders.Add("Accept", "*/*");
                client.DefaultRequestHeaders.Add("x-ms-version", "2017-11-09");

                // Serve the response directly in users browser. This code works against any browser, e.g. chrome, edge or even internet explorer
                var response = await client.GetAsync(blobUri);
                var contentType = response.Content.Headers.FirstOrDefault(p => p.Key.Equals("Content-Type", StringComparison.OrdinalIgnoreCase));
                var byteArray = await response.Content.ReadAsByteArrayAsync();

                var result = new FileContentResult(byteArray, contentType.Value.Any() ? contentType.Value.First() : defaultContentType);

                return result;
            }
        }
    }
}

这篇关于使用新的基于AAD的访问控制通过Web浏览器通过URI访问Blob文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆