Поделиться через


Обеспечьте безопасность ASP.NET Core Blazor Web App с помощью Microsoft Entra ID

В этой статье описывается, как защитить Blazor Web App с помощью платформы удостоверений Microsoft и веб-пакетов Microsoft Identity для Microsoft Entra ID, используя пример приложения.

Эта версия статьи охватывает реализацию Entra без внедрения шаблона Backend для frontend (BFF). Шаблон BFF полезен для выполнения аутентифицированных запросов к внешним службам. Измените выбор версии статьи на шаблон BFF, если в спецификации приложения предусмотрено внедрение шаблона BFF.

Рассматривается следующая спецификация:

  • Blazor Web App используется режим автоматической отрисовки с глобальным взаимодействием (InteractiveAuto).
  • Серверный проект вызывает AddAuthenticationStateSerialization для добавления провайдера состояния проверки подлинности на стороне сервера, который используется PersistentComponentState для передачи состояния проверки подлинности на клиент. Клиент вызывает AddAuthenticationStateDeserialization для десериализации и использует состояние аутентификации, переданное сервером. Состояние аутентификации зафиксировано на весь срок службы приложения WebAssembly.
  • Приложение использует Microsoft Entra ID, основанный на пакетах MicrosoftIdentity Web.
  • Автоматическое обновление неинтерактивного токена управляется фреймворком.
  • Приложение использует абстракции на стороне сервера и клиентской службы для отображения созданных данных о погоде:
    • При отрисовке компонента Weather на сервере для отображения данных о погоде используется ServerWeatherForecaster. Веб-пакеты Майкрософт Identity предоставляют API для создания именованной нижестоящей веб-службы для вызова веб-API. IDownstreamApi вставляется в ServerWeatherForecasterобъект, который используется для вызова CallApiForUserAsync для получения данных о погоде из внешнего веб-API (MinimalApiJwt проекта).
    • Когда компонент Weather отрисовывается на клиенте, он использует реализацию службы ClientWeatherForecaster, которая применяет предварительно настроенный HttpClient (в файле клиентского проекта Program) для вызова веб-API сервера с использованием минимального API (/weather-forecast) для данных о погоде. Минимальная конечная точка API получает данные о погоде из ServerWeatherForecaster класса и возвращает его клиенту для отрисовки компонентом.

Образец решения

Пример решения состоит из следующих проектов:

  • BlazorWebAppEntra: серверный проект объекта Blazor Web App, содержащий пример минимальной конечной точки API для данных погоды.
  • BlazorWebAppEntra.Client: клиентский проект объекта Blazor Web App.
  • MinimalApiJwt: серверный веб API, содержащий пример конечной точки минимального API для данных о погоде.

Доступ к образцу можно получить через папку с последней версией в репозитории образцов Blazor, используя следующую ссылку. Пример находится в папке BlazorWebAppEntra для .NET 9 или более поздней версии.

Запустите решение из Aspire/Aspire.AppHost проекта.

Просмотреть или скачать образец кода (описание загрузки)

Регистрация приложений Microsoft Entra ID

Мы рекомендуем использовать отдельные регистрации для приложений и веб-API, даже если приложения и веб-API находятся в одном решении. Представленные рекомендации предназначены для BlazorWebAppEntra приложения и MinimalApiJwt веб-API примерного решения, но те же рекомендации также применяются к любым Entra-базированным регистрациям для приложений и веб-API.

Сначала зарегистрируйте веб-API,MinimalApiJwt чтобы предоставить доступ к веб-API при регистрации приложения. Идентификатор арендатора и идентификатор клиента веб-API используются для настройки веб-API в файле Program. После регистрации веб-API откройте веб-API в регистрациях приложений>Expose an API с именем области Weather.Get. Запишите URI идентификатора приложения для использования в конфигурации приложения.

Затем зарегистрируйте приложение (BlazorWebAppEntra) с конфигурацией веб-платформы и URI перенаправления (порт не требуется). Идентификатор арендатора приложения, домен арендатора и идентификатор клиента, а также базовый адрес веб-API, URI ID приложения и имя области погоды используются для настройки приложения в его appsettings.json файле. Предоставьте разрешение API для доступа к веб-API в Регистрация приложений>разрешениях API. Если спецификация безопасности приложения вызывает ее, вы можете предоставить согласие администратора для организации для доступа к веб-API. Авторизованные пользователи и группы назначаются на регистрацию приложения в Регистрация приложений>Корпоративные приложения.

В конфигурации регистрации приложений на портале Entra или Azure в разделе неявного предоставления и гибридного потоков не устанавливайте флажки ни для конечной точки авторизации, возвращающей токены доступа или токены идентификатора. Обработчик OpenID Connect автоматически запрашивает соответствующие маркеры с помощью кода, возвращенного из конечной точки авторизации.

Создайте секрет клиента в регистрации приложения на портале Entra или Azure (управление>сертификатами и секретами>нового секрета клиента). Сохраните значение секрета клиента для использования в следующем разделе.

Дополнительные рекомендации по настройке Entra для определенных параметров приведены далее в этой статье.

Серверный Blazor Web App проект (BlazorWebAppEntra)

Проект BlazorWebAppEntra является серверным проектом Blazor Web App.

Клиентский Blazor Web App проект (BlazorWebAppEntra.Client)

Проект BlazorWebAppEntra.Client — это клиентский проект Blazor Web App.

Если пользователю необходимо войти или выйти во время отрисовки на стороне клиента, запускается полная перезагрузка страницы.

Проект серверного веб-API (MinimalApiJwt)

Проект MinimalApiJwt — это внутренний веб-API для нескольких интерфейсных проектов. Проект настраивает минимальную конечную точку API для данных о погоде.

Файл MinimalApiJwt.http можно использовать для тестирования запроса данных о погоде. Обратите внимание, что MinimalApiJwt проект должен выполняться для тестирования конечной точки, а конечная точка жестко закодирована в файл. Дополнительные сведения см. в статье "Использование HTTP-файлов в Visual Studio 2022".

Проект включает пакеты и конфигурацию для создания документов OpenAPI и пользовательского интерфейса Swagger в среде разработки. Дополнительные сведения см. в статье "Использование созданных документов OpenAPI".

Конечная точка данных для защищенного прогноза погоды находится в файле проекта Program.

app.MapGet("/weather-forecast", () =>
{
    var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
}).RequireAuthorization();

Метод RequireAuthorization расширения требует авторизации для определения маршрута. Для всех контроллеров, добавляющихся в проект, добавьте [Authorize] атрибут в контроллер или действие.

Настройка проекта веб-API серверной части (MinimalApiJwt)

Настройте проект в вызове JwtBearerOptions в AddJwtBearerMinimalApiJwt файле проектаProgram.

Для регистрации веб-приложения API область Weather.Get настраивается на портале Entra или Azure в режиме предоставления API.

Authority задает полномочия для осуществления вызовов OIDC.

jwtOptions.Authority = "{AUTHORITY}";

В следующих примерах используется идентификатор клиента aaaabbbb-0000-cccc-1111-dddd2222eeee.

Если приложение зарегистрировано в клиенте ME-ID, авторитет должен соответствовать издателю (iss) JWT, возвращаемого поставщиком удостоверений.

jwtOptions.Authority = "https://sts.windows.net/aaaabbbb-0000-cccc-1111-dddd2222eeee/";

Если приложение зарегистрировано в клиенте AAD B2C:

jwtOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";

Audience задает аудиторию для любого полученного маркера доступа JWT.

jwtOptions.Audience = "{AUDIENCE}";

Сопоставьте значение только с путем URI идентификатора приложения который настроен при добавлении Weather.Get области в разделе Предоставление API на портале Entra или Azure. Не включайте имя области "Weather.Get" в значение.

В следующих примерах используется идентификатор 11112222-bbbb-3333-cccc-4444dddd5555приложения (клиента). Во втором примере используется домен клиента contoso.onmicrosoft.com.

пример клиента ME-ID:

jwtOptions.Audience = "api://11112222-bbbb-3333-cccc-4444dddd5555";

Пример клиента AAD B2C:

jwtOptions.Audience = "https://contoso.onmicrosoft.com/11112222-bbbb-3333-cccc-4444dddd5555";

Настройка серверного проекта (BlazorWebAppEntra)

AddMicrosoftIdentityWebApp из Microsoft Identity Web (Microsoft.Identity.Web пакет NuGet, документация по API) настраивается в BlazorWebAppEntra файле проекта Program .

Получите идентификатор приложения (клиента), домен клиента (издателя) и идентификатор каталога (клиента) из регистрации приложения на портале Entra или Azure. URI идентификатора приложения для области действия Weather.Get получается из регистрации веб-API. Не включайте имя области при принятии URI идентификатора приложения на портале.

BlazorWebAppEntra В файле проекта Program укажите значения для следующих заполнителей в конфигурации Microsoft Identity Web:

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(msIdentityOptions =>
    {
        msIdentityOptions.CallbackPath = "/signin-oidc";
        msIdentityOptions.ClientId = "{CLIENT ID (BLAZOR APP)}";
        msIdentityOptions.Domain = "{DIRECTORY NAME}.onmicrosoft.com";
        msIdentityOptions.Instance = "https://login.microsoftonline.com/";
        msIdentityOptions.ResponseType = "code";
        msIdentityOptions.TenantId = "{TENANT ID}";
    })
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddDownstreamApi("DownstreamApi", configOptions =>
    {
        configOptions.BaseUrl = "{BASE ADDRESS}";
        configOptions.Scopes = [ "{APP ID URI}/Weather.Get" ];
    })
    .AddDistributedTokenCaches();

Заполнители в предыдущей конфигурации:

  • {CLIENT ID (BLAZOR APP)}: идентификатор приложения (клиента).
  • {DIRECTORY NAME}: имя каталога домена клиента (издателя).
  • {TENANT ID}: Идентификатор клиента в каталоге.
  • {BASE ADDRESS}: базовый адрес веб-API.
  • {APP ID URI}: URI идентификатора приложения для областей веб-API. Используется любой из следующих форматов, где заполнитель {CLIENT ID (WEB API)} представляет собой идентификатор клиента регистрации Entra веб-API, а заполнитель {DIRECTORY NAME} — это имя каталога домена арендатора (издателей) (например: contoso).
    • формат клиента ME-ID: api://{CLIENT ID (WEB API)}
    • Формат клиента B2C: https://{DIRECTORY NAME}.onmicrosoft.com/{CLIENT ID (WEB API)}

Пример:

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(msIdentityOptions =>
    {
        msIdentityOptions.CallbackPath = "/signin-oidc";
        msIdentityOptions.ClientId = "00001111-aaaa-2222-bbbb-3333cccc4444";
        msIdentityOptions.Domain = "contoso.onmicrosoft.com";
        msIdentityOptions.Instance = "https://login.microsoftonline.com/";
        msIdentityOptions.ResponseType = "code";
        msIdentityOptions.TenantId = "aaaabbbb-0000-cccc-1111-dddd2222eeee";
    })
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddDownstreamApi("DownstreamApi", configOptions =>
    {
        configOptions.BaseUrl = "https://localhost:7277";
        configOptions.Scopes = [ "api://11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get" ];
    })
    .AddDistributedTokenCaches();

Эта версия статьи охватывает реализацию Entra с помощью шаблона Backend for Frontend (BFF). Измените селектор версии статьи на шаблон, отличный от BFF , если спецификация приложения не вызывает внедрения шаблона BFF.

Рассматривается следующая спецификация:

  • Blazor Web App используется режим автоматической отрисовки с глобальным взаимодействием (InteractiveAuto).
  • Серверный проект вызывает AddAuthenticationStateSerialization для добавления провайдера состояния проверки подлинности на стороне сервера, который используется PersistentComponentState для передачи состояния проверки подлинности на клиент. Клиент вызывает AddAuthenticationStateDeserialization для десериализации и использует состояние аутентификации, переданное сервером. Состояние аутентификации зафиксировано на весь срок службы приложения WebAssembly.
  • Приложение использует Microsoft Entra ID, основанный на пакетах MicrosoftIdentity Web.
  • Автоматическое обновление неинтерактивного токена управляется фреймворком.
  • Шаблон сервер для пользовательского интерфейса (BFF) применяется с использованием .NET Aspire для обнаружения служб и YARP для проксирования запросов к конечной точке прогноза погоды в серверном приложении.
    • Веб-API на стороне сервера использует аутентификацию с помощью JWT-бирера для валидирования JWT-токенов, сохраненных Blazor Web App в процедуре входа cookie.
    • Aspire улучшает возможности создания облачных приложений .NET. Он предоставляет согласованный набор средств и шаблонов для создания и запуска распределенных приложений.
    • YARP (еще один обратный прокси-сервер) — это библиотека, используемая для создания обратного прокси-сервера.
  • Приложение использует абстракции на стороне сервера и клиентской службы для отображения созданных данных погоды.
    • При отрисовке компонента Weather на сервере для отображения данных о погоде используется ServerWeatherForecaster. Веб-пакеты Майкрософт Identity предоставляют API для создания именованной нижестоящей веб-службы для вызова веб-API. IDownstreamApi вставляется в ServerWeatherForecasterобъект, который используется для вызова CallApiForUserAsync для получения данных о погоде из внешнего веб-API (MinimalApiJwt проекта).
    • Когда компонент Weather отрисовывается на клиенте, он использует реализацию службы ClientWeatherForecaster, которая применяет предварительно настроенный HttpClient (в файле клиентского проекта Program) для вызова веб-API сервера с использованием минимального API (/weather-forecast) для данных о погоде. Минимальная конечная точка API получает маркер доступа для пользователя путем вызова GetAccessTokenForUserAsync. Наряду с правильными параметрами области действия, вызов через обратный прокси выполняется к внешнему веб-API (MinimalApiJwt проект) для получения и возврата данных о погоде клиенту для отрисовки компонентом.

Для получения дополнительной информации о .NET Aspire, см. Генеральная доступность .NET Aspire: упрощение разработки .NET Cloud-Native (май 2024 г.).

Предпосылки

.NET Aspire требуется Visual Studio версии 17.10 или более поздней.

Также см. раздел Предварительные требования в Краткое руководство: создание вашего первого .NET Aspire приложения.

Образец решения

Пример решения состоит из следующих проектов:

  • .NET Aspire:
    • Aspire.AppHost: Используется для управления высокоуровневыми аспектами оркестрации приложения.
    • Aspire.ServiceDefaults: содержит конфигурации приложений по умолчанию .NET Aspire , которые можно расширить и настроить по мере необходимости.
  • MinimalApiJwt: серверный веб API, содержащий пример конечной точки минимального API для данных о погоде.
  • BlazorWebAppEntra: серверный проект системы Blazor Web App.
  • BlazorWebAppEntra.Client: клиентский проект объекта Blazor Web App.

Доступ к образцу можно получить через папку с последней версией в репозитории образцов Blazor, используя следующую ссылку. Пример находится в папке BlazorWebAppEntraBff для .NET 9 или более поздней версии.

Просмотреть или скачать образец кода (описание загрузки)

Регистрация приложений Microsoft Entra ID

Мы рекомендуем использовать отдельные регистрации для приложений и веб-API, даже если приложения и веб-API находятся в одном решении. Представленные рекомендации предназначены для BlazorWebAppEntra приложения и MinimalApiJwt веб-API примерного решения, но те же рекомендации также применяются к любым Entra-базированным регистрациям для приложений и веб-API.

Сначала зарегистрируйте веб-API,MinimalApiJwt чтобы предоставить доступ к веб-API при регистрации приложения. Идентификатор арендатора и идентификатор клиента веб-API используются для настройки веб-API в файле Program. После регистрации веб-API откройте веб-API в регистрациях приложений>Expose an API с именем области Weather.Get. Запишите URI идентификатора приложения для использования в конфигурации приложения.

Затем зарегистрируйте приложение (BlazorWebAppEntra) с конфигурацией веб-платформы и URI перенаправления (порт не требуется). Идентификатор арендатора приложения, домен арендатора и идентификатор клиента, а также базовый адрес веб-API, URI ID приложения и имя области погоды используются для настройки приложения в его appsettings.json файле. Предоставьте разрешение API для доступа к веб-API в Регистрация приложений>разрешениях API. Если спецификация безопасности приложения вызывает ее, вы можете предоставить согласие администратора для организации для доступа к веб-API. Авторизованные пользователи и группы назначаются на регистрацию приложения в Регистрация приложений>Корпоративные приложения.

В конфигурации регистрации приложений на портале Entra или Azure в разделе неявного предоставления и гибридного потоков не устанавливайте флажки ни для конечной точки авторизации, возвращающей токены доступа или токены идентификатора. Обработчик OpenID Connect автоматически запрашивает соответствующие маркеры с помощью кода, возвращенного из конечной точки авторизации.

Создайте секрет клиента в регистрации приложения на портале Entra или Azure (управление>сертификатами и секретами>нового секрета клиента). Сохраните значение секрета клиента для использования в следующем разделе.

Дополнительные рекомендации по настройке Entra для определенных параметров приведены далее в этой статье.

.NET Aspire проектов

Дополнительные сведения об использовании .NET Aspire и .AppHost.ServiceDefaults проектах примера приложения см. в .NET Aspire документации.

Убедитесь, что вы выполнили предварительные условия для .NET Aspire. Дополнительные сведения см. в разделе "Предварительные требования " краткого руководства. Создание первого .NET Aspire приложения.

Пример приложения настраивает только небезопасный профиль запуска HTTP (http) для использования во время тестирования разработки. Дополнительные сведения, включая пример небезопасных и безопасных профилей параметров запуска, см. в разделе «Разрешить небезопасный транспорт» (.NET Aspire.NET Aspire документация).

Серверный Blazor Web App проект (BlazorWebAppEntra)

Проект BlazorWebAppEntra является серверным проектом Blazor Web App.

Клиентский Blazor Web App проект (BlazorWebAppEntra.Client)

Проект BlazorWebAppEntra.Client — это клиентский проект Blazor Web App.

Если пользователю необходимо войти или выйти во время отрисовки на стороне клиента, запускается полная перезагрузка страницы.

Проект серверного веб-API (MinimalApiJwt)

Проект MinimalApiJwt — это внутренний веб-API для нескольких интерфейсных проектов. Проект настраивает минимальную конечную точку API для данных о погоде. Запросы из Blazor Web App серверного проекта (BlazorWebAppEntra) перенаправляются через прокси на проект MinimalApiJwt.

Файл MinimalApiJwt.http можно использовать для тестирования запроса данных о погоде. Обратите внимание, что MinimalApiJwt проект должен выполняться для тестирования конечной точки, а конечная точка жестко закодирована в файл. Дополнительные сведения см. в статье "Использование HTTP-файлов в Visual Studio 2022".

Проект включает пакеты и конфигурацию для создания документов OpenAPI и пользовательского интерфейса Swagger в среде разработки. Дополнительные сведения см. в статье "Использование созданных документов OpenAPI".

Конечная точка данных для защищенного прогноза погоды находится в файле проекта Program.

app.MapGet("/weather-forecast", () =>
{
    var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
}).RequireAuthorization();

Метод RequireAuthorization расширения требует авторизации для определения маршрута. Для всех контроллеров, добавляющихся в проект, добавьте [Authorize] атрибут в контроллер или действие.

Настройка проекта веб-API серверной части (MinimalApiJwt)

Настройте MinimalApiJwt проект в вызове JwtBearerOptions в файле проекта AddJwtBearer.

Для регистрации веб-приложения API область Weather.Get настраивается на портале Entra или Azure в режиме предоставления API.

Authority задает полномочия для осуществления вызовов OIDC.

jwtOptions.Authority = "{AUTHORITY}";

В следующих примерах используется идентификатор клиента aaaabbbb-0000-cccc-1111-dddd2222eeee.

Если приложение зарегистрировано в клиенте ME-ID, авторитет должен соответствовать издателю (iss) JWT, возвращаемого поставщиком удостоверений.

jwtOptions.Authority = "https://sts.windows.net/aaaabbbb-0000-cccc-1111-dddd2222eeee/";

Если приложение зарегистрировано в клиенте AAD B2C:

jwtOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";

Audience задает аудиторию для любого полученного маркера доступа JWT.

jwtOptions.Audience = "{AUDIENCE}";

Сопоставьте значение только с путем URI идентификатора приложения который настроен при добавлении Weather.Get области в разделе Предоставление API на портале Entra или Azure. Не включайте имя области "Weather.Get" в значение.

В следующих примерах используется идентификатор 11112222-bbbb-3333-cccc-4444dddd5555приложения (клиента). Во втором примере используется домен клиента contoso.onmicrosoft.com.

пример клиента ME-ID:

jwtOptions.Audience = "api://11112222-bbbb-3333-cccc-4444dddd5555";

Пример клиента AAD B2C:

jwtOptions.Audience = "https://contoso.onmicrosoft.com/11112222-bbbb-3333-cccc-4444dddd5555";

Настройка серверного проекта (BlazorWebAppEntra)

AddMicrosoftIdentityWebApp из Microsoft Identity Web (Microsoft.Identity.Web пакет NuGet, документация по API) настраивается в BlazorWebAppEntra файле проекта Program .

Получите идентификатор приложения (клиента), домен клиента (издателя) и идентификатор каталога (клиента) из регистрации приложения на портале Entra или Azure. URI идентификатора приложения для области действия Weather.Get получается из регистрации веб-API. Не включайте имя области при принятии URI идентификатора приложения на портале.

BlazorWebAppEntra В файле проекта Program укажите значения для следующих заполнителей в конфигурации Microsoft Identity Web:

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(msIdentityOptions =>
    {
        msIdentityOptions.CallbackPath = "/signin-oidc";
        msIdentityOptions.ClientId = "{CLIENT ID (BLAZOR APP)}";
        msIdentityOptions.Domain = "{DIRECTORY NAME}.onmicrosoft.com";
        msIdentityOptions.Instance = "https://login.microsoftonline.com/";
        msIdentityOptions.ResponseType = "code";
        msIdentityOptions.TenantId = "{TENANT ID}";
    })
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddDownstreamApi("DownstreamApi", configOptions =>
    {
        configOptions.BaseUrl = "{BASE ADDRESS}";
        configOptions.Scopes = [ "{APP ID URI}/Weather.Get" ];
    })
    .AddDistributedTokenCaches();

Заполнители в предыдущей конфигурации:

  • {CLIENT ID (BLAZOR APP)}: идентификатор приложения (клиента).
  • {DIRECTORY NAME}: имя каталога домена клиента (издателя).
  • {TENANT ID}: Идентификатор клиента в каталоге.
  • {BASE ADDRESS}: базовый адрес веб-API.
  • {APP ID URI}: URI идентификатора приложения для областей веб-API. Используется любой из следующих форматов, где заполнитель {CLIENT ID (WEB API)} представляет собой идентификатор клиента регистрации Entra веб-API, а заполнитель {DIRECTORY NAME} — это имя каталога домена арендатора (издателей) (например: contoso).
    • формат клиента ME-ID: api://{CLIENT ID (WEB API)}
    • Формат клиента B2C: https://{DIRECTORY NAME}.onmicrosoft.com/{CLIENT ID (WEB API)}

Пример:

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(msIdentityOptions =>
    {
        msIdentityOptions.CallbackPath = "/signin-oidc";
        msIdentityOptions.ClientId = "00001111-aaaa-2222-bbbb-3333cccc4444";
        msIdentityOptions.Domain = "contoso.onmicrosoft.com";
        msIdentityOptions.Instance = "https://login.microsoftonline.com/";
        msIdentityOptions.ResponseType = "code";
        msIdentityOptions.TenantId = "aaaabbbb-0000-cccc-1111-dddd2222eeee";
    })
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddDownstreamApi("DownstreamApi", configOptions =>
    {
        configOptions.BaseUrl = "https://localhost:7277";
        configOptions.Scopes = [ "api://11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get" ];
    })
    .AddDistributedTokenCaches();

Предупреждение

Производственные приложения должны использовать поставщика кэша распределённых токенов в производственной среде. В противном случае приложение может иметь низкую производительность в некоторых сценариях. Дополнительные сведения см. в разделе "Использование поставщика кэша распределенных маркеров для рабочей среды ".

Путь обратного вызова (CallbackPath) должен соответствовать URI перенаправления (путь обратного вызова при входе), настроенному при регистрации приложения в Entra или портале Azure. Пути настраиваются в панели Проверки подлинности регистрации приложения. Значение по умолчанию CallbackPath для зарегистрированного URI перенаправления /signin-oidc — это https://localhost/signin-oidc (порт не требуется).

Это SignedOutCallbackPath путь запроса в базовом пути приложения, перехваченный обработчиком OpenID Connect, где пользовательский агент сначала возвращается после выхода из системы Entra. Пример приложения не задает значение для пути, так как используется значение по умолчанию "/signout-callback-oidc". После перехвата запроса обработчик OpenID Connect перенаправляет на SignedOutRedirectUri или RedirectUri, если задано.

Настройте путь обратного вызова после выхода в регистрации Entra приложения. В портале Entra или Azure настройте путь в записях параметров конфигурации веб-платформы для перенаправления URI:

https://localhost/signout-callback-oidc

Примечание.

Порт не требуется для адресов localhost при использовании Entra.

Если вы не добавляете URI пути обратного вызова после выхода из системы в регистрацию приложения в Entra, Entra отказывается перенаправить пользователя обратно в приложение и просто просит закрыть окно браузера.

Примечание.

Entra не перенаправляет основного администратора (корневую учетную запись) или внешнего пользователя обратно в приложение Blazor. Вместо этого Entra регистрирует пользователя из приложения и рекомендует закрыть все окна браузера. Дополнительную информацию см. в статье о неработающем postLogoutRedirectUri, если URL-адрес авторитета содержит Tenant ID (AzureAD/microsoft-authentication-library-for-js #5783).

Предупреждение

Не сохраняйте секреты приложений, строка подключения, учетные данные, пароли, персональные идентификационные номера (ПИН-коды), частный код C#/.NET или закрытые ключи и токены в клиентском коде, который всегда небезопасн. В тестовых, промежуточных и рабочих средах код на стороне сервера Blazor и веб-API должны использовать безопасные потоки аутентификации, которые не хранят учетные данные в файлах кода проекта или конфигурации. Вне локального тестирования разработки рекомендуется избегать использования переменных среды для хранения конфиденциальных данных, так как переменные среды не являются наиболее безопасным подходом. Для локального тестирования разработки средство Secret Manager рекомендуется для защиты конфиденциальных данных. Дополнительные сведения см. в разделе "Безопасное обслуживание конфиденциальных данных и учетных данных".

Установка секретного ключа клиента

Этот раздел относится только к серверной версии проекта Blazor Web App.

Используйте любой из следующих подходов для предоставления секрета клиента приложению:

  • Средство Secret Manager: средство Secret Manager хранит частные данные на локальном компьютере и используется только во время локальной разработки.
  • Azure Key Vault. Вы можете хранить секрет клиента в хранилище ключей для использования в любой среде, включая среду разработки при локальной работе. Некоторые разработчики предпочитают использовать хранилища ключей для промежуточных и рабочих развертываний и использовать средство Secret Manager для локальной разработки.

Настоятельно рекомендуется избегать хранения секретов клиента в файлах кода проекта или конфигурации. Используйте безопасные потоки проверки подлинности, например оба подхода в этом разделе.

Инструмент управления секретами

Средство Secret Manager может хранить секрет клиента приложения сервера под ключ конфигурации AzureAd:ClientSecret.

Серверное Blazor приложение не инициализировано для средства Диспетчера секретов. Используйте командную оболочку, например командную оболочку PowerShell разработчика в Visual Studio, чтобы выполнить следующую команду. Перед выполнением команды воспользуйтесь командой cd для смены каталога на каталог проекта сервера. Команда устанавливает идентификатор секретов пользователя (<UserSecretsId>) в файле проекта приложения сервера, который используется внутри средства для отслеживания секретов для приложения:

dotnet user-secrets init

Выполните следующую команду, чтобы задать секрет клиента. Заполнитель {SECRET} — это секрет клиента, полученный из регистрации приложения в Entra.

dotnet user-secrets set "AzureAd:ClientSecret" "{SECRET}"

При использовании Visual Studio можно убедиться, что секрет задан, щелкнув правой кнопкой мыши проект сервера в Обозреватель решений и выбрав "Управление секретами пользователей".

Azure Key Vault

Azure Key Vault обеспечивает безопасный подход для предоставления секрета клиента приложения приложению.

Сведения о создании хранилища ключей и настройке секрета клиента см. в статье "Сведения о секретах Azure Key Vault" (документация Azure), которые перекрестно связывают ресурсы для начала работы с Azure Key Vault. Чтобы реализовать код в этом разделе, запишите URI хранилища ключей и имя секрета из Azure при создании хранилища ключей и секрета. Например, в этом разделе имя секрета — "BlazorWebAppEntraClientSecret".

При создании хранилища ключей в портале Entra или Azure:

  • Настройте хранилище ключей для использования управления доступом на основе ролей Azure (RABC). Если вы не работаете в виртуальной сети Azure, в том числе для локальной разработки и тестирования, убедитесь, что общедоступный доступ на шаге "Сеть " включен (установлен). Включение общедоступного доступа предоставляет только конечную точку хранилища ключей. Учетные записи, прошедшие проверку подлинности, по-прежнему необходимы для доступа.

  • Создайте управляемую Identity Azure (или добавьте роль в существующий управляемый Identity объект, который вы планируете использовать) с ролью пользователя секретов Key Vault. Назначьте управляемую Identity учетную запись службе приложений Azure, который размещает развертывание: Параметры>Identity>Назначено пользователем>Добавить.

    Примечание.

    Если вы также планируете локально запускать приложение с авторизованным пользователем для доступа к хранилищу ключей с помощью Azure CLI или проверки подлинности службы Azure Visual Studio, добавьте учетную запись пользователя Azure разработчика в службе управления доступом (IAM) с ролью пользователя секретов Key Vault . Если вы хотите использовать Azure CLI через Visual Studio, выполните az login команду на панели PowerShell разработчика и следуйте инструкциям по проверке подлинности с помощью клиента.

Чтобы реализовать код в этом разделе, запишите URI хранилища ключей (например, "https://contoso.vault.azure.net/", обязательно с косой чертой в конце) и имя секрета (например, "BlazorWebAppEntraClientSecret") из Azure при создании хранилища ключей и секрета.

Внимание

Секрет хранилища ключей создается с датой окончания срока действия. Обязательно отслеживайте срок истечения секрета в хранилище ключей и создайте новый секрет для приложения до наступления этой даты.

Добавьте следующий класс AzureHelper в серверный проект. Метод GetKeyVaultSecret извлекает секрет из хранилища ключей. Настройте пространство имен (BlazorSample.Helpers) в соответствии со схемой пространства имен проекта.

Helpers/AzureHelper.cs:

using Azure.Core;
using Azure.Security.KeyVault.Secrets;

namespace BlazorWebAppEntra.Helpers;

public static class AzureHelper
{
    public static string GetKeyVaultSecret(string vaultUri, 
        TokenCredential credential, string secretName)
    {
        var client = new SecretClient(new Uri(vaultUri), credential);
        var secret = client.GetSecretAsync(secretName).Result;

        return secret.Value.Value;
    }
}

Примечание.

В предыдущем примере используется DefaultAzureCredential для упрощения проверки подлинности при разработке приложений, развертываемых в Azure, путем объединения учетных данных, используемых в средах размещения Azure с учетными данными, используемыми в локальной разработке. При переходе в рабочую среду альтернатива является лучшим выбором, например ManagedIdentityCredential. Дополнительные сведения см. в статье Аутентификация размещенных в Azure приложений .NET в ресурсах Azure с помощью управляемого удостоверения, назначаемого системой.

Где службы зарегистрированы в файле проекта Program сервера, получите и примените секрет клиента с помощью следующего кода:

TokenCredential? credential;

if (builder.Environment.IsProduction())
{
    credential = new ManagedIdentityCredential("{MANAGED IDENTITY CLIENT ID}");
}
else
{
    // Local development and testing only
    DefaultAzureCredentialOptions options = new()
    {
        // Specify the tenant ID to use the dev credentials when running the app locally
        // in Visual Studio.
        VisualStudioTenantId = "{TENANT ID}",
        SharedTokenCacheTenantId = "{TENANT ID}"
    };

    credential = new DefaultAzureCredential(options);
}

В тех местах, где установлены MicrosoftIdentityOptions, вызовите GetKeyVaultSecret для получения и назначения клиентского секрета приложения.

msIdentityOptions.ClientSecret = AzureHelper.GetKeyVaultSecret("{VAULT URI}", 
    credential, "{SECRET NAME}");

{MANAGED IDENTITY CLIENT ID}: идентификатор управляемого Identity клиента Azure (GUID).

{TENANT ID}: Идентификатор клиента в каталоге. Пример: aaaabbbb-0000-cccc-1111-dddd2222eeee

{VAULT URI}: URI хранилища ключей. Включите завершающую косую черту в URI. Пример: https://contoso.vault.azure.net/

{SECRET NAME}: секретное имя. Пример: BlazorWebAppEntraClientSecret

Конфигурация используется для упрощения предоставления доступа к выделенным хранилищам ключей и именам секретов на основе файлов конфигурации среды приложения. Например, можно указать различные значения appsettings.Development.json конфигурации для разработки, appsettings.Staging.json при промежуточном и appsettings.Production.json рабочем развертывании. Дополнительные сведения см. в статье Конфигурация ASP.NET Core Blazor.

Сериализуйте только утверждения о имени и роли.

В файле Program все утверждения сериализуются путём установки SerializeAllClaims в true. Если вы хотите сериализовать для CSR только имя и заявки о роли, удалите параметр или задайте для него значение false.

Укажите конфигурацию с помощью поставщика конфигурации JSON (параметры приложения)

Примеры проектов решений настраивают проверку подлинности носителя Microsoft Identity Web и JWT в своих Program файлах для обнаружения параметров конфигурации с помощью автозаполнения C#. Профессиональные приложения обычно используют поставщика конфигурации для настройки OIDC параметров, таких как поставщик конфигурации JSON по умолчанию. Поставщик конфигурации JSON загружает конфигурацию из файлов appsettings.json/appsettings.{ENVIRONMENT}.jsonпараметров приложения, где {ENVIRONMENT} заполнитель — среда выполнения приложения. Следуйте инструкциям в этом разделе, чтобы использовать файлы параметров приложения для настройки.

В файле параметров приложения (appsettings.json) BlazorWebAppEntra проекта добавьте следующую конфигурацию JSON:

{
  "AzureAd": {
    "CallbackPath": "/signin-oidc",
    "ClientId": "{CLIENT ID (BLAZOR APP)}",
    "Domain": "{DIRECTORY NAME}.onmicrosoft.com",
    "Instance": "https://login.microsoftonline.com/",
    "ResponseType": "code",
    "TenantId": "{TENANT ID}"
  },
  "DownstreamApi": {
    "BaseUrl": "{BASE ADDRESS}",
    "Scopes": [ "{APP ID URI}/Weather.Get" ]
  }
}

Чтобы значения, которые приложение использует в файле Program, совпадали, обновите заполнители в предыдущей конфигурации.

  • {CLIENT ID (BLAZOR APP)}: идентификатор приложения (клиента).
  • {DIRECTORY NAME}: имя каталога домена клиента (издателя).
  • {TENANT ID}: Идентификатор клиента в каталоге.
  • {BASE ADDRESS}: базовый адрес веб-API.
  • {APP ID URI}: URI идентификатора приложения для областей веб-API. Используется любой из следующих форматов, где заполнитель {CLIENT ID (WEB API)} представляет собой идентификатор клиента регистрации Entra веб-API, а заполнитель {DIRECTORY NAME} — это имя каталога домена арендатора (издателей) (например: contoso).
    • формат клиента ME-ID: api://{CLIENT ID (WEB API)}
    • Формат клиента B2C: https://{DIRECTORY NAME}.onmicrosoft.com/{CLIENT ID (WEB API)}

Пример:

"AzureAd": {
  "CallbackPath": "/signin-oidc",
  "ClientId": "00001111-aaaa-2222-bbbb-3333cccc4444",
  "Domain": "contoso.onmicrosoft.com",
  "Instance": "https://login.microsoftonline.com/",
  "ResponseType": "code",
  "TenantId": "aaaabbbb-0000-cccc-1111-dddd2222eeee"
},
"DownstreamApi": {
  "BaseUrl": "https://localhost:7277",
  "Scopes": [ "api://11112222-bbbb-3333-cccc-4444dddd5555/Weather.Get" ]
}

Обновите любые другие значения в предыдущей конфигурации, чтобы соответствовать пользовательским или не по умолчанию значениям, используемым в Program файле.

Конфигурация автоматически выбирается конструктором аутентификации.

Внесите следующие изменения в Program файл:

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
-   .AddMicrosoftIdentityWebApp(msIdentityOptions =>
-   {
-       msIdentityOptions.CallbackPath = "...";
-       msIdentityOptions.ClientId = "...";
-       msIdentityOptions.Domain = "...";
-       msIdentityOptions.Instance = "...";
-       msIdentityOptions.ResponseType = "...";
-       msIdentityOptions.TenantId = "...";
-   })
+   .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi()
-   .AddDownstreamApi("DownstreamApi", configOptions =>
-   {
-       configOptions.BaseUrl = "...";
-       configOptions.Scopes = [ "..." ];
-   })
+   .AddDownstreamApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi"))
    .AddDistributedTokenCaches();

Примечание.

Производственные приложения должны использовать поставщика кэша распределённых токенов в производственной среде. В противном случае приложение может иметь низкую производительность в некоторых сценариях. Дополнительные сведения см. в разделе "Использование поставщика кэша распределенных маркеров для рабочей среды ".

В проекте MinimalApiJwt добавьте следующую конфигурацию параметров приложения в файл appsettings.json.

"Authentication": {
  "Schemes": {
    "Bearer": {
      "Authority": "https://sts.windows.net/{TENANT ID (WEB API)}/",
      "ValidAudiences": [ "{APP ID URI (WEB API)}" ]
    }
  }
},

Чтобы значения, которые приложение использует в файле Program, совпадали, обновите заполнители в предыдущей конфигурации.

  • {TENANT ID (WEB API)}: идентификатор арендатора веб-API.
  • {APP ID URI (WEB API)}: URI идентификатора приложения веб-API.

Форматы власти принимают следующие шаблоны:

  • тип арендатора ME-ID: https://sts.windows.net/{TENANT ID}/
  • Тип клиента B2C: https://login.microsoftonline.com/{TENANT ID}/v2.0/

Форматы аудитории принимают следующие шаблоны ({CLIENT ID} является идентификатором клиента веб-API; {DIRECTORY NAME} это имя каталога, например contoso):

  • тип арендатора ME-ID: api://{CLIENT ID}
  • Тип клиента B2C: https://{DIRECTORY NAME}.onmicrosoft.com/{CLIENT ID}

Конфигурация автоматически выбирается построителем проверки подлинности носителя JWT.

Удалите следующие строки из Program файла:

- jwtOptions.Authority = "...";
- jwtOptions.Audience = "...";

Дополнительные сведения о конфигурации см. в следующих ресурсах:

Используйте поставщика распределенного кеша токенов в производственной среде

Кэши распределенных маркеров в памяти создаются при вызове AddDistributedTokenCaches , чтобы обеспечить базовую реализацию, доступную для кэширования распределенных маркеров.

Рабочие веб-приложения и веб-API должны использовать рабочий кэш распределенных маркеров (например, Redis, Microsoft SQL Server, Microsoft Azure Cosmos DB).

Примечание.

Для локальной разработки и тестирования на одном компьютере можно использовать кэши маркеров в памяти вместо распределенных кэшей маркеров:

builder.Services.AddInMemoryTokenCaches();

Позже, в период разработки и тестирования, используйте рабочий распределенный провайдер кэша токенов.

AddDistributedMemoryCache добавляет реализацию IDistributedCache по умолчанию, которая сохраняет элементы кэша в оперативной памяти и используется Microsoft Identity Web для кэширования токенов.

Кэш распределенных маркеров настраивается следующим образом MsalDistributedTokenCacheAdapterOptions:

  • В процессе разработки для отладки можно отключить кэш L1, установив для этого значение DisableL1Cachetrue. Не забудьте сбросить его обратно в рабочий false режим.
  • Задайте максимальный размер кэша L1, L1CacheOptions.SizeLimit чтобы предотвратить перерасход памяти сервера. Значение по умолчанию — 500 МБ.
  • Для целей отладки в разработке, вы можете отключить шифрование токенов в состоянии покоя, установив Encrypt на false, которое является значением по умолчанию. Не забудьте сбросить его обратно в рабочий true режим.
  • Установите вытеснение токена из кэша SlidingExpiration. Значение по умолчанию — 1 час.
  • Дополнительные сведения, включая рекомендации по обратному вызову для сбоев кэша L2 (OnL2CacheFailure) и асинхронных операций записи кэша L2 (EnableAsyncL2Write), см. в разделе MsalDistributedTokenCacheAdapterOptions и сериализация кэша маркеров: распределенные кэши маркеров.
builder.Services.AddDistributedMemoryCache();

builder.Services.Configure<MsalDistributedTokenCacheAdapterOptions>(
    options => 
    {
      // The following lines that are commented out reflect
      // default values. We recommend overriding the default
      // value of Encrypt to encrypt tokens at rest.

      //options.DisableL1Cache = false;
      //options.L1CacheOptions.SizeLimit = 500 * 1024 * 1024;
      options.Encrypt = true;
      //options.SlidingExpiration = TimeSpan.FromHours(1);
    });

AddDistributedMemoryCache требует ссылки на Microsoft.Extensions.Caching.Memory пакет NuGet.

Примечание.

Рекомендации по добавлению пакетов в приложения .NET см. в статьях в разделе "Установка пакетов и управление пакетами" в рабочем процессе потребления пакетов (документация NuGet). Проверьте правильность версий пакета на сайте NuGet.org.

Сведения о настройке поставщика распределенного кэша в рабочей среде см. в разделе " Распределенное кэширование" в ASP.NET Core.

Предупреждение

Всегда заменяйте распределенные кэши токенов в памяти на реального поставщика кэша токенов при развертывании приложения в рабочей среде. Если не удается внедрить поставщика кэша распределенных маркеров в промышленной среде, производительность приложения может значительно ухудшиться.

Дополнительные сведения см. в разделе сериализация кэша маркеров: распределенные кэши. Однако приведенные примеры кода не применяются к приложениям ASP.NET Core, которые настраивают распределенные кэши с помощью AddDistributedMemoryCache, а не AddDistributedTokenCache.

Используйте общедоступное кольцо ключей защиты данных в рабочей среде, чтобы экземпляры приложения на серверах в веб-ферме могли расшифровать маркеры, когда для MsalDistributedTokenCacheAdapterOptions.Encrypt задано значение true.

Примечание.

Для ранней разработки и локального тестирования на одном компьютере можно задать значение Encrypt как false, а позже настроить общее кольцо ключей защиты данных:

options.Encrypt = false;

Позже в период разработки и тестирования включите шифрование маркеров и установите общий круг ключей защиты данных.

В следующем примере показано, как использовать Azure Blob Storage и Azure Key Vault (PersistKeysToAzureBlobStorage/ProtectKeysWithAzureKeyVault) для общего набора ключей. Конфигурации служб — это базовые сценарии для демонстрационных целей. Перед развертыванием рабочих приложений познакомьтесь с функциями и услугами Azure и примите во внимание рекомендации по наилучшей практике использования в специальных наборах документации, ссылки на которые приведены в конце этого раздела.

Подтвердите наличие следующих пакетов в серверном проекте Blazor Web App:

Примечание.

Рекомендации по добавлению пакетов в приложения .NET см. в статьях в разделе "Установка пакетов и управление пакетами" в рабочем процессе потребления пакетов (документация NuGet). Проверьте правильность версий пакета на сайте NuGet.org.

Примечание.

Прежде чем продолжить действия, убедитесь, что приложение зарегистрировано в Microsoft Entra.

Следующий код обычно реализуется одновременно с реализацией поставщика распределенного кэша маркеров . Другие варианты, как в Azure, так и за пределами Azure, доступны для управления ключами защиты данных в нескольких экземплярах приложений, но в примере приложения показано, как использовать службы Azure.

Настройте хранилище BLOB-объектов Azure для поддержки ключей защиты данных. Следуйте указаниям поставщиков хранилища ключей в ASP.NET Core.

Настройте Azure Key Vault для шифрования неактивных ключей защиты данных. Следуйте инструкциям в разделе "Настройка ASP.NET Core Data Protection".

Используйте следующий код в Program файле, где зарегистрированы службы:

TokenCredential? credential;

if (builder.Environment.IsProduction())
{
    credential = new ManagedIdentityCredential("{MANAGED IDENTITY CLIENT ID}");
}
else
{
    // Local development and testing only
    DefaultAzureCredentialOptions options = new()
    {
        // Specify the tenant ID to use the dev credentials when running the app locally
        // in Visual Studio.
        VisualStudioTenantId = "{TENANT ID}",
        SharedTokenCacheTenantId = "{TENANT ID}"
    };

    credential = new DefaultAzureCredential(options);
}

builder.Services.AddDataProtection()
    .SetApplicationName("BlazorWebAppEntra")
    .PersistKeysToAzureBlobStorage(new Uri("{BLOB URI}"), credential)
    .ProtectKeysWithAzureKeyVault(new Uri("{KEY IDENTIFIER}"), credential);

Вы можете передать любое имя приложения в SetApplicationName. Убедитесь, что все развертывания приложений используют одинаковое значение.

{MANAGED IDENTITY CLIENT ID}: идентификатор управляемого Identity клиента Azure (GUID).

{TENANT ID}: идентификатор клиента.

{BLOB URI}: полный универсальный код ресурса (URI) к файлу ключа. Код URI создается службой хранилища Azure при создании файла ключа. Не используйте SAS.

{KEY IDENTIFIER}: идентификатор ключа Azure Key Vault, используемый для шифрования ключей. Политика доступа позволяет приложению получать доступ к хранилищу ключей с разрешениями Get, Unwrap Key и Wrap Key. Версия ключа получается из ключа, созданного на портале Entra или Azure. Если вы включаете автоматическое обновление ключа в хранилище ключей, убедитесь, что в конфигурации хранилища ключей приложения используется идентификатор ключа без указания версии. В конце этого идентификатора не должно быть GUID ключа (например, https://contoso.vault.azure.net/keys/data-protection).

Примечание.

В непроизводственных средах приведенный выше пример используется для упрощения проверки подлинности при разработке приложений, развертываемых в Azure, путем объединения учетных данных, используемых в средах хостинга Azure, с учетными данными, используемыми в процессе локальной разработки. Дополнительные сведения см. в статье Аутентификация размещенных в Azure приложений .NET в ресурсах Azure с помощью управляемого удостоверения, назначаемого системой.

Кроме того, можно настроить приложение для предоставления значений из файлов параметров приложения с помощью поставщика конфигурации JSON. Добавьте следующее в файл параметров приложения:

"DistributedTokenCache": {
  "DisableL1Cache": false,
  "L1CacheSizeLimit": 524288000,
  "Encrypt": true,
  "SlidingExpirationInHours": 1
},
"DataProtection": {
  "BlobUri": "{BLOB URI}",
  "KeyIdentifier": "{KEY IDENTIFIER}"
}

Пример DataProtection раздела:

"DataProtection": {
  "BlobUri": "https://contoso.blob.core.windows.net/data-protection/keys.xml",
  "KeyIdentifier": "https://contoso.vault.azure.net/keys/data-protection"
}

Примечание.

Идентификатор ключа в предыдущем примере является бессерверным. В конце идентификатора отсутствует версия ключа GUID. Это особенно важно, если вы решили настроить автоматическую смену ключей для ключа. Дополнительные сведения см. в статье "Настройка автоматического поворота криптографического ключа" в Azure Key Vault: политика смены ключей.

Внесите следующие изменения в Program файл:

builder.Services.Configure<MsalDistributedTokenCacheAdapterOptions>(
    options =>
    {
+       var config = builder.Configuration.GetSection("DistributedTokenCache");

-       options.DisableL1Cache = false;
+       options.DisableL1Cache = config.GetValue<bool>("DisableL1Cache");

-       options.L1CacheOptions.SizeLimit = 500 * 1024 * 1024;
+       options.L1CacheOptions.SizeLimit = config.GetValue<long>("L1CacheSizeLimit");

-       options.Encrypt = true;
+       options.Encrypt = config.GetValue<bool>("Encrypt");

-       options.SlidingExpiration = TimeSpan.FromHours(1);
+       options.SlidingExpiration = 
+           TimeSpan.FromHours(config.GetValue<int>("SlidingExpirationInHours"));
    });

- builder.Services.AddDataProtection()
-     .SetApplicationName("BlazorWebAppEntra")
-     .PersistKeysToAzureBlobStorage(new Uri("{BLOB URI}"), credential)
-     .ProtectKeysWithAzureKeyVault(new Uri("{KEY IDENTIFIER}"), credential);

Добавьте следующий код, где службы настроены в Program файле:

var config = builder.Configuration.GetSection("DataProtection");

builder.Services.AddDataProtection()
    .SetApplicationName("BlazorWebAppEntra")
    .PersistKeysToAzureBlobStorage(
        new Uri(config.GetValue<string>("BlobUri") ??
        throw new Exception("Missing Blob URI")),
        credential)
    .ProtectKeysWithAzureKeyVault(
        new Uri(config.GetValue<string>("KeyIdentifier") ?? 
        throw new Exception("Missing Key Identifier")), 
        credential);

Дополнительные сведения об использовании общего круга ключей Защиты данных и поставщиков хранилища ключей см. в следующих ресурсах:

Префикс назначения пересылки YARP

Переадресатор YARP серверного проекта Blazor Web App, в котором маркер доступа пользователя присоединен к MinimalApiJwt вызову веб-API, указывает префикс назначения https://weatherapi. Это значение соответствует имени проекта, переданному в файл AddProject проекта Program через Aspire.AppHost.

Переадресация в серверном Blazor Web App проекте (BlazorWebAppEntra):

app.MapForwarder("/weather-forecast", "https://weatherapi", transformBuilder =>
{
    ...
}).RequireAuthorization();

Сопоставление имени проекта в файле проекта ProgramAspire App HostAspire.AppHost:

var weatherApi = builder.AddProject<Projects.MinimalApiJwt>("weatherapi");

При развертывании Blazor Web App в рабочей среде не нужно изменять префикс назначения пересылки YARP. Пакет Microsoft Identity Web Downstream API использует базовый URI, передаваемый через конфигурацию, чтобы сделать вызов веб-API из ServerWeatherForecaster, а не из конечного префикса пересылки YARP. В рабочей среде средство пересылки YARP просто преобразует запрос, добавляя маркер доступа пользователя.

Перенаправление на домашнюю страницу при выходе

Компонент LogInOrOut (Layout/LogInOrOut.razor) задает скрытое поле для возвращаемого URL-адреса (ReturnUrl) для текущего URL-адреса (currentURL). Когда пользователь выходит из приложения, поставщик удостоверений возвращает пользователя на страницу, из которой они выошли. Если пользователь выходит из безопасной страницы, он возвращается на ту же безопасную страницу и отправляется обратно через процесс проверки подлинности. Этот поток проверки подлинности является разумным, если пользователям нужно регулярно изменять учетные записи.

В качестве альтернативы используйте следующий компонент LogInOrOut, который не предоставляет возвращаемый URL-адрес при выходе из системы.

Layout/LogInOrOut.razor:

<div class="nav-item px-3">
    <AuthorizeView>
        <Authorized>
            <form action="authentication/logout" method="post">
                <AntiforgeryToken />
                <button type="submit" class="nav-link">
                    <span class="bi bi-arrow-bar-left-nav-menu" aria-hidden="true">
                    </span> Logout
                </button>
            </form>
        </Authorized>
        <NotAuthorized>
            <a class="nav-link" href="authentication/login">
                <span class="bi bi-person-badge-nav-menu" aria-hidden="true"></span>
                Login
            </a>
        </NotAuthorized>
    </AuthorizeView>
</div>

Безопасность данных погоды

Дополнительные сведения о том, как это приложение защищает данные о погоде, см. в разделе Безопасные данные в Blazor Web Appс помощью интерактивной автоматической визуализации.

Устранение неполадок

Логирование

Серверное приложение — это стандартное приложение ASP.NET Core. Ознакомьтесь с руководством по ведению журнала ASP.NET Core, чтобы включить более низкий уровень ведения журнала в серверном приложении.

Чтобы включить ведение журнала отладки или трассировки для Blazor WebAssembly проверки подлинности, см. раздел Blazor ASP.NET Core с селектором версий статьи, установленным для ASP.NET Core в .NET 7 или более поздней версии.

Распространенные ошибки

  • Отладчик останавливается из-за исключения во время выхода с использованием внешнего идентификатора Microsoft Entra

    Следующее исключение останавливает отладчик Visual Studio во время выхода из системы с идентификатором Microsoft Entra External ID для внешнего доступа:

    Uncaught TypeError TypeError: Failed to execute 'postMessage' on 'Window': The provided value cannot be converted to a sequence.

    Отладчик Visual Studio прерывается при исключении JavaScript во время выхода из системы

    Исключение выбрасывается кодом Entra JavaScript, поэтому это не проблема ASP.NET Core. Исключение не влияет на функциональные возможности приложений в рабочей среде, поэтому исключение можно игнорировать во время локального тестирования разработки.

  • Неправильная настройка приложения или поставщика Identity (IP)

    Наиболее частые ошибки вызваны неправильной настройкой. Ниже приводятся несколько примеров.

    • В зависимости от требований сценария, отсутствующие или неправильные элементы, такие как удостоверяющий центр, экземпляр, идентификатор арендатора, домен арендатора, идентификатор клиента или URI перенаправления, не позволяют приложению проверить подлинность клиентов.
    • Неверные области запросов не позволяют клиентам получать доступ к конечным точкам веб-API сервера.
    • Неправильные или отсутствующие разрешения API сервера не позволяют клиентам получить доступ к конечным точкам веб-API сервера.
    • Запуск приложения на порте, отличном от настроенного в URI переадресации в регистрации приложения для IP-адреса. Обратите внимание, что порт не требуется для идентификатора Microsoft Entra и приложения, работающего на localhost адресе тестирования разработки, но конфигурация порта приложения и порт, на котором оно выполняется, должны соответствовать для всех остальных адресовlocalhost.

    Охват конфигурации в этой статье показывает примеры корректной конфигурации. Тщательно проверьте конфигурацию, ища неправильную настройку приложения и IP-адреса.

    Если конфигурация верна, выполните приведенные ниже действия.

    • Проанализируйте журналы приложений.

    • Изучите сетевой трафик между клиентским приложением и серверным приложением или IP с помощью инструментов разработчика браузера. Зачастую точное сообщение об ошибке или сообщение с указанием на то, что вызывает проблему, возвращается клиенту со стороны приложения IP или серверного приложения после выполнения запроса. Руководство по инструментам разработчика можно найти в следующих статьях:

    Команда разработчиков документации реагирует на отзывы о документах и ошибки в статьях (откройте запрос в разделе отзывов на этой странице), но не может предоставить поддержку продукта. Помощь в устранении неполадок в приложении предоставляют несколько общественных форумов поддержки. Мы рекомендуем следующее:

    Указанные выше форумы не принадлежат корпорации Майкрософт и не управляются ею.

    Чтобы сообщить о воспроизводимых ошибках платформы, которые не связаны с безопасностью, не являются секретными и не содержат конфиденциальной информации, откройте запрос в продуктовый отдел ASP.NET Core. Не открывайте обращение в поддержку продукта, пока вы тщательно не выясните причины проблемы и не попытаетесь решить её самостоятельно и с помощью сообщества на общедоступном форуме поддержки. Единица продукта не способна устранять неполадки отдельных приложений, которые не работают из-за неправильной конфигурации или вариантов использования с участием сторонних служб. Если отчет носит конфиденциальный характер или описывает потенциальный недостаток безопасности в продукте, который может использоваться злоумышленниками, см. статью "Отчеты о проблемах безопасности и ошибках" (в репозитории GitHub).

  • Несанкционированный клиент для ME-ID

    info: Авторизация не удалась в Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]. Эти требования не выполнены: DenyAnonymousAuthorizationRequirement: требуется прошедший проверку подлинности пользователь.

    Ошибка при выполнении обратного вызова авторизации из ME-ID:

    • Ошибка: unauthorized_client
    • Описание: AADB2C90058: The provided application is not configured to allow public clients.

    Чтобы устранить эту ошибку, сделайте следующее:

    1. На портале Azure перейдите к манифесту приложения.
    2. Задайте для атрибута allowPublicClient значение null или true.

Файлы cookie и данные сайта

Файлы cookie и данные сайта могут сохраняться в разных обновлениях приложений и повлиять на тестирование и устранение неполадок. При внесении изменений в код приложения, изменений в учетную запись пользователя у поставщика или изменений конфигурации приложения поставщика очистите следующее:

  • файлы cookie входа пользователей;
  • файлы cookie приложения;
  • кэшированные и сохраненные данные сайта.

Один из подходов, позволяющих предотвратить влияние устаревших файлов cookie и данных сайта на тестирование и устранение неполадок заключается в следующем:

  • Настройка браузера
    • Для тестирования используйте браузер, в котором можно настроить удаление всех файлов cookie и данных сайта при каждом закрытии браузера.
    • Убедитесь, что при любых изменениях в приложении, в данных тестового пользователя или в конфигурации поставщика закрытие браузера выполняется вручную или интегрированной средой разработки.
  • Используйте пользовательскую команду, чтобы открыть браузер в режиме InPrivate или Incognito в Visual Studio:
    • Откройте в Visual Studio диалоговое окно Просмотр с помощью с кнопки Запуск.
    • Нажмите кнопку Добавить.
    • Укажите путь к браузеру в поле Программа. Следующие пути к исполняемым файлам являются типичными расположениями установки для Windows 10. Если браузер установлен в другом расположении или вы используете операционную систему, отличную от Windows 10, укажите путь к исполняемому файлу браузера.
      • Microsoft Edge: C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe
      • Google Chrome: C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
      • Mozilla Firefox: C:\Program Files\Mozilla Firefox\firefox.exe
    • В поле "Аргументы" укажите параметр командной строки, который браузер использует для открытия в режиме InPrivate или Incognito. Для некоторых браузеров требуется URL-адрес приложения.
      • Microsoft Edge: используйте -inprivate.
      • Google Chrome: используйте --incognito --new-window {URL}, где {URL} — это заполнитель для URL-адреса, который нужно открыть (например, https://localhost:5001).
      • Mozilla Firefox: используйте -private -url {URL}, где {URL} заполнитель — это URL-адрес для открытия (например, https://localhost:5001).
    • Введите имя в поле дружественное имя. Например, Firefox Auth Testing.
    • Выберите кнопку ОК.
    • Чтобы не выбирать профиль браузера для каждой операции тестирования с помощью приложения, задайте профиль по умолчанию с помощью кнопки По умолчанию.
    • Убедитесь, что при любых изменениях в приложении, в данных тестового пользователя или в конфигурации поставщика закрытие браузера выполняется интегрированной средой разработки.

Обновление приложений

Работающее приложение может перестать функционировать сразу после обновления либо пакета SDK для .NET Core на компьютере разработчика, либо изменения версии пакетов в самом приложении. В некоторых случаях в результате важного обновления несогласованные пакеты могут нарушить работу приложения. Большинство этих проблем можно исправить следующим образом:

  1. Очистите кэши пакетов NuGet локальных систем, выполнив команду dotnet nuget locals all --clear из командной оболочки.
  2. Удалите папки bin и obj проекта.
  3. Восстановите и перестройте проект.
  4. Удалите все файлы из папки развертывания на сервере, прежде чем повторно развернуть приложение.

Примечание.

Использование версий пакета, несовместимых с требуемой платформой приложения, не поддерживается. Чтобы получить информацию о пакете, используйте NuGet Gallery.

Начните решение из правильного проекта

Blazor Web Apps:

  • Для одного из примеров шаблона Backend for-Frontend (BFF) запустите решение из Aspire/Aspire.AppHost проекта.
  • Для одного из примеров шаблонов, отличных от BFF, запустите решение из серверного проекта.

Blazor Server:

Запустите решение из серверного проекта.

Проверка пользователя

Следующий UserClaims компонент можно использовать непосредственно в приложениях или служить основой для дальнейшей настройки.

UserClaims.razor:

@page "/user-claims"
@using System.Security.Claims
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]

<PageTitle>User Claims</PageTitle>

<h1>User Claims</h1>

@if (claims.Any())
{
    <ul>
        @foreach (var claim in claims)
        {
            <li><b>@claim.Type:</b> @claim.Value</li>
        }
    </ul>
}

@code {
    private IEnumerable<Claim> claims = Enumerable.Empty<Claim>();

    [CascadingParameter]
    private Task<AuthenticationState>? AuthState { get; set; }

    protected override async Task OnInitializedAsync()
    {
        if (AuthState == null)
        {
            return;
        }

        var authState = await AuthState;
        claims = authState.User.Claims;
    }
}

Дополнительные ресурсы