Проверка подлинности и авторизация в ASP.NET Core Blazor Hybrid

Примечание.

Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 8 этой статьи.

Внимание

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

В текущем выпуске см . версию .NET 8 этой статьи.

В этой статье приводятся сведения о поддержке настройки и администрирования функций безопасности и ASP.NET Core Identity в приложениях Blazor Hybrid.

Проверка подлинности в приложениях Blazor Hybrid обрабатывается собственными библиотеками платформы, так как они предлагают более широкие гарантии безопасности, чем песочница браузера. Для проверки подлинности нативных приложений используется механизм, зависящий от ОС, или федеративный протокол, например OpenID Connect (OIDC). Следуйте указаниям для поставщика удостоверений, выбранного для приложения, а затем выполните интеграцию удостоверения с Blazor с помощью инструкций, приведенных в этой статье.

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

  • Использование абстракций в пакете Microsoft.AspNetCore.Components.Authorization, например AuthorizeView.
  • Реакция на изменения в контексте проверки подлинности.
  • Доступ к учетным данным, подготовленным приложением от поставщика удостоверений, например маркерам доступа для выполнения авторизованных вызовов API.

Когда проверка подлинности будет добавлена в приложение .NET MAUI, WPF или Windows Forms и пользователи смогут выполнить вход и выход в системе, интегрируйте проверку подлинности с Blazor, чтобы сделать сведения о пользователе, прошедшем проверку подлинности, доступными для компонентов и служб Razor. Выполните следующие шаги:

  • Ссылаться на пакет Microsoft.AspNetCore.Components.Authorization.

    Примечание.

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

  • Реализуйте абстракцию AuthenticationStateProvider, которую компоненты Razor используют для доступа к сведениям о пользователе, прошедшем проверку подлинности, и для получения обновлений при изменении состояния проверки подлинности.

  • Зарегистрируйте пользовательского поставщика состояний проверки подлинности в контейнере внедрения зависимостей.

Приложения .NET MAUI используют Xamarin.Essentials: Web Authenticator. Класс WebAuthenticator позволяет приложению инициировать потоки проверки подлинности на основе браузера, которые прослушивают обратный вызов по определенному URL-адресу, зарегистрированному за приложением.

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

Приложения Windows Forms используют платформа удостоверений Майкрософт для интеграции с Microsoft Entra (ME-ID) и AAD B2C. Дополнительные сведения см. в статье Обзор библиотеки проверки подлинности Майкрософт (MSAL).

Создание пользовательского AuthenticationStateProvider без обновлений измененных сведений о пользователе

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

Примечание.

Следующий пользовательский AuthenticationStateProvider не объявляет пространство имен, чтобы сделать пример кода применимым к любому приложению Blazor Hybrid. Однако рекомендуется предоставить пространство имен приложения при реализации примера в рабочем приложении.

ExternalAuthStateProvider.cs:

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;

public class ExternalAuthStateProvider : AuthenticationStateProvider
{
    private readonly Task<AuthenticationState> authenticationState;

    public ExternalAuthStateProvider(AuthenticatedUser user) => 
        authenticationState = Task.FromResult(new AuthenticationState(user.Principal));

    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        authenticationState;
}

public class AuthenticatedUser
{
    public ClaimsPrincipal Principal { get; set; } = new();
}

Ниже рассматривается, как:

  • Добавление обязательных пространств имен.
  • Добавление служб авторизации и абстракций Blazor в коллекцию служб.
  • Создание коллекции служб.
  • Разрешение службы AuthenticatedUser для указания субъекта утверждений пользователя, прошедшего проверку подлинности. Дополнительные сведения см. в документации по вашему поставщику удостоверений.
  • Возвращение созданного узла.

В методе MauiProgram.CreateMauiApp из MauiProgram.cs добавьте пространства имен для Microsoft.AspNetCore.Components.Authorization и System.Security.Claims:

using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;

Удалите следующую строку кода, возвращающую созданный Microsoft.Maui.Hosting.MauiApp:

- return builder.Build();

Замените указанную выше строку кода приведенным ниже кодом. Добавьте код OpenID/MSAL для проверки подлинности пользователя. Дополнительные сведения см. в документации по вашему поставщику удостоверений.

builder.Services.AddAuthorizationCore();
builder.Services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
builder.Services.AddSingleton<AuthenticatedUser>();
var host = builder.Build();

var authenticatedUser = host.Services.GetRequiredService<AuthenticatedUser>();

/*
Provide OpenID/MSAL code to authenticate the user. See your identity provider's 
documentation for details.

The user is represented by a new ClaimsPrincipal based on a new ClaimsIdentity.
*/
var user = new ClaimsPrincipal(new ClaimsIdentity());

authenticatedUser.Principal = user;

return host;

Ниже рассматривается, как:

  • Добавление обязательных пространств имен.
  • Добавление служб авторизации и абстракций Blazor в коллекцию служб.
  • Создайте коллекцию служб и добавьте созданную коллекцию служб в качестве ресурса в ResourceDictionary приложения.
  • Разрешение службы AuthenticatedUser для указания субъекта утверждений пользователя, прошедшего проверку подлинности. Дополнительные сведения см. в документации по вашему поставщику удостоверений.
  • Возвращение созданного узла.

В конструкторе MainWindow (MainWindow.xaml.cs) добавьте пространства имен для Microsoft.AspNetCore.Components.Authorization и System.Security.Claims:

using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;

Удалите следующую строку кода, которая добавляет созданную коллекцию служб в качестве ресурса в ResourceDictionary приложения:

- Resources.Add("services", serviceCollection.BuildServiceProvider());

Замените указанную выше строку кода приведенным ниже кодом. Добавьте код OpenID/MSAL для проверки подлинности пользователя. Дополнительные сведения см. в документации по вашему поставщику удостоверений.

serviceCollection.AddAuthorizationCore();
serviceCollection.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
serviceCollection.AddSingleton<AuthenticatedUser>();
var services = serviceCollection.BuildServiceProvider();
Resources.Add("services", services);

var authenticatedUser = services.GetRequiredService<AuthenticatedUser>();

/*
Provide OpenID/MSAL code to authenticate the user. See your identity provider's 
documentation for details.

The user is represented by a new ClaimsPrincipal based on a new ClaimsIdentity.
*/
var user = new ClaimsPrincipal(new ClaimsIdentity());

authenticatedUser.Principal = user;

Ниже рассматривается, как:

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

В конструкторе Form1 (Form1.cs) добавьте пространства имен для Microsoft.AspNetCore.Components.Authorization и System.Security.Claims:

using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;

Удалите следующую строку кода, в которой для созданной коллекции служб задается поставщик службы приложения:

- blazorWebView1.Services = services.BuildServiceProvider();

Замените указанную выше строку кода приведенным ниже кодом. Добавьте код OpenID/MSAL для проверки подлинности пользователя. Дополнительные сведения см. в документации по вашему поставщику удостоверений.

services.AddAuthorizationCore();
services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
services.AddSingleton<AuthenticatedUser>();
var serviceCollection = services.BuildServiceProvider();
blazorWebView1.Services = serviceCollection;

var authenticatedUser = serviceCollection.GetRequiredService<AuthenticatedUser>();

/*
Provide OpenID/MSAL code to authenticate the user. See your identity provider's 
documentation for details.

The user is represented by a new ClaimsPrincipal based on a new ClaimsIdentity.
*/
var user = new ClaimsPrincipal(new ClaimsIdentity());

authenticatedUser.Principal = user;

Создание пользовательского AuthenticationStateProvider с обновлениями измененных сведений о пользователе

Чтобы обновить сведения о пользователя во время выполнения приложения Blazor, вызовите NotifyAuthenticationStateChanged в реализации AuthenticationStateProvider с помощью любого из следующих подходов:

Оповещение об обновлении проверки подлинности извне BlazorWebView (вариант 1)

Пользовательский AuthenticationStateProvider может использовать глобальную службу для оповещения об обновлении проверки подлинности. Рекомендуется, чтобы служба предлагала событие, на которое AuthenticationStateProvider может подписаться, где событие вызывает NotifyAuthenticationStateChanged.

Примечание.

Следующий пользовательский AuthenticationStateProvider не объявляет пространство имен, чтобы сделать пример кода применимым к любому приложению Blazor Hybrid. Однако рекомендуется предоставить пространство имен приложения при реализации примера в рабочем приложении.

ExternalAuthStateProvider.cs:

using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;

public class ExternalAuthStateProvider : AuthenticationStateProvider
{
    private AuthenticationState currentUser;

    public ExternalAuthStateProvider(ExternalAuthService service)
    {
        currentUser = new AuthenticationState(service.CurrentUser);

        service.UserChanged += (newUser) =>
        {
            currentUser = new AuthenticationState(newUser);
            NotifyAuthenticationStateChanged(Task.FromResult(currentUser));
        };
    }

    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        Task.FromResult(currentUser);
}

public class ExternalAuthService
{
    public event Action<ClaimsPrincipal>? UserChanged;
    private ClaimsPrincipal? currentUser;

    public ClaimsPrincipal CurrentUser
    {
        get { return currentUser ?? new(); }
        set
        {
            currentUser = value;

            if (UserChanged is not null)
            {
                UserChanged(currentUser);
            }
        }
    }
}

В методе MauiProgram.CreateMauiApp из MauiProgram.cs добавьте пространство имен для Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Добавьте службы авторизации и абстракции Blazor в коллекцию служб:

builder.Services.AddAuthorizationCore();
builder.Services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
builder.Services.AddSingleton<ExternalAuthService>();

В конструкторе MainWindow (MainWindow.xaml.cs) добавьте пространство имен для Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Добавьте службы авторизации и абстракции Blazor в коллекцию служб:

serviceCollection.AddAuthorizationCore();
serviceCollection.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
serviceCollection.AddSingleton<ExternalAuthService>();

В конструкторе Form1 (Form1.cs) добавьте пространство имен для Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Добавьте службы авторизации и абстракции Blazor в коллекцию служб:

services.AddAuthorizationCore();
services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
services.AddSingleton<ExternalAuthService>();

Везде, где приложение выполняет проверку подлинности пользователя, разрешите службу ExternalAuthService:

var authService = host.Services.GetRequiredService<ExternalAuthService>();

Выполните пользовательский код OpenID/MSAL для проверки подлинности пользователя. Дополнительные сведения см. в документации по вашему поставщику удостоверений. Пользователь, прошедший проверку подлинности (в следующем примере — authenticatedUser), является новым субъектом ClaimsPrincipal, созданным на основе нового ClaimsIdentity.

Укажите текущего пользователя в качестве пользователя, прошедшего проверку подлинности:

authService.CurrentUser = authenticatedUser;

В качестве альтернативы предыдущему подходу можно задать субъект пользователя в System.Threading.Thread.CurrentPrincipal вместо его настройки с помощью службы, чтобы не использовать контейнер внедрения зависимостей:

public class CurrentThreadUserAuthenticationStateProvider : AuthenticationStateProvider
{
    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        Task.FromResult(
            new AuthenticationState(Thread.CurrentPrincipal as ClaimsPrincipal ?? 
                new ClaimsPrincipal(new ClaimsIdentity())));
}

При использовании альтернативного подхода в коллекцию служб добавляются только службы авторизации (AddAuthorizationCore) и CurrentThreadUserAuthenticationStateProvider (.TryAddScoped<AuthenticationStateProvider, CurrentThreadUserAuthenticationStateProvider>()).

Обработка проверки подлинности в пределах BlazorWebView (вариант 2)

Пользовательский AuthenticationStateProvider может включать дополнительные методы для активации входа и выхода в системе и обновления сведений о пользователе.

Примечание.

Следующий пользовательский AuthenticationStateProvider не объявляет пространство имен, чтобы сделать пример кода применимым к любому приложению Blazor Hybrid. Однако рекомендуется предоставить пространство имен приложения при реализации примера в рабочем приложении.

ExternalAuthStateProvider.cs:

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;

public class ExternalAuthStateProvider : AuthenticationStateProvider
{
    private ClaimsPrincipal currentUser = new ClaimsPrincipal(new ClaimsIdentity());

    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        Task.FromResult(new AuthenticationState(currentUser));

    public Task LogInAsync()
    {
        var loginTask = LogInAsyncCore();
        NotifyAuthenticationStateChanged(loginTask);

        return loginTask;

        async Task<AuthenticationState> LogInAsyncCore()
        {
            var user = await LoginWithExternalProviderAsync();
            currentUser = user;

            return new AuthenticationState(currentUser);
        }
    }

    private Task<ClaimsPrincipal> LoginWithExternalProviderAsync()
    {
        /*
            Provide OpenID/MSAL code to authenticate the user. See your identity 
            provider's documentation for details.

            Return a new ClaimsPrincipal based on a new ClaimsIdentity.
        */
        var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity());

        return Task.FromResult(authenticatedUser);
    }

    public void Logout()
    {
        currentUser = new ClaimsPrincipal(new ClaimsIdentity());
        NotifyAuthenticationStateChanged(
            Task.FromResult(new AuthenticationState(currentUser)));
    }
}

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

  • Вызов LogInAsyncCore активирует процесс входа.
  • Вызов NotifyAuthenticationStateChanged уведомляет о том, что выполняется обновление. Это позволяет приложению предоставить временный пользовательский интерфейс в процессе входа или выхода.
  • При возвращении loginTask возвращается задача, чтобы компонент, активировавший вход, дождался завершения задачи и отреагировал на нее соответственно.
  • Разработчик реализует метод LoginWithExternalProviderAsync для входа пользователя с применением пакета SDK поставщика удостоверений. Дополнительные сведения см. в документации поставщика удостоверений. Прошедший проверку подлинности пользователь (authenticatedUser) является новым ClaimsPrincipal на основе нового ClaimsIdentity.

В методе MauiProgram.CreateMauiApp из MauiProgram.cs добавьте службы авторизации и абстракцию Blazor в коллекцию служб:

builder.Services.AddAuthorizationCore();
builder.Services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

В конструкторе MainWindow (MainWindow.xaml.cs) добавьте службы авторизации и абстракцию Blazor в коллекцию служб:

serviceCollection.AddAuthorizationCore();
serviceCollection.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

В конструкторе Form1 (Form1.cs) добавьте службы авторизации и абстракцию Blazor в коллекцию служб:

services.AddAuthorizationCore();
services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

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

Shared/LoginComponent.razor:

@inject AuthenticationStateProvider AuthenticationStateProvider

<button @onclick="Login">Log in</button>

@code
{
    public async Task Login()
    {
        await ((ExternalAuthStateProvider)AuthenticationStateProvider)
            .LogInAsync();
    }
}

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

Shared/LogoutComponent.razor:

@inject AuthenticationStateProvider AuthenticationStateProvider

<button @onclick="Logout">Log out</button>

@code
{
    public async Task Logout()
    {
        await ((ExternalAuthStateProvider)AuthenticationStateProvider)
            .Logout();
    }
}

Доступ к другим сведениям о проверке подлинности

Blazor не определяет абстракцию для работы с другими учетными данными, такими как маркеры доступа, используемые для HTTP-запросов к веб-API. Мы рекомендуем следовать указаниям поставщика удостоверений для управления учетными данными пользователя с помощью примитивов, которые предоставляет пакет SDK поставщика удостоверений.

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

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

Другие рекомендации по безопасности проверки подлинности

Процесс проверки подлинности является внешним по отношению к Blazor. Мы рекомендуем разработчикам обратиться к руководству для поставщика удостоверений, чтобы получить дополнительные рекомендации по обеспечению безопасности.

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

  • Не следует выполнять проверку подлинности в контексте Web View. Например, не используйте библиотеку OAuth JavaScript для выполнения потока проверки подлинности. В одностраничном приложении маркеры проверки подлинности не скрыты в JavaScript, и злоумышленники могут легко их обнаружить и использовать в злонамеренных целях. Нативные приложения не подвержены такому риску, так как они могут получать маркеры только за пределами контекста браузера. Это означает, что злонамеренным сторонним скриптам не удастся украсть маркеры и скомпрометировать приложение.
  • Не следует самостоятельно реализовывать рабочий процесс проверки подлинности. В большинстве случаев библиотеки платформы безопасно обрабатывают рабочий процесс проверки подлинности, используя браузер системы вместо пользовательского Web View, который можно взломать.
  • Не используйте элемент управления Web View платформы для выполнения проверки подлинности. Вместо этого по возможности используйте браузер системы.
  • Не следует передавать маркеры в контекст документа (JavaScript). В некоторых ситуациях для выполнения авторизованного вызова внешней службы требуется библиотека JavaScript в документе. Вместо того чтобы сделать маркер доступным для JavaScript с помощью JS взаимодействия:
    • предоставьте созданный временный маркер библиотеке и в Web View;
    • перехватите исходящий сетевой запрос в коде;
    • замените временный маркер реальным маркером и убедитесь, что назначение для запроса является допустимым.

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

Проверка подлинности в приложениях Blazor Hybrid обрабатывается собственными библиотеками платформы, так как они предлагают более широкие гарантии безопасности, чем песочница браузера. Для проверки подлинности нативных приложений используется механизм, зависящий от ОС, или федеративный протокол, например OpenID Connect (OIDC). Следуйте указаниям для поставщика удостоверений, выбранного для приложения, а затем выполните интеграцию удостоверения с Blazor с помощью инструкций, приведенных в этой статье.

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

  • Использование абстракций в пакете Microsoft.AspNetCore.Components.Authorization, например AuthorizeView.
  • Реакция на изменения в контексте проверки подлинности.
  • Доступ к учетным данным, подготовленным приложением от поставщика удостоверений, например маркерам доступа для выполнения авторизованных вызовов API.

Когда проверка подлинности будет добавлена в приложение .NET MAUI, WPF или Windows Forms и пользователи смогут выполнить вход и выход в системе, интегрируйте проверку подлинности с Blazor, чтобы сделать сведения о пользователе, прошедшем проверку подлинности, доступными для компонентов и служб Razor. Выполните следующие шаги:

  • Ссылаться на пакет Microsoft.AspNetCore.Components.Authorization.

    Примечание.

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

  • Реализуйте абстракцию AuthenticationStateProvider, которую компоненты Razor используют для доступа к сведениям о пользователе, прошедшем проверку подлинности, и для получения обновлений при изменении состояния проверки подлинности.

  • Зарегистрируйте пользовательского поставщика состояний проверки подлинности в контейнере внедрения зависимостей.

Приложения .NET MAUI используют Xamarin.Essentials: Web Authenticator. Класс WebAuthenticator позволяет приложению инициировать потоки проверки подлинности на основе браузера, которые прослушивают обратный вызов по определенному URL-адресу, зарегистрированному за приложением.

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

Приложения Windows Forms используют платформа удостоверений Майкрософт для интеграции с Microsoft Entra (ME-ID) и AAD B2C. Дополнительные сведения см. в статье Обзор библиотеки проверки подлинности Майкрософт (MSAL).

Создание пользовательского AuthenticationStateProvider без обновлений измененных сведений о пользователе

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

Примечание.

Следующий пользовательский AuthenticationStateProvider не объявляет пространство имен, чтобы сделать пример кода применимым к любому приложению Blazor Hybrid. Однако рекомендуется предоставить пространство имен приложения при реализации примера в рабочем приложении.

ExternalAuthStateProvider.cs:

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;

public class ExternalAuthStateProvider : AuthenticationStateProvider
{
    private readonly Task<AuthenticationState> authenticationState;

    public ExternalAuthStateProvider(AuthenticatedUser user) => 
        authenticationState = Task.FromResult(new AuthenticationState(user.Principal));

    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        authenticationState;
}

public class AuthenticatedUser
{
    public ClaimsPrincipal Principal { get; set; } = new();
}

Ниже рассматривается, как:

  • Добавление обязательных пространств имен.
  • Добавление служб авторизации и абстракций Blazor в коллекцию служб.
  • Создание коллекции служб.
  • Разрешение службы AuthenticatedUser для указания субъекта утверждений пользователя, прошедшего проверку подлинности. Дополнительные сведения см. в документации по вашему поставщику удостоверений.
  • Возвращение созданного узла.

В методе MauiProgram.CreateMauiApp из MauiProgram.cs добавьте пространства имен для Microsoft.AspNetCore.Components.Authorization и System.Security.Claims:

using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;

Удалите следующую строку кода, возвращающую созданный Microsoft.Maui.Hosting.MauiApp:

- return builder.Build();

Замените указанную выше строку кода приведенным ниже кодом. Добавьте код OpenID/MSAL для проверки подлинности пользователя. Дополнительные сведения см. в документации по вашему поставщику удостоверений.

builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
builder.Services.AddSingleton<AuthenticatedUser>();
var host = builder.Build();

var authenticatedUser = host.Services.GetRequiredService<AuthenticatedUser>();

/*
Provide OpenID/MSAL code to authenticate the user. See your identity provider's 
documentation for details.

The user is represented by a new ClaimsPrincipal based on a new ClaimsIdentity.
*/
var user = new ClaimsPrincipal(new ClaimsIdentity());

authenticatedUser.Principal = user;

return host;

Ниже рассматривается, как:

  • Добавление обязательных пространств имен.
  • Добавление служб авторизации и абстракций Blazor в коллекцию служб.
  • Создайте коллекцию служб и добавьте созданную коллекцию служб в качестве ресурса в ResourceDictionary приложения.
  • Разрешение службы AuthenticatedUser для указания субъекта утверждений пользователя, прошедшего проверку подлинности. Дополнительные сведения см. в документации по вашему поставщику удостоверений.
  • Возвращение созданного узла.

В конструкторе MainWindow (MainWindow.xaml.cs) добавьте пространства имен для Microsoft.AspNetCore.Components.Authorization и System.Security.Claims:

using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;

Удалите следующую строку кода, которая добавляет созданную коллекцию служб в качестве ресурса в ResourceDictionary приложения:

- Resources.Add("services", serviceCollection.BuildServiceProvider());

Замените указанную выше строку кода приведенным ниже кодом. Добавьте код OpenID/MSAL для проверки подлинности пользователя. Дополнительные сведения см. в документации по вашему поставщику удостоверений.

serviceCollection.AddAuthorizationCore();
serviceCollection.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
serviceCollection.AddSingleton<AuthenticatedUser>();
var services = serviceCollection.BuildServiceProvider();
Resources.Add("services", services);

var authenticatedUser = services.GetRequiredService<AuthenticatedUser>();

/*
Provide OpenID/MSAL code to authenticate the user. See your identity provider's 
documentation for details.

The user is represented by a new ClaimsPrincipal based on a new ClaimsIdentity.
*/
var user = new ClaimsPrincipal(new ClaimsIdentity());

authenticatedUser.Principal = user;

Ниже рассматривается, как:

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

В конструкторе Form1 (Form1.cs) добавьте пространства имен для Microsoft.AspNetCore.Components.Authorization и System.Security.Claims:

using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;

Удалите следующую строку кода, в которой для созданной коллекции служб задается поставщик службы приложения:

- blazorWebView1.Services = services.BuildServiceProvider();

Замените указанную выше строку кода приведенным ниже кодом. Добавьте код OpenID/MSAL для проверки подлинности пользователя. Дополнительные сведения см. в документации по вашему поставщику удостоверений.

services.AddAuthorizationCore();
services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
services.AddSingleton<AuthenticatedUser>();
var serviceCollection = services.BuildServiceProvider();
blazorWebView1.Services = serviceCollection;

var authenticatedUser = serviceCollection.GetRequiredService<AuthenticatedUser>();

/*
Provide OpenID/MSAL code to authenticate the user. See your identity provider's 
documentation for details.

The user is represented by a new ClaimsPrincipal based on a new ClaimsIdentity.
*/
var user = new ClaimsPrincipal(new ClaimsIdentity());

authenticatedUser.Principal = user;

Создание пользовательского AuthenticationStateProvider с обновлениями измененных сведений о пользователе

Чтобы обновить сведения о пользователя во время выполнения приложения Blazor, вызовите NotifyAuthenticationStateChanged в реализации AuthenticationStateProvider с помощью любого из следующих подходов:

Оповещение об обновлении проверки подлинности извне BlazorWebView (вариант 1)

Пользовательский AuthenticationStateProvider может использовать глобальную службу для оповещения об обновлении проверки подлинности. Рекомендуется, чтобы служба предлагала событие, на которое AuthenticationStateProvider может подписаться, где событие вызывает NotifyAuthenticationStateChanged.

Примечание.

Следующий пользовательский AuthenticationStateProvider не объявляет пространство имен, чтобы сделать пример кода применимым к любому приложению Blazor Hybrid. Однако рекомендуется предоставить пространство имен приложения при реализации примера в рабочем приложении.

ExternalAuthStateProvider.cs:

using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;

public class ExternalAuthStateProvider : AuthenticationStateProvider
{
    private AuthenticationState currentUser;

    public ExternalAuthStateProvider(ExternalAuthService service)
    {
        currentUser = new AuthenticationState(service.CurrentUser);

        service.UserChanged += (newUser) =>
        {
            currentUser = new AuthenticationState(newUser);
            NotifyAuthenticationStateChanged(Task.FromResult(currentUser));
        };
    }

    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        Task.FromResult(currentUser);
}

public class ExternalAuthService
{
    public event Action<ClaimsPrincipal>? UserChanged;
    private ClaimsPrincipal? currentUser;

    public ClaimsPrincipal CurrentUser
    {
        get { return currentUser ?? new(); }
        set
        {
            currentUser = value;

            if (UserChanged is not null)
            {
                UserChanged(currentUser);
            }
        }
    }
}

В методе MauiProgram.CreateMauiApp из MauiProgram.cs добавьте пространство имен для Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Добавьте службы авторизации и абстракции Blazor в коллекцию служб:

builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
builder.Services.AddSingleton<ExternalAuthService>();

В конструкторе MainWindow (MainWindow.xaml.cs) добавьте пространство имен для Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Добавьте службы авторизации и абстракции Blazor в коллекцию служб:

serviceCollection.AddAuthorizationCore();
serviceCollection.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
serviceCollection.AddSingleton<ExternalAuthService>();

В конструкторе Form1 (Form1.cs) добавьте пространство имен для Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Добавьте службы авторизации и абстракции Blazor в коллекцию служб:

services.AddAuthorizationCore();
services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
services.AddSingleton<ExternalAuthService>();

Везде, где приложение выполняет проверку подлинности пользователя, разрешите службу ExternalAuthService:

var authService = host.Services.GetRequiredService<ExternalAuthService>();

Выполните пользовательский код OpenID/MSAL для проверки подлинности пользователя. Дополнительные сведения см. в документации по вашему поставщику удостоверений. Пользователь, прошедший проверку подлинности (в следующем примере — authenticatedUser), является новым субъектом ClaimsPrincipal, созданным на основе нового ClaimsIdentity.

Укажите текущего пользователя в качестве пользователя, прошедшего проверку подлинности:

authService.CurrentUser = authenticatedUser;

В качестве альтернативы предыдущему подходу можно задать субъект пользователя в System.Threading.Thread.CurrentPrincipal вместо его настройки с помощью службы, чтобы не использовать контейнер внедрения зависимостей:

public class CurrentThreadUserAuthenticationStateProvider : AuthenticationStateProvider
{
    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        Task.FromResult(
            new AuthenticationState(Thread.CurrentPrincipal as ClaimsPrincipal ?? 
                new ClaimsPrincipal(new ClaimsIdentity())));
}

При использовании альтернативного подхода в коллекцию служб добавляются только службы авторизации (AddAuthorizationCore) и CurrentThreadUserAuthenticationStateProvider (.AddScoped<AuthenticationStateProvider, CurrentThreadUserAuthenticationStateProvider>()).

Обработка проверки подлинности в пределах BlazorWebView (вариант 2)

Пользовательский AuthenticationStateProvider может включать дополнительные методы для активации входа и выхода в системе и обновления сведений о пользователе.

Примечание.

Следующий пользовательский AuthenticationStateProvider не объявляет пространство имен, чтобы сделать пример кода применимым к любому приложению Blazor Hybrid. Однако рекомендуется предоставить пространство имен приложения при реализации примера в рабочем приложении.

ExternalAuthStateProvider.cs:

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;

public class ExternalAuthStateProvider : AuthenticationStateProvider
{
    private ClaimsPrincipal currentUser = new ClaimsPrincipal(new ClaimsIdentity());

    public override Task<AuthenticationState> GetAuthenticationStateAsync() =>
        Task.FromResult(new AuthenticationState(currentUser));

    public Task LogInAsync()
    {
        var loginTask = LogInAsyncCore();
        NotifyAuthenticationStateChanged(loginTask);

        return loginTask;

        async Task<AuthenticationState> LogInAsyncCore()
        {
            var user = await LoginWithExternalProviderAsync();
            currentUser = user;

            return new AuthenticationState(currentUser);
        }
    }

    private Task<ClaimsPrincipal> LoginWithExternalProviderAsync()
    {
        /*
            Provide OpenID/MSAL code to authenticate the user. See your identity 
            provider's documentation for details.

            Return a new ClaimsPrincipal based on a new ClaimsIdentity.
        */
        var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity());

        return Task.FromResult(authenticatedUser);
    }

    public void Logout()
    {
        currentUser = new ClaimsPrincipal(new ClaimsIdentity());
        NotifyAuthenticationStateChanged(
            Task.FromResult(new AuthenticationState(currentUser)));
    }
}

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

  • Вызов LogInAsyncCore активирует процесс входа.
  • Вызов NotifyAuthenticationStateChanged уведомляет о том, что выполняется обновление. Это позволяет приложению предоставить временный пользовательский интерфейс в процессе входа или выхода.
  • При возвращении loginTask возвращается задача, чтобы компонент, активировавший вход, дождался завершения задачи и отреагировал на нее соответственно.
  • Разработчик реализует метод LoginWithExternalProviderAsync для входа пользователя с применением пакета SDK поставщика удостоверений. Дополнительные сведения см. в документации поставщика удостоверений. Прошедший проверку подлинности пользователь (authenticatedUser) является новым ClaimsPrincipal на основе нового ClaimsIdentity.

В методе MauiProgram.CreateMauiApp из MauiProgram.cs добавьте службы авторизации и абстракцию Blazor в коллекцию служб:

builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

В конструкторе MainWindow (MainWindow.xaml.cs) добавьте службы авторизации и абстракцию Blazor в коллекцию служб:

serviceCollection.AddAuthorizationCore();
serviceCollection.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

В конструкторе Form1 (Form1.cs) добавьте службы авторизации и абстракцию Blazor в коллекцию служб:

services.AddAuthorizationCore();
services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

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

Shared/LoginComponent.razor:

@inject AuthenticationStateProvider AuthenticationStateProvider

<button @onclick="Login">Log in</button>

@code
{
    public async Task Login()
    {
        await ((ExternalAuthStateProvider)AuthenticationStateProvider)
            .LogInAsync();
    }
}

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

Shared/LogoutComponent.razor:

@inject AuthenticationStateProvider AuthenticationStateProvider

<button @onclick="Logout">Log out</button>

@code
{
    public async Task Logout()
    {
        await ((ExternalAuthStateProvider)AuthenticationStateProvider)
            .Logout();
    }
}

Доступ к другим сведениям о проверке подлинности

Blazor не определяет абстракцию для работы с другими учетными данными, такими как маркеры доступа, используемые для HTTP-запросов к веб-API. Мы рекомендуем следовать указаниям поставщика удостоверений для управления учетными данными пользователя с помощью примитивов, которые предоставляет пакет SDK поставщика удостоверений.

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

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

Другие рекомендации по безопасности проверки подлинности

Процесс проверки подлинности является внешним по отношению к Blazor. Мы рекомендуем разработчикам обратиться к руководству для поставщика удостоверений, чтобы получить дополнительные рекомендации по обеспечению безопасности.

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

  • Не следует выполнять проверку подлинности в контексте Web View. Например, не используйте библиотеку OAuth JavaScript для выполнения потока проверки подлинности. В одностраничном приложении маркеры проверки подлинности не скрыты в JavaScript, и злоумышленники могут легко их обнаружить и использовать в злонамеренных целях. Нативные приложения не подвержены такому риску, так как они могут получать маркеры только за пределами контекста браузера. Это означает, что злонамеренным сторонним скриптам не удастся украсть маркеры и скомпрометировать приложение.
  • Не следует самостоятельно реализовывать рабочий процесс проверки подлинности. В большинстве случаев библиотеки платформы безопасно обрабатывают рабочий процесс проверки подлинности, используя браузер системы вместо пользовательского Web View, который можно взломать.
  • Не используйте элемент управления Web View платформы для выполнения проверки подлинности. Вместо этого по возможности используйте браузер системы.
  • Не следует передавать маркеры в контекст документа (JavaScript). В некоторых ситуациях для выполнения авторизованного вызова внешней службы требуется библиотека JavaScript в документе. Вместо того чтобы сделать маркер доступным для JavaScript с помощью JS взаимодействия:
    • предоставьте созданный временный маркер библиотеке и в Web View;
    • перехватите исходящий сетевой запрос в коде;
    • замените временный маркер реальным маркером и убедитесь, что назначение для запроса является допустимым.

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