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


Защита размещенного приложения ASP.NET Core Blazor WebAssembly с помощью IdentityServer

В этой статье описано, как создать новое размещенное решение Blazor WebAssembly, которое использует Duende Identity Server для проверки подлинности пользователей и вызовов API.

Внимание

Компания Duende Software может потребовать лицензионный сбор за использование Duende IdentityServer в рабочей среде. Дополнительные сведения см. в статье Миграция с ASP.NET Core 5.0 на 6.0.

Примечание.

Чтобы настроить автономное или размещенное приложение Blazor WebAssembly для использования существующего внешнего экземпляра Identity Server, следуйте указаниям в статье Защита изолированного приложения ASP.NET Core Blazor WebAssembly с помощью библиотеки проверки подлинности.

Дополнительные сведения о сценариях безопасности после чтения этой статьи см. в разделе ASP.NET Основных Blazor WebAssembly дополнительных сценариев безопасности.

Пошаговое руководство

В подразделах пошагового руководства объясняется, как:

  • Blazor Создание приложения
  • Выполнить приложение

Создание приложения Blazor

Чтобы создать новый проект Blazor WebAssembly с механизмом аутентификации, выполните следующие действия:

  1. Создание проекта

  2. Выберите шаблон приложения Blazor WebAssembly. Выберите Далее.

  3. Укажите имя проекта без использования дефисов. Проверьте правильность расположения. Выберите Далее.

    Избегайте использования дефисов (-) в имени проекта, который нарушает формирование идентификатора приложения OIDC. Логика в шаблоне Blazor WebAssembly проекта использует имя проекта для идентификатора приложения OIDC в конфигурации решения, а дефисы не допускаются в идентификаторе приложения OIDC. Также допускаются название в регистре Pascal (BlazorSample) и использование символов подчеркивания (Blazor_Sample).

  4. В диалоговом окне "Дополнительные сведения" выберите отдельные учетные записи в качестве типа проверки подлинности для хранения пользователей в приложении с помощью системы ASP.NET CoreIdentity.

  5. Установите флажок Размещение в ASP.NET Core.

  6. Нажмите кнопку "Создать", чтобы создать приложение.

Выполнить приложение

Запустите приложение из Server проекта. При использовании Visual Studio выполните одно из следующих действий.

  • Щелкните стрелку раскрывающегося списка рядом с кнопкой "Запустить ". Откройте раздел "Настройка запускаемых проектов " из раскрывающегося списка. Выберите параметр "Единый запуск проекта ". Подтвердите или измените проект запускаемого проекта на Server проект.

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

    • Нажмите кнопку Запустить.
    • В меню выберите Отладка>Начать отладку.
    • Нажмите клавишу F5.
  • В командной оболочке перейдите в Server папку проекта решения. Выполните команду dotnet run.

Части решения

В этом разделе описываются части решения, созданного на основе Blazor WebAssembly шаблона проекта, и описывается, как решения Client и Server проекты настроены для справки. В этом разделе нет конкретных рекомендаций для базового рабочего приложения, если вы создали приложение с помощью руководства в разделе "Пошаговое руководство ". Руководство в этом разделе полезно для обновления приложения для проверки подлинности и авторизации пользователей. Однако альтернативный подход к обновлению приложения — создать новое приложение из руководства в разделе "Пошаговое руководство " и переместить компоненты, классы и ресурсы приложения в новое приложение.

Server службы приложений

Этот раздел относится к приложению Server решения.

Регистрируются следующие службы.

  • В файле Program:

    • Entity Framework Core и ASP.NET Core Identity:

      builder.Services.AddDbContext<ApplicationDbContext>(options =>
          options.UseSqlite( ... ));
      builder.Services.AddDatabaseDeveloperPageExceptionFilter();
      
      builder.Services.AddDefaultIdentity<ApplicationUser>(options => 
              options.SignIn.RequireConfirmedAccount = true)
          .AddEntityFrameworkStores<ApplicationDbContext>();
      
    • Identity Сервер с дополнительным AddApiAuthorization вспомогательным методом, который настраивает соглашения по умолчанию ASP.NET Core поверх Identity сервера:

      builder.Services.AddIdentityServer()
          .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
      
    • Проверка подлинности с помощью дополнительного AddIdentityServerJwt вспомогательного метода, который настраивает приложение для проверки маркеров JWT, созданных сервером Identity :

      builder.Services.AddAuthentication()
          .AddIdentityServerJwt();
      
  • В методе Startup.ConfigureServices в файле Startup.cs:

    • Entity Framework Core и ASP.NET Core Identity:

      services.AddDbContext<ApplicationDbContext>(options =>
          options.UseSqlite(
              Configuration.GetConnectionString("DefaultConnection")));
      
      services.AddDefaultIdentity<ApplicationUser>(options => 
              options.SignIn.RequireConfirmedAccount = true)
          .AddEntityFrameworkStores<ApplicationDbContext>();
      
    • Identity Сервер с дополнительным AddApiAuthorization вспомогательным методом, который настраивает соглашения по умолчанию ASP.NET Core поверх Identity сервера:

      services.AddIdentityServer()
          .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
      
    • Проверка подлинности с помощью дополнительного AddIdentityServerJwt вспомогательного метода, который настраивает приложение для проверки маркеров JWT, созданных сервером Identity :

      services.AddAuthentication()
          .AddIdentityServerJwt();
      

Примечание.

При регистрации одной схемы проверки подлинности схема проверки подлинности автоматически используется в качестве схемы по умолчанию приложения, и не требуется указать схему или AddAuthentication через нее AuthenticationOptions. Дополнительные сведения см. в разделе "Обзор проверки подлинности ядра" ASP.NET и объявление ASP.NET Core (aspnet/Announcements #490).

  • В файле Program:
  • В методе Startup.Configure в файле Startup.cs:
  • ПО Identity промежуточного слоя сервера предоставляет конечные точки OpenID Подключение (OIDC):

    app.UseIdentityServer();
    
  • ПО промежуточного слоя проверки подлинности отвечает за проверку учетных данных запроса и настройку пользователя в контексте запроса:

    app.UseAuthentication();
    
  • ПО промежуточного слоя для проверки подлинности обеспечивает возможности авторизации:

    app.UseAuthorization();
    

Авторизация API

Этот раздел относится к приложению Server решения.

Вспомогательный метод AddApiAuthorization настраивает Identity Server для сценариев ASP.NET Core. Identity Server — это функциональная и расширяемая платформа для повышения уровня безопасности приложений. Identity Сервер предоставляет ненужную сложность для наиболее распространенных сценариев. Следовательно, предусмотрен набор соглашений и параметров конфигурации, которые можно рассматривать в качестве хорошей отправной точки. После изменения проверки подлинности полная мощность Identity сервера доступна для настройки проверки подлинности в соответствии с требованиями приложения.

Добавление обработчика проверки подлинности для API, который сосуществует с Identity сервером

Этот раздел относится к приложению Server решения.

Вспомогательный метод AddIdentityServerJwt настраивает схему политики для приложения в качестве обработчика проверки подлинности по умолчанию. Политика настроена, чтобы разрешить Identity обрабатывать все запросы, перенаправленные в любой Identity подпат, в пространстве URL-адресов в разделе /Identity. JwtBearerHandler обрабатывает все остальные запросы. Кроме того, этот метод:

  • Регистрирует ресурс API на Identity сервере с область {PROJECT NAME}APIпо умолчанию, где {PROJECT NAME} заполнитель — имя проекта при создании приложения.
  • Настраивает ПО промежуточного слоя маркера носителя JWT для проверки маркеров, выданных сервером Identity для приложения.

Контроллер прогноза погоды

Этот раздел относится к приложению Server решения.

В WeatherForecastController (Controllers/WeatherForecastController.cs) к классу применяется атрибут [Authorize]. Атрибут указывает, что пользователь должен быть авторизован на основе политики по умолчанию, чтобы получить доступ к ресурсу. Политика авторизации по умолчанию настроена на использование схемы проверки подлинности по умолчанию, которая настраивается AddIdentityServerJwt. Вспомогательный метод настраивает JwtBearerHandler в качестве обработчика по умолчанию для запросов к приложению.

Контекст базы данных приложения

Этот раздел относится к приложению Server решения.

В поле ApplicationDbContext (Data/ApplicationDbContext.cs) DbContext расширяется ApiAuthorizationDbContext<TUser> , чтобы включить схему для Identity сервера. Класс ApiAuthorizationDbContext<TUser> является производным от IdentityDbContext.

Чтобы получить полный контроль над схемой базы данных, наследуйте один из доступных классов IdentityDbContext и настройте контекст для включения схемы Identity путем вызова builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value) в методе OnModelCreating.

Контроллер конфигурации OIDC

Этот раздел относится к приложению Server решения.

В OidcConfigurationController (Controllers/OidcConfigurationController.cs) выполняется подготовка конечной точки клиента для обслуживания параметров OIDC.

Параметры приложения

Этот раздел относится к приложению Server решения.

В файле параметров приложения (appsettings.json) в корневом каталоге проекта в разделе IdentityServer описывается список настроенных клиентов. В следующем примере есть один клиент. Имя клиента соответствует Client имени сборки приложения и сопоставляется по соглашению с параметром OAuth ClientId . Профиль указывает на настраиваемый тип приложения. Профиль используется внутри для обозначения соглашений, упрощающих процесс настройки сервера.

"IdentityServer": {
  "Clients": {
    "{ASSEMBLY NAME}": {
      "Profile": "IdentityServerSPA"
    }
  }
}

Заполнитель {ASSEMBLY NAME} — это Client имя сборки приложения (например, BlazorSample.Client).

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

Этот раздел относится к приложению Client решения.

Когда приложение создается для использования отдельных учетных записей пользователей (Individual), оно автоматически получает ссылку на пакет Microsoft.AspNetCore.Components.WebAssembly.Authentication. В пакете содержится набор примитивов, которые помогают приложению проверять подлинность пользователей и получать маркеры для вызова защищенных API.

При добавлении проверки подлинности в приложение вручную добавьте пакет Microsoft.AspNetCore.Components.WebAssembly.Authentication в приложение.

Примечание.

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

HttpClientКонфигурация

Этот раздел относится к приложению Client решения.

Program В файле имя HttpClient настраивается для предоставления HttpClient экземпляров, включающих маркеры доступа при выполнении запросов к API сервера. По умолчанию при создании решения именованное HttpClient{PROJECT NAME}.ServerAPI{PROJECT NAME} имя — это заполнитель, имя которого является именем проекта.

builder.Services.AddHttpClient("{PROJECT NAME}.ServerAPI", 
        client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
    .AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
    .CreateClient("{PROJECT NAME}.ServerAPI"));

Заполнитель {PROJECT NAME} — это имя проекта при создании решения. Например, если укажите имя BlazorSample проекта, создающее имя.HttpClientBlazorSample.ServerAPI

Примечание.

Если вы хотите настроить приложение Blazor WebAssembly на использование существующего экземпляра IdentityServer, который не является частью размещенного решения Blazor, измените регистрацию базового адреса HttpClient с IWebAssemblyHostEnvironment.BaseAddress (builder.HostEnvironment.BaseAddress) на URL-адрес конечной точки авторизации API серверного приложения.

Поддержка авторизации API

Этот раздел относится к приложению Client решения.

Поддержка проверки подлинности пользователей подключается в контейнере службы с помощью метода расширения, предоставляемого в пакете Microsoft.AspNetCore.Components.WebAssembly.Authentication. Этот метод настраивает службы, необходимые для взаимодействия с существующей системой авторизации.

builder.Services.AddApiAuthorization();

По умолчанию конфигурация приложения загружается по соглашению от _configuration/{client-id}. По соглашению в качестве идентификатора клиента используется имя сборки приложения. Этот URL-адрес можно изменить, чтобы он указывал на отдельную конечную точку, вызвав перегрузку с параметрами.

Файл Imports

Этот раздел относится к приложению Client решения.

Пространство имен Microsoft.AspNetCore.Components.Authorization становится доступным для всего приложения с помощью файла _Imports.razor:

@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
@using {APPLICATION ASSEMBLY}
@using {APPLICATION ASSEMBLY}.Shared

Index Страницы

Этот раздел относится к приложению Client решения.

Страница индекса (wwwroot/index.html) содержит сценарий, определяющий AuthenticationService в JavaScript. AuthenticationService обрабатывает низкоуровневые сведения о протоколе OIDC. Приложение внутренне вызывает методы, определенные в сценарии для выполнения операций проверки подлинности.

<script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"></script>

Компонент App

Этот раздел относится к приложению Client решения.

Компонент App (App.razor) аналогичен компоненту App, который находится в приложениях Blazor Server:

  • Компонент CascadingAuthenticationState управляет предоставлением AuthenticationState остальным приложениям.
  • Компонент AuthorizeRouteView гарантирует, что текущий пользователь имеет право доступа к определенной странице или иным образом работать с компонентом RedirectToLogin.
  • Компонент RedirectToLogin управляет перенаправлением неавторизованных пользователей на страницу входа.

Из-за различий в платформе между выпусками ASP.NET Core в этом разделе отсутствует разметка Razor для компонента App (App.razor). Чтобы изучить разметку этого компонента для конкретного выпуска, используйте один из следующих подходов:

  • создайте приложение, подготовленное для проверки подлинности, на основе шаблона проекта по умолчанию Blazor WebAssembly для версии ASP.NET Core, которую предполагается использовать. Изучите компонент App (App.razor) в созданном приложении;

  • изучите компонент App (App.razor) в справочных материалах. Выберите версию из селектора ветви и найдите компонент в ProjectTemplates папке репозитория, так как он перемещен на протяжении многих лет.

    Примечание.

    По ссылкам в документации на справочные материалы по .NET обычно загружается ветвь репозитория по умолчанию, которая представляет текущую разработку для следующего выпуска .NET. Чтобы выбрать тег для определенного выпуска, используйте раскрывающийся список Switch branches or tags (Переключение ветвей или тегов). Дополнительные сведения см. в статье Выбор тега версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Компонент RedirectToLogin

Этот раздел относится к приложению Client решения.

Компонент RedirectToLogin (RedirectToLogin.razor):

  • управляет перенаправлением неавторизованных пользователей на страницу входа.
  • Текущий URL-адрес, который пользователь пытается получить доступ, сохраняется таким образом, чтобы его можно было вернуть на ту страницу, если проверка подлинности выполнена успешно:
    • Состояние журнала навигации в ASP.NET Core в .NET 7 или более поздней версии.
    • Строка запроса в ASP.NET Core в .NET 6 или более ранней версии.

Изучите компонент RedirectToLogin в справочных материалах. Расположение компонента изменилось с течением времени, поэтому используйте средства поиска GitHub для поиска компонента.

Примечание.

По ссылкам в документации на справочные материалы по .NET обычно загружается ветвь репозитория по умолчанию, которая представляет текущую разработку для следующего выпуска .NET. Чтобы выбрать тег для определенного выпуска, используйте раскрывающийся список Switch branches or tags (Переключение ветвей или тегов). Дополнительные сведения см. в статье Выбор тега версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Компонент LoginDisplay

Этот раздел относится к приложению Client решения.

Компонент LoginDisplay (LoginDisplay.razor) отображается в компоненте MainLayout (MainLayout.razor) и управляет следующими поведениями.

  • Для прошедших проверку подлинности пользователей:
    • Отображает имя текущего пользователя.
    • Отображает ссылку на страницу профиля пользователя в ASP.NET Core Identity.
    • отображает кнопку для выхода из приложения.
  • Для анонимных пользователей:
    • Предоставляет возможность регистрации.
    • Предоставляет возможность входа в систему.

Из-за различий в платформе между выпусками ASP.NET Core в этом разделе отсутствует разметка Razor для компонента LoginDisplay. Чтобы изучить разметку этого компонента для конкретного выпуска, используйте один из следующих подходов:

  • создайте приложение, подготовленное для проверки подлинности, на основе шаблона проекта по умолчанию Blazor WebAssembly для версии ASP.NET Core, которую предполагается использовать. Изучите компонент LoginDisplay в созданном приложении.

  • Изучите компонент LoginDisplay в справочных материалах. Расположение компонента изменилось с течением времени, поэтому используйте средства поиска GitHub для поиска компонента. В качестве шаблонного содержимого используется Hosted со значением true.

    Примечание.

    По ссылкам в документации на справочные материалы по .NET обычно загружается ветвь репозитория по умолчанию, которая представляет текущую разработку для следующего выпуска .NET. Чтобы выбрать тег для определенного выпуска, используйте раскрывающийся список Switch branches or tags (Переключение ветвей или тегов). Дополнительные сведения см. в статье Выбор тега версии исходного кода ASP.NET Core (dotnet/AspNetCore.Docs #26205).

Компонент Authentication

Этот раздел относится к приложению Client решения.

Страница, созданная компонентом Authentication (Pages/Authentication.razor), определяет маршруты, необходимые для обработки различных этапов проверки подлинности.

Компонент RemoteAuthenticatorView:

@page "/authentication/{action}"
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication

<RemoteAuthenticatorView Action="Action" />

@code {
    [Parameter]
    public string? Action { get; set; }
}

Примечание.

В ASP.NET Core в .NET 6 или более поздней версии поддерживается статический анализ со значением NULL и компилятора .NET. До выпуска ASP.NET Core в .NET 6 string тип отображается без обозначения типа NULL (?).

Компонент FetchData

Этот раздел относится к приложению Client решения.

Компонент FetchData показывает, как:

  • подготовить маркер доступа;
  • использовать маркер доступа для вызова API защищенных ресурсов в приложении Server.

Директива @attribute [Authorize] указывает на систему авторизации Blazor WebAssembly, в которой пользователь должен пройти авторизацию для перехода к этому компоненту. Наличие атрибута в приложении Client не мешает вызову API на сервере без соответствующих учетных данных. Приложение Server также должно использовать [Authorize] на соответствующих конечных точках, чтобы правильно защитить их.

IAccessTokenProvider.RequestAccessToken запрашивает маркера доступа, который можно добавить в запрос для вызова API. Если маркер кэшируется или служба может подготовить новый маркер доступа без вмешательства пользователя, запрос маркера будет выполнен. В противном случае запрос маркера завершается ошибкой AccessTokenNotAvailableException, которая перехватывается инструкцией try-catch.

Чтобы получить фактический токен для включения в запрос, приложение должно проверить, что запрос был обработан, вызвав tokenResult.TryGetToken(out var token).

Если запрос был успешным, переменная маркера заполняется маркером доступа. Свойство AccessToken.Value маркера предоставляет строку литерала для включения в заголовок запроса Authorization.

Если запрос завершился ошибкой, поскольку не удалось подготовить маркер без взаимодействия с пользователем:

  • ASP.NET Core в .NET 7 или более поздней версии: приложение переходит к AccessTokenResult.InteractiveRequestUrl использованию заданного AccessTokenResult.InteractionOptions , чтобы разрешить обновление маркера доступа.
  • ASP.NET Core в .NET 6 или более ранней версии: результат маркера содержит URL-адрес перенаправления. При переходе по этому URL-адресу пользователь переходит на страницу входа и возвращается к текущей странице после успешной проверки подлинности.
@page "/fetchdata"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using {APP NAMESPACE}.Shared
@attribute [Authorize]
@inject HttpClient Http

...

@code {
    private WeatherForecast[] forecasts;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
        }
        catch (AccessTokenNotAvailableException exception)
        {
            exception.Redirect();
        }
    }
}

Служба приложений Azure в Linux

При развертывании в Службе приложений Azure в Linux издателя нужно указать явно. Дополнительные сведения см. в статье "Защита Identity серверной части веб-API для spAs".

Утверждение имени и роли с помощью авторизации API

Настраиваемая фабрика пользователей

В приложении Client создайте настраиваемую фабрику пользователей. Identity Server отправляет несколько ролей в формате массива JSON в одном утверждении role. Одна роль отправляется как строковое значение в утверждении. Фабрика создает отдельное утверждение role для каждой из ролей пользователя.

CustomUserFactory.cs:

using System.Security.Claims;
using System.Text.Json;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;

public class CustomUserFactory
    : AccountClaimsPrincipalFactory<RemoteUserAccount>
{
    public CustomUserFactory(IAccessTokenProviderAccessor accessor)
        : base(accessor)
    {
    }

    public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
        RemoteUserAccount account,
        RemoteAuthenticationUserOptions options)
    {
        var user = await base.CreateUserAsync(account, options);

        if (user.Identity is not null && user.Identity.IsAuthenticated)
        {
            var identity = (ClaimsIdentity)user.Identity;
            var roleClaims = identity.FindAll(identity.RoleClaimType).ToArray();

            if (roleClaims.Any())
            {
                foreach (var existingClaim in roleClaims)
                {
                    identity.RemoveClaim(existingClaim);
                }

                var rolesElem = 
                    account.AdditionalProperties[identity.RoleClaimType];

                if (options.RoleClaim is not null && rolesElem is JsonElement roles)
                {
                    if (roles.ValueKind == JsonValueKind.Array)
                    {
                        foreach (var role in roles.EnumerateArray())
                        {
                            var roleValue = role.GetString();

                            if (!string.IsNullOrEmpty(roleValue))
                            {
                                identity.AddClaim(
                                  new Claim(options.RoleClaim, roleValue));
                            }

                        }
                    }
                    else
                    {
                        var roleValue = roles.GetString();

                        if (!string.IsNullOrEmpty(roleValue))
                        {
                            identity.AddClaim(
                              new Claim(options.RoleClaim, roleValue));
                        }
                    }
                }
            }
        }

        return user;
    }
}
using System.Linq;
using System.Security.Claims;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;

public class CustomUserFactory
    : AccountClaimsPrincipalFactory<RemoteUserAccount>
{
    public CustomUserFactory(IAccessTokenProviderAccessor accessor)
        : base(accessor)
    {
    }

    public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
        RemoteUserAccount account,
        RemoteAuthenticationUserOptions options)
    {
        var user = await base.CreateUserAsync(account, options);

        if (user.Identity.IsAuthenticated)
        {
            var identity = (ClaimsIdentity)user.Identity;
            var roleClaims = identity.FindAll(identity.RoleClaimType).ToArray();

            if (roleClaims.Any())
            {
                foreach (var existingClaim in roleClaims)
                {
                    identity.RemoveClaim(existingClaim);
                }

                var rolesElem = account.AdditionalProperties[identity.RoleClaimType];

                if (rolesElem is JsonElement roles)
                {
                    if (roles.ValueKind == JsonValueKind.Array)
                    {
                        foreach (var role in roles.EnumerateArray())
                        {
                            identity.AddClaim(new Claim(options.RoleClaim, role.GetString()));
                        }
                    }
                    else
                    {
                        identity.AddClaim(new Claim(options.RoleClaim, roles.GetString()));
                    }
                }
            }
        }

        return user;
    }
}

Client В приложении зарегистрируйте фабрику Program в файле:

builder.Services.AddApiAuthorization()
    .AddAccountClaimsPrincipalFactory<CustomUserFactory>();

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

В файле Program:

using Microsoft.AspNetCore.Identity;

...

builder.Services.AddDefaultIdentity<ApplicationUser>(options => 
    options.SignIn.RequireConfirmedAccount = true)
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

В Startup.cs:

using Microsoft.AspNetCore.Identity;

...

services.AddDefaultIdentity<ApplicationUser>(options => 
    options.SignIn.RequireConfirmedAccount = true)
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

Настройка Identity Server

Воспользуйтесь одним из перечисленных ниже подходов.

Параметры авторизации API

В приложении Server:

  • Настройте Identity Server для размещения утверждений name и role в маркере идентификации и маркере доступа.
  • Запретите сопоставление по умолчанию для ролей в обработчике маркера JWT.

В файле Program:

using System.IdentityModel.Tokens.Jwt;

...

builder.Services.AddIdentityServer()
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options => {
        options.IdentityResources["openid"].UserClaims.Add("name");
        options.ApiResources.Single().UserClaims.Add("name");
        options.IdentityResources["openid"].UserClaims.Add("role");
        options.ApiResources.Single().UserClaims.Add("role");
    });

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

В Startup.cs:

using System.IdentityModel.Tokens.Jwt;
using System.Linq;

...

services.AddIdentityServer()
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options => {
        options.IdentityResources["openid"].UserClaims.Add("name");
        options.ApiResources.Single().UserClaims.Add("name");
        options.IdentityResources["openid"].UserClaims.Add("role");
        options.ApiResources.Single().UserClaims.Add("role");
    });

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

Служба профилей

В приложении Server создайте реализацию ProfileService.

ProfileService.cs:

using IdentityModel;
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Services;

public class ProfileService : IProfileService
{
    public ProfileService()
    {
    }

    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var nameClaim = context.Subject.FindAll(JwtClaimTypes.Name);
        context.IssuedClaims.AddRange(nameClaim);

        var roleClaims = context.Subject.FindAll(JwtClaimTypes.Role);
        context.IssuedClaims.AddRange(roleClaims);

        await Task.CompletedTask;
    }

    public async Task IsActiveAsync(IsActiveContext context)
    {
        await Task.CompletedTask;
    }
}
using IdentityModel;
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Services;
using System.Threading.Tasks;

public class ProfileService : IProfileService
{
    public ProfileService()
    {
    }

    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var nameClaim = context.Subject.FindAll(JwtClaimTypes.Name);
        context.IssuedClaims.AddRange(nameClaim);

        var roleClaims = context.Subject.FindAll(JwtClaimTypes.Role);
        context.IssuedClaims.AddRange(roleClaims);

        await Task.CompletedTask;
    }

    public async Task IsActiveAsync(IsActiveContext context)
    {
        await Task.CompletedTask;
    }
}

Server В приложении зарегистрируйте службу профилей Program в файле:

using Duende.IdentityServer.Services;

...

builder.Services.AddTransient<IProfileService, ProfileService>();

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

Server В приложении зарегистрируйте службу профилей в Startup.ConfigureServices следующих элементахStartup.cs:

using IdentityServer4.Services;

...

services.AddTransient<IProfileService, ProfileService>();

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

Использование механизмов авторизации

На этом этапе подходы с авторизацией компонентов в приложении Client являются функциональными. Любой из механизмов авторизации в компонентах может использовать роль для авторизации пользователя:

Для User.Identity.Name в приложении Client указывается имя пользователя, которое обычно является адресом электронной почты для входа.

UserManager и SignInManager.

Задайте тип утверждения идентификатора пользователя, если требуется серверное приложение:

  • UserManager<TUser> или SignInManager<TUser> в конечной точке API.
  • Подробные сведения, связанные с IdentityUser, например имя пользователя, адрес электронной почты или время окончания блокировки.

В Program.cs ASP.NET Core в .NET 6 или более поздней версии:

using System.Security.Claims;

...

builder.Services.Configure<IdentityOptions>(options => 
    options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier);

В Startup.ConfigureServices для версий ASP.NET Core, предшествующих версии 6.0:

using System.Security.Claims;

...

services.Configure<IdentityOptions>(options => 
    options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier);

В следующем WeatherForecastController случае метод регистрируется UserName при вызове Get метода.

Примечание.

В следующем примере используется пространство имен с область файлами, которое является функцией C# 10 или более поздней версии (.NET 6 или более поздней версии).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using BlazorSample.Server.Models;
using BlazorSample.Shared;

namespace BlazorSample.Server.Controllers;

[Authorize]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private readonly UserManager<ApplicationUser> userManager;

    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", 
        "Balmy", "Hot", "Sweltering", "Scorching"
    };

    private readonly ILogger<WeatherForecastController> logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger, 
        UserManager<ApplicationUser> userManager)
    {
        this.logger = logger;
        this.userManager = userManager;
    }

    [HttpGet]
    public async Task<IEnumerable<WeatherForecast>> Get()
    {
        var rng = new Random();

        var user = await userManager.GetUserAsync(User);

        if (user != null)
        {
            logger.LogInformation("User.Identity.Name: {UserIdentityName}", user.UserName);
        }

        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = rng.Next(-20, 55),
            Summary = Summaries[rng.Next(Summaries.Length)]
        })
        .ToArray();
    }
}

В предыдущем примере:

  • Пространство имен проекта Server — BlazorSample.Server.
  • Пространство имен проекта Shared — BlazorSample.Shared.

Размещение в Службе приложений Azure с использованием личного домена и сертификата

В следующем руководстве описывается:

  • как развернуть размещенное приложение Blazor WebAssembly с Identity Server в Службе приложений Azure с использованием личного домена;
  • как создать и использовать TLS-сертификат для обмена данными по протоколу HTTPS с браузерами. Хотя руководство посвящено использованию сертификата с личным доменом, рекомендации также можно применять при работе с доменом приложений Azure по умолчанию, например contoso.azurewebsites.net.

В этом сценарии размещения не используйте один и тот же сертификат для ключа подписывания токена Identity Server и защищенного обмена данными по протоколу HTTPS между сайтом и браузерами.

  • Использование разных сертификатов в этих целях рекомендуется из соображений безопасности, так как позволяет изолировать закрытые ключи разного назначения.
  • Управление TLS-сертификатами для обмена данными с браузерами производится независимо и не влияет на подписывание маркеров Identity Server.
  • Когда Azure Key Vault предоставляет сертификат приложению Службы приложений для привязки к личному домену, Identity Server не может получить тот же сертификат из Azure Key Vault для подписывания токена. Хотя Identity Server можно настроить так, чтобы он использовал тот же TLS-сертификат по физическому пути, размещать сертификаты безопасности в системе управления версиями не рекомендуется, в большинстве случаев этого следует избегать.

Далее в Azure Key Vault создается самозаверяющий сертификат исключительно для подписывания токена Identity Server. Конфигурация Identity Server использует сертификат из хранилища ключей посредством хранилища сертификатов CurrentUser>My приложения. Другие сертификаты, используемые для трафика HTTPS личных доменов, создаются и настраиваются отдельно от сертификата подписывания Identity Server.

Чтобы настроить приложение, Службу приложений Azure и Azure Key Vault для размещения в личном домене с HTTPS, выполните указанные ниже действия.

  1. Создайте план Службы приложений уровня Basic B1 или выше. Для использования личных доменов Службе приложений требуется уровень служб Basic B1 или более высокий.

  2. Создайте PFX-сертификат для безопасного обмена данными между сайтом и браузером (по протоколу HTTPS) с полным доменным именем (FQDN) сайта, которое контролирует организация (например, www.contoso.com), в качестве общего имени. Создайте сертификат с указанными ниже параметрами.

    • Использование ключа
      • проверку цифровой подписи (digitalSignature);
      • шифрование ключей (keyEncipherment).
    • Использование расширенного или расширенного ключа
      • проверка подлинности клиента (1.3.6.1.5.5.7.3.2);
      • проверка подлинности сервера (1.3.6.1.5.5.7.3.1).

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

    Запишите пароль, который будет использоваться позже для импорта сертификата в Azure Key Vault.

    Дополнительные сведения о сертификатах Azure Key Vault см. в статье Azure Key Vault: сертификаты.

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

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

  5. В Azure Key Vault создайте самозаверяющий сертификат для подписывания токена Identity Server. Укажите имя сертификата и субъект. Субъект указывается в формате CN={COMMON NAME}, где заполнитель {COMMON NAME} — это общее имя сертификата. Общим именем может быть любая буквенно-цифровая строка. Например, CN=IdentityServerSigning — допустимый субъект сертификата. В разделе Политика выдачи>Расширенная настройка политики используйте настройки по умолчанию. Запишите отпечаток сертификата, который будет использоваться позже в конфигурации приложения.

  6. Перейдите к Службе приложений Azure на портале Azure и создайте Службу приложений со следующей конфигурацией:

    • Опубликовать — Code;
    • Стек времени выполнения — среда выполнения приложения;
    • Номер SKU и размер — уровень Службы приложений не ниже Basic B1. Для использования личных доменов Службе приложений требуется уровень служб Basic B1 или более высокий.
  7. После того как в Azure будет создана Служба приложений, откройте конфигурацию приложения и добавьте новый параметр, указав записанные ранее отпечатки сертификатов. Ключ параметра — WEBSITE_LOAD_CERTIFICATES. В значении параметра разделяйте отпечатки запятыми, как показано в следующем примере.

    • Ключ: WEBSITE_LOAD_CERTIFICATES.
    • Значение: 57443A552A46DB...D55E28D412B943565,29F43A772CB6AF...1D04F0C67F85FB0B1

    На портале Azure параметры приложения сохраняются в два шага: сохраните параметр WEBSITE_LOAD_CERTIFICATES, а затем в верхней части колонки нажмите кнопку Сохранить.

  8. Выберите параметры TLS/SSL приложения. Выберите Сертификаты закрытых ключей (PFX). Используйте процесс Импорт сертификата Key Vault. Дважды выполните процесс, чтобы импортировать и сертификат сайта для обмена данными по протоколу HTTPS, и самозаверяющий сертификат сайта для подписывания токена Identity Server.

  9. Перейдите к колонке Личные домены. На веб-сайте регистратора своего домена используйте IP-адрес и идентификатор проверки личного домена для настройки домена. Типичная конфигурация домена включает в себя следующие параметры:

    • запись A с узлом@ и значением IP-адреса с портала Azure;
    • запись TXT с узломasuid и значением идентификатора проверки, созданным Azure и предоставленным на портале Azure.

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

  10. Вернитесь к колонке Личные домены на портале Azure. Нажмите кнопку Добавить личный домен. Выберите параметр Запись A. Укажите домен и выберите команду Проверить. Если записи домена верны и распространены в Интернете, на портале можно будет нажать кнопку Добавить личный домен.

    На распространение изменений регистрации домена на серверах доменных имен (DNS) в Интернете после их обработки регистратором домена может потребоваться несколько дней. Если записи домена не были обновлены в течение трех рабочих дней, убедитесь в том, что они были правильно заданы на сайте регистратора, и обратитесь в его службу поддержки клиентов.

  11. В колонке Личные доменыСОСТОЯНИЕ SSL домена должно иметь значение Not Secure. Щелкните ссылку Добавить привязку. Выберите HTTPS-сертификат сайта из хранилища ключей для привязки личного домена.

  12. В Visual Studio откройте файл параметров приложения для серверного проекта (appsettings.json или appsettings.Production.json). В конфигурации Identity Server добавьте приведенный ниже раздел Key. Укажите субъект самозаверяющего сертификата для ключа Name. В следующем примере в хранилище ключей сертификату присвоено общее имя IdentityServerSigning, поэтому субъект — CN=IdentityServerSigning.

    "IdentityServer": {
    
      ...
    
      "Key": {
        "Type": "Store",
        "StoreName": "My",
        "StoreLocation": "CurrentUser",
        "Name": "CN=IdentityServerSigning"
      }
    },
    
  13. В Visual Studio создайте профиль публикации Службы приложений Azure для серверного проекта. В строке меню выберите: Сборка>Опубликовать>Создать>Azure>Служба приложений Azure (Windows или Linux). Когда Visual Studio подключится к подписке Azure, вы сможете настроить представление ресурсов Azure по типу ресурса. В списке Веб-приложение найдите Службу приложений для приложения и выберите ее. Выберите Готово.

  14. Когда в Visual Studio снова отобразится окно Публикация, зависимости хранилища ключей и службы базы данных SQL Server будут обнаружены автоматически.

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

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

  15. Щелкните ссылку Изменить под именем профиля развертывания в верхней части окна. Измените URL-адрес назначения на URL-адрес личного домена сайта (например, https://www.contoso.com). Сохранить параметры.

  16. Публикация приложения. В Visual Studio откроется окно браузера, и будет выполнен запрос к сайту в личном домене.

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

Мы рекомендуем использовать новое окно браузера в частном режиме (например, режим Microsoft Edge InPrivate или режим Google Chrome Incognito) для каждого теста приложения после изменения приложения, конфигурации приложения или служб Azure в портал Azure. Оставшиеся после предыдущего тестового запуска файлы cookie могут привести к ошибкам проверки подлинности или авторизации при тестировании сайта, даже если его конфигурация верна. Дополнительные сведения о настройке Visual Studio для открытия нового окна частного браузера для каждого тестового запуска см. в Cookieразделе данных сайта и s.

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

При устранении проблемы с загрузкой сертификата для подписи ключа Identity Server выполните приведенную ниже команду в командной оболочке PowerShell Kudu на портале Azure. Она выводит список сертификатов, к которым приложение может получить доступ в хранилище сертификатов CurrentUser>My. Выходные данные включают в себя субъекты и отпечатки сертификатов, которые могут быть полезны при отладке приложения.

Get-ChildItem -path Cert:\CurrentUser\My -Recurse | Format-List DnsNameList, Subject, Thumbprint, EnhancedKeyUsageList

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

Ведение журнала

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

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

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

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

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

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

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

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

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

    • Для выпусков, Blazor в JSкоторых используется веб-токен ON (JWT), декодирует содержимое маркера, используемого для проверки подлинности клиента или доступа к веб-API сервера в зависимости от места возникновения проблемы. Дополнительные сведения о проверке содержимого ON Web Token (JWT) см. в JSэтом разделе.

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

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

    Чтобы сообщить об ошибках с воспроизведением платформы, которые не связаны с безопасностью и конфиденциальностью, откройте запрос с единицей продукта ASP.NET Core. Не открывайте запрос с единицей продукта, пока вы тщательно не изучите причину проблемы и не попытаетесь решить ее самостоятельно или с помощью сообщества на общедоступном форуме поддержки. Единица продукта не способна устранять неполадки отдельных приложений, которые не работают из-за неправильной конфигурации или вариантов использования с участием сторонних служб. Если отчет является конфиденциальным или конфиденциальным в природе или описывает потенциальный недостаток безопасности в продукте, который злоумышленники могут использовать, см. статью "Отчеты о проблемах безопасности и ошибках" (dotnet/aspnetcoreрепозиторий 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 или обозревателя пакетов FuGet.

Server Запуск приложения

При тестировании и устранении неполадок в размещенном решенииBlazor WebAssembly убедитесь, что вы используете приложение из проекта Server.

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

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

User.razor:

@page "/user"
@attribute [Authorize]
@using System.Text.Json
@using System.Security.Claims
@inject IAccessTokenProvider AuthorizationService

<h1>@AuthenticatedUser?.Identity?.Name</h1>

<h2>Claims</h2>

@foreach (var claim in AuthenticatedUser?.Claims ?? Array.Empty<Claim>())
{
    <p class="claim">@(claim.Type): @claim.Value</p>
}

<h2>Access token</h2>

<p id="access-token">@AccessToken?.Value</p>

<h2>Access token claims</h2>

@foreach (var claim in GetAccessTokenClaims())
{
    <p>@(claim.Key): @claim.Value.ToString()</p>
}

@if (AccessToken != null)
{
    <h2>Access token expires</h2>

    <p>Current time: <span id="current-time">@DateTimeOffset.Now</span></p>
    <p id="access-token-expires">@AccessToken.Expires</p>

    <h2>Access token granted scopes (as reported by the API)</h2>

    @foreach (var scope in AccessToken.GrantedScopes)
    {
        <p>Scope: @scope</p>
    }
}

@code {
    [CascadingParameter]
    private Task<AuthenticationState> AuthenticationState { get; set; }

    public ClaimsPrincipal AuthenticatedUser { get; set; }
    public AccessToken AccessToken { get; set; }

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        var state = await AuthenticationState;
        var accessTokenResult = await AuthorizationService.RequestAccessToken();

        if (!accessTokenResult.TryGetToken(out var token))
        {
            throw new InvalidOperationException(
                "Failed to provision the access token.");
        }

        AccessToken = token;

        AuthenticatedUser = state.User;
    }

    protected IDictionary<string, object> GetAccessTokenClaims()
    {
        if (AccessToken == null)
        {
            return new Dictionary<string, object>();
        }

        // header.payload.signature
        var payload = AccessToken.Value.Split(".")[1];
        var base64Payload = payload.Replace('-', '+').Replace('_', '/')
            .PadRight(payload.Length + (4 - payload.Length % 4) % 4, '=');

        return JsonSerializer.Deserialize<IDictionary<string, object>>(
            Convert.FromBase64String(base64Payload));
    }
}

Проверка содержимого JSON Web Token (JWT)

Чтобы декодировать JSON Web Token (JWT), используйте средство jwt.ms (Майкрософт). Значения в пользовательском интерфейсе остаются в браузере.

Пример закодированного JWT (сокращено для отображения):

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ilg1ZVhrNHh5b2pORnVtMWtsMll0djhkbE5QNC1j ... bQdHBHGcQQRbW7Wmo6SWYG4V_bU55Ug_PW4pLPr20tTS8Ct7_uwy9DWrzCMzpD-EiwT5IjXwlGX3IXVjHIlX50IVIydBoPQtadvT7saKo1G5Jmutgq41o-dmz6-yBMKV2_nXA25Q

Пример JWT, декодированного инструментом для приложения, которое проходит проверку подлинности в Azure AAD B2C:

{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "X5eXk4xyojNFum1kl2Ytv8dlNP4-c57dO6QGTVBwaNk"
}.{
  "exp": 1610059429,
  "nbf": 1610055829,
  "ver": "1.0",
  "iss": "https://mysiteb2c.b2clogin.com/5cc15ea8-a296-4aa3-97e4-226dcc9ad298/v2.0/",
  "sub": "5ee963fb-24d6-4d72-a1b6-889c6e2c7438",
  "aud": "70bde375-fce3-4b82-984a-b247d823a03f",
  "nonce": "b2641f54-8dc4-42ca-97ea-7f12ff4af871",
  "iat": 1610055829,
  "auth_time": 1610055822,
  "idp": "idp.com",
  "tfp": "B2C_1_signupsignin"
}.[Signature]

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