Хотя этот ответ технически правильный, он не был прямым ответом на мой первоначальный вопрос.
Я искал способ предоставить прямой URI любого блоба для бизнес-пользователей, чтобы они могли просто открыть его в любом веб-браузере и просмотреть файл.
В моем случае мы хотелипредоставить пользователям доступ к файлам, которые были загружены в хранилище BLOB-объектов через нашего бота поддержки, на основе платформы Microsoft Bot.Например, служа приложением в качестве ссылки в нашей системе поддержки, к которой должен обращаться агент службы поддержки.
После изучения этого вопроса я могу ответить на свой вопрос:
С объявлениемподдержки хранилища Azure для управления доступом на основе Azure Active Directory, возможно ли обслуживать большой двоичный объект (определенный файл) через веб-браузер только по его URI?
Нет , это невозможно.Точнее говоря, простое открытие прямого URI для блоба в браузере не запускает поток OAuth.Вместо этого он всегда будет давать вам ответ ResourceNotFound
, если вы не предоставите токен запроса SAS или не установите для большого двоичного объекта значение public.Оба решения плохие с точки зрения безопасности (когда вовлечены обычные пользователи) и, очевидно, плохой UX.
Решение
Ища способ архивировать именно то, что я хочу, я пришел к идееФункция Azure, обслуживающая вложение любому бизнес-пользователю, передавая fileName
URI, создающего путь с использованием шаблона маршрута.
В любом случае, думая о безопасности и о необходимости токена доступа, вы можете защитить функциональное приложение с помощью аутентификации платформы (также называемой easyAuth).
Однако этого недостаточно, и настройка всех частей решения не проста.Вот почему я делюсь этим.
TL; высокоуровневые шаги DR:
- Создание нового приложения функции (рекомендуется v2)
- Включение функции приложения для аутентификация (easyAuth)
- Создание субъекта службы (или регистрация приложения) для приложения-функции (подразумевается в шаге 2)
- Добавление дополнительной разрешенной аудитории токенов https://storage.microsoft.com при регистрации приложения
- Измените манифест регистрации приложения, включив в него разрешение API хранилища Azure (см. специальные замечания ниже)
- Измените authSettings в проводнике ресурсов Azure, включив
additionalLoginParams
для ответа токенаи resourceId - Предоставьте как минимум Storage Blob Data Reader разрешению для BLOB-объекта всем пользователям, имеющим доступ к файлам
- Разверните свое функциональное приложение, вызовите его, получите доступ к токену пользователявызовите хранилище больших двоичных объектов и представьте результат пользователю (см. примеры кода ниже)
Замечания о разрешении API Azure Storage и токене доступа (шаги 5 и 6)
Как указано в последней документации для поддержки аутентификации AAD в хранилище Azure, приложение должно увеличить user_impersonation
область полномочий для resourceId https://storage.azure.com/
.К сожалению, в документации не указано, как установить это разрешение API, поскольку оно не отображается на портале (по крайней мере, я его не нашел).
Таким образом, единственный способ - настроить его через глобальный GUID (можно найти в Интернете), отредактировав манифест регистрации приложения непосредственно на портале Azure.
Обновление : Как оказалось, поиск правильного разрешения на портале является ошибкой.Смотрите мой ответ здесь .Изменение манифеста вручную приводит к тому же результату, но непосредственно делать это на портале гораздо удобнее.
"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"
}
]
}
]
Первыйодин из них - это область действия user_impersonation
в хранилище Azure, а второй - разрешение для графика для User.Read
, что в большинстве случаев полезно или необходимо.
После загрузки измененного манифеста вы можете проверить его на вкладке Разрешения API регистрации вашего приложения.
Поскольку easyAuth использует конечную точку AAD v1, ваше приложение должно запрашивать эти разрешения статически, передавая resource=https://storage.azure.com/
при запуске потока OAuth.
Кроме того, для хранилища Azure требуется схема однонаправленного канала для заголовка аутентификации и, следовательно, необходим токен JWT.Чтобы получить токен JWT от конечной точки, нам нужно передать response_type=code id_token
в качестве дополнительного параметра входа в систему.
И то, и другое можно сделать только через Исследователь ресурсов Azure или 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;
}
}
}
}