【问题标题】:Access a blob file via URI over a web browser using new AAD based access control使用基于 AAD 的新访问控制通过 Web 浏览器通过 URI 访问 blob 文件
【发布时间】:2019-08-16 13:45:22
【问题描述】:

随着Azure Storage support for Azure Active Directory based access control 的发布,是否可以仅通过其 URI 在 Web 浏览器上提供 blob(特定文件)?

我要简化的用例是让少数人访问 blob 上的文件,而无需将 SAS 令牌附加到 URI。相反,当尝试在他/她的 Web 浏览器中打开纯 URI 时启动典型的 OAuth 流程会很棒。

在我的例子中,我们希望通过我们的支持机器人(基于 Microsoft Bot 框架构建)授予对用户已上传到 blob 存储的文件的访问权限。我们支持系统中的链接应该可以由支持代理在他们选择的网络浏览器中访问。

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

如果是这样,是否有关于如何从 Azure 函数应用启动 OAuth 流并使用生成的令牌下载文件(通过 Azure 存储 REST 端点)的良好示例?

【问题讨论】:

  • 我的回答解决了你的问题吗?
  • @TonyJu 这不是我问题的直接答案,而是一个很好的指南。我自己弄清楚该怎么做。让我在我自己的答案中总结一下。无论如何,我最终将您的答案标记为正确答案。给我两天时间。
  • 你好@TonyJu - 我已经发布了我自己的答案。也许您可以帮助改进它或留下您的想法。我已投票并引用了您的答案,但将我的答案标记为正确的答案。

标签: azure azure-active-directory botframework azure-storage azure-functions


【解决方案1】:

虽然this answer 在技术上是正确的,但这并不是对我最初的问题的直接回应。

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

在我的例子中,我们希望允许用户通过我们的支持机器人(基于 Microsoft Bot 框架构建)访问已上传到 Blob 存储的文件。例如。将附件作为我们支持系统中的链接提供给支持代理访问。

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

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

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

解决方案

寻找一种方法来实现我想要的,我想出了一个天蓝色函数的想法,通过将fileName 作为 url 参数传递并使用路由模板构造路径来为任何业务用户提供附件。

考虑到安全性和访问令牌的需求,您可以通过平台身份验证(也称为 easyAuth)来保护函数应用。

然而,这还不够,配置解决方案的所有部分并不简单。这就是我分享它的原因。

TL;DR 高级步骤:

  1. 创建一个新的函数应用(推荐 v2)
  2. authentication(easyAuth)启用功能App
  3. 为函数应用创建服务主体(也称为应用注册)(步骤 2 隐含)
  4. 在应用注册时添加额外的允许令牌受众https://storage.microsoft.com
  5. 编辑应用注册清单以包含 Azure 存储 API 权限(请参阅下面的特别说明)
  6. 在 Azure 资源管理器中修改 authSettings 以包含 additionalLoginParams 用于令牌响应和 resourceId
  7. 至少将 blob 的 Storage Blob Data Reader 权限授予访问文件的所有用户
  8. 部署您的函数应用、调用它、访问用户令牌、调用 Blob 存储并将结果呈现给用户(请参阅下面的代码示例)

关于 Azure Storage API 权限和访问令牌的说明(步骤 5 和 6)

如最新的 documentation 中所述,用于 Azure 存储上的 AAD 身份验证支持,应用程序必须为 resourceId https://storage.azure.com/ 提供 user_impersonation 权限范围。不幸的是,文档没有说明如何设置此 API 权限,因为它在门户中不可见(至少我没有找到它)。

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

更新事实证明,在门户中找不到正确的权限是一个错误。请参阅我的回答here。手动修改manifest的结果是一样的,但是直接在portal里做会方便很多。

"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 的图形权限,在大多数情况下是有用的或需要的。

上传修改后的清单后,您可以在应用注册的API权限选项卡上对其进行验证。

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

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

两者都只能通过Azure Resource explorer 或powershell 完成。

使用 Azure 资源浏览器,您必须一直向下导航到函数应用上的 authSettings 并相应地设置 additionalLoginParams

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

代码示例

这是一个使用上述所有机制的简单天蓝色函数的完整代码示例。

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;
            }
        }
    }
}

【讨论】:

    【解决方案2】:

    如果要对存储使用基于 Azure Active Directory 的访问控制,则需要获取访问令牌。以下是供您参考的步骤。

    1. Register an application

    2.Assign a build-in RBAC role to this application 这取决于您将哪个角色分配给应用程序。

    3.Get the access token

    4.有了访问令牌,现在就可以调用storage rest api了。

    【讨论】:

    • 我收到错误消息,“此版本不支持身份验证方案承载。”访问 blob 服务 Rest API 时。我的访问令牌没有任何问题。是 x-ms-version 导致了错误!我能够使用不记名访问令牌和 x-ms-version 读取私有 blob。 x-ms-version 2018-03-28 和 2019-07-07 都有效。
    猜你喜欢
    • 2019-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-14
    • 2017-05-02
    • 2018-06-24
    • 2017-06-01
    • 1970-01-01
    相关资源
    最近更新 更多