Udostępnij za pośrednictwem


Uwierzytelnianie i autoryzacja na platformie ASP.NET Core Blazor Hybrid

Uwaga

Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.

Ostrzeżenie

Ta wersja ASP.NET Core nie jest już obsługiwana. Aby uzyskać więcej informacji, zobacz .NET i .NET Core Support Policy (Zasady obsługi platformy .NET Core). Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.

Ważne

Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.

Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.

W tym artykule opisano dostępną na platformie ASP.NET Core obsługę konfiguracji i zarządzania zabezpieczeniami i rozwiązaniem ASP.NET Core Identity w aplikacjach Blazor Hybrid.

Uwierzytelnianie w aplikacjach Blazor Hybrid jest obsługiwane przez biblioteki platformy natywnej, ponieważ udostępniają one rozszerzone gwarancje zabezpieczeń, których nie może zapewnić piaskownica przeglądarki. Przy uwierzytelnianiu aplikacji natywnych używa się mechanizmu specyficznego dla systemu operacyjnego lub jest ono realizowane za pośrednictwem protokołu federacyjnego, takiego jak OpenID Connect (OIDC). Postępuj zgodnie ze wskazówkami dotyczącymi dostawcy tożsamości wybranego dla aplikacji, a następnie dodatkowo zintegruj tożsamość z platformą Blazor na podstawie wskazówek zawartych w tym artykule.

Integracja uwierzytelniania musi zapewnić osiągnięcie następujących celów dla usług i składników Razor:

  • Stosowanie abstrakcji w pakiecie Microsoft.AspNetCore.Components.Authorization, takich jak AuthorizeView.
  • Reagowanie na zmiany w kontekście uwierzytelniania.
  • Uzyskiwanie dostępu do poświadczeń aprowizowanych przez aplikację z poziomu dostawcy tożsamości, takich jak tokeny dostępu do wykonywania autoryzowanych wywołań interfejsu API.

Po dodaniu uwierzytelniania do aplikacji .NET MAUI, WPF lub Windows Forms i upewnieniu się, że użytkownicy mogą się zalogować i wylogować, zintegruj uwierzytelnianie z platformą Blazor w celu udostępnienia uwierzytelnionego użytkownika usługom i składnikom Razor. Wykonaj następujące kroki:

Aplikacje .NET MAUI używają składnika Xamarin.Essentials: Web Authenticator: klasa WebAuthenticator umożliwia aplikacji inicjowanie przepływów uwierzytelniania opartych na przeglądarce, które nasłuchują wywołania zwrotnego do określonego adresu URL zarejestrowanego w aplikacji.

Aby uzyskać dodatkowe wskazówki, zobacz następujące zasoby:

Aplikacje Windows Forms używają Platforma tożsamości Microsoft do integracji z firmą Microsoft Entra (ME-ID) i usługą AAD B2C. Aby uzyskać więcej informacji, zobacz Omówienie biblioteki Microsoft Authentication Library (MSAL).

Tworzenie niestandardowego dostawcy AuthenticationStateProvider bez aktualizacji zmian użytkownika

Jeśli aplikacja uwierzytelnia użytkownika bezpośrednio po jej uruchomieniu, a uwierzytelniony użytkownik pozostaje taki sam w całym okresie jej istnienia, powiadomienia o zmianie użytkownika nie są wymagane, a aplikacja udostępnia tylko informacje o uwierzytelnionym użytkowniku. W tym scenariuszu użytkownik loguje się do aplikacji po jej otwarciu, a ekran logowania jest ponownie wyświetlany po wylogowaniu użytkownika. Poniższy dostawca ExternalAuthStateProvider to przykład implementacji niestandardowego dostawcy AuthenticationStateProvider w tym scenariuszu uwierzytelniania.

Uwaga

Poniższy niestandardowy dostawca AuthenticationStateProvider nie zawiera deklaracji przestrzeni nazw, dzięki czemu ten przykład kodu może dotyczyć dowolnej aplikacji Blazor Hybrid. Jednak w przypadku implementowania tego przykładu w aplikacji produkcyjnej najlepszym rozwiązaniem jest podanie przestrzeni nazw aplikacji.

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

W poniższych krokach opisano, jak to zrobić:

  • Dodaj wymagane przestrzenie nazw.
  • Dodaj usługi autoryzacji i abstrakcje Blazor do kolekcji usług.
  • Skompiluj kolekcję usług.
  • Rozwiąż problem z usługą AuthenticatedUser, aby ustawić podmiot zabezpieczeń oświadczeń uwierzytelnianego użytkownika. Aby uzyskać szczegółowe informacje, zobacz dokumentację dostawcy tożsamości.
  • Zwróć skompilowanego hosta.

W metodzie MauiProgram.CreateMauiApp pliku MauiProgram.cs dodaj przestrzenie nazw dla Microsoft.AspNetCore.Components.Authorization i System.Security.Claims:

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

Usuń następujący wiersz kodu, który zwraca skompilowaną aplikację Microsoft.Maui.Hosting.MauiApp:

- return builder.Build();

Zastąp poprzedni wiersz kodu następującym kodem. Dodaj kod OpenID/MSAL, aby uwierzytelnić użytkownika. Aby uzyskać szczegółowe informacje, zobacz dokumentację dostawcy tożsamości.

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;

W poniższych krokach opisano, jak to zrobić:

  • Dodaj wymagane przestrzenie nazw.
  • Dodaj usługi autoryzacji i abstrakcje Blazor do kolekcji usług.
  • Skompiluj kolekcję usług i dodaj tę skompilowaną kolekcję usług jako zasób do słownika ResourceDictionary aplikacji.
  • Rozwiąż problem z usługą AuthenticatedUser, aby ustawić podmiot zabezpieczeń oświadczeń uwierzytelnianego użytkownika. Aby uzyskać szczegółowe informacje, zobacz dokumentację dostawcy tożsamości.
  • Zwróć skompilowanego hosta.

W konstruktorze MainWindow(MainWindow.xaml.cs) dodaj przestrzenie nazw dla Microsoft.AspNetCore.Components.Authorization i System.Security.Claims:

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

Usuń następujący wiersz kodu, który dodaje skompilowaną kolekcję usług jako zasób do słownika ResourceDictionary aplikacji:

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

Zastąp poprzedni wiersz kodu następującym kodem. Dodaj kod OpenID/MSAL, aby uwierzytelnić użytkownika. Aby uzyskać szczegółowe informacje, zobacz dokumentację dostawcy tożsamości.

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;

W poniższych krokach opisano, jak to zrobić:

  • Dodaj wymagane przestrzenie nazw.
  • Dodaj usługi autoryzacji i abstrakcje Blazor do kolekcji usług.
  • Skompiluj kolekcję usług i dodaj tę skompilowaną kolekcję usług do dostawcy usług aplikacji.
  • Rozwiąż problem z usługą AuthenticatedUser, aby ustawić podmiot zabezpieczeń oświadczeń uwierzytelnianego użytkownika. Aby uzyskać szczegółowe informacje, zobacz dokumentację dostawcy tożsamości.

W konstruktorze Form1(Form1.cs) dodaj przestrzenie nazw dla Microsoft.AspNetCore.Components.Authorization i System.Security.Claims:

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

Usuń następujący wiersz kodu, który ustawia skompilowaną kolekcję usług jako dostawcę usług aplikacji:

- blazorWebView1.Services = services.BuildServiceProvider();

Zastąp poprzedni wiersz kodu następującym kodem. Dodaj kod OpenID/MSAL, aby uwierzytelnić użytkownika. Aby uzyskać szczegółowe informacje, zobacz dokumentację dostawcy tożsamości.

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;

Tworzenie niestandardowego dostawcy AuthenticationStateProvider z aktualizacjami zmian użytkownika

Aby zaktualizować użytkownika w trakcie działania aplikacji Blazor, wywołaj metodę NotifyAuthenticationStateChanged w ramach implementacji dostawcy AuthenticationStateProvider przy użyciu jednej z następujących metod:

Sygnalizowanie aktualizacji uwierzytelniania spoza widoku BlazorWebView (opcja 1)

Niestandardowy dostawca AuthenticationStateProvider może używać usługi globalnej do sygnalizowania aktualizacji uwierzytelniania. Zalecamy, aby usługa udostępniała zdarzenie, które dostawca AuthenticationStateProvider może zasubskrybować, a zdarzenie to wywoływało metodę NotifyAuthenticationStateChanged.

Uwaga

Poniższy niestandardowy dostawca AuthenticationStateProvider nie zawiera deklaracji przestrzeni nazw, dzięki czemu ten przykład kodu może dotyczyć dowolnej aplikacji Blazor Hybrid. Jednak w przypadku implementowania tego przykładu w aplikacji produkcyjnej najlepszym rozwiązaniem jest podanie przestrzeni nazw aplikacji.

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

W metodzie MauiProgram.CreateMauiApp pliku MauiProgram.cs dodaj przestrzeń nazw dla Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Dodaj usługi autoryzacji i abstrakcje Blazor do kolekcji usług:

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

W konstruktorze MainWindow(MainWindow.xaml.cs) dodaj przestrzeń nazw dla Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Dodaj usługi autoryzacji i abstrakcje Blazor do kolekcji usług:

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

W konstruktorze Form1(Form1.cs) dodaj przestrzeń nazw dla Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Dodaj usługi autoryzacji i abstrakcje Blazor do kolekcji usług:

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

Wszędzie tam, gdzie aplikacja uwierzytelnia użytkownika, zapewnij rozstrzygnięcie usługi ExternalAuthService:

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

Wykonaj niestandardowy kod OpenID/MSAL, aby uwierzytelnić użytkownika. Aby uzyskać szczegółowe informacje, zobacz dokumentację dostawcy tożsamości. Uwierzytelniony użytkownik (w poniższym przykładzie: authenticatedUser) jest nowym podmiotem ClaimsPrincipal opartym na nowej tożsamości ClaimsIdentity.

Ustaw uwierzytelnionego użytkownika jako bieżącego użytkownika:

authService.CurrentUser = authenticatedUser;

Alternatywą dla powyższego podejścia jest ustawienie podmiotu zabezpieczeń użytkownika w obszarze System.Threading.Thread.CurrentPrincipal zamiast ustawiania go za pośrednictwem usługi, co pozwala uniknąć użycia kontenera wstrzykiwania zależności:

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

W przypadku tego alternatywnego podejścia do kolekcji usług dodawane są tylko usługi autoryzacji (AddAuthorizationCore) i dostawca CurrentThreadUserAuthenticationStateProvider (.TryAddScoped<AuthenticationStateProvider, CurrentThreadUserAuthenticationStateProvider>()).

Obsługa uwierzytelniania w obrębie widoku BlazorWebView (opcja 2)

Niestandardowy dostawca AuthenticationStateProvider może zawierać dodatkowe metody wyzwalania logowania i wylogowywania oraz aktualizowania użytkownika.

Uwaga

Poniższy niestandardowy dostawca AuthenticationStateProvider nie zawiera deklaracji przestrzeni nazw, dzięki czemu ten przykład kodu może dotyczyć dowolnej aplikacji Blazor Hybrid. Jednak w przypadku implementowania tego przykładu w aplikacji produkcyjnej najlepszym rozwiązaniem jest podanie przestrzeni nazw aplikacji.

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

W powyższym przykładzie:

  • Wywołanie LogInAsyncCore wyzwala proces logowania.
  • Wywołanie metody NotifyAuthenticationStateChanged powiadamia o tym, że w toku jest aktualizacja, co umożliwia aplikacji udostępnienie tymczasowego interfejsu użytkownika podczas procesu logowania lub wylogowania.
  • Zwracany element loginTask zwraca zadanie, dzięki czemu składnik, który wyzwolił logowanie, może czekać i zareagować po zakończeniu tego zadania.
  • Deweloper implementuje metodę LoginWithExternalProviderAsync na potrzeby zalogowania użytkownika przy użyciu zestawu SDK dostawcy tożsamości. Aby uzyskać więcej informacji, zobacz dokumentację dostawcy tożsamości. Uwierzytelniony użytkownik (authenticatedUser) jest nowym podmiotem ClaimsPrincipal opartym na nowej tożsamości ClaimsIdentity.

W metodzie MauiProgram.CreateMauiApp pliku MauiProgram.cs dodaj usługi autoryzacji i abstrakcje Blazor do kolekcji usług:

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

W konstruktorze MainWindow (MainWindow.xaml.cs) dodaj usługi autoryzacji i abstrakcje Blazor do kolekcji usług:

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

W konstruktorze Form1 (Form1.cs) dodaj usługi autoryzacji i abstrakcje Blazor do kolekcji usług:

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

Na podstawie poniższego składnika LoginComponent pokazano, jak zalogować użytkownika. W typowej aplikacji składnik LoginComponent jest wyświetlany w składniku nadrzędnym tylko w przypadku, gdy użytkownik nie jest zalogowany do aplikacji.

Shared/LoginComponent.razor:

@inject AuthenticationStateProvider AuthenticationStateProvider

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

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

Na podstawie poniższego składnika LogoutComponent pokazano, jak wylogować użytkownika. W typowej aplikacji składnik LogoutComponent jest wyświetlany w składniku nadrzędnym tylko w przypadku, gdy użytkownik jest zalogowany do aplikacji.

Shared/LogoutComponent.razor:

@inject AuthenticationStateProvider AuthenticationStateProvider

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

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

Uzyskiwanie dostępu do innych informacji uwierzytelniania

Platforma Blazor nie definiuje abstrakcji do obsługi innych poświadczeń, takich jak tokeny dostępu stosowane przy żądaniach HTTP do internetowych interfejsów API. Zalecamy przestrzeganie wskazówek dotyczących dostawcy tożsamości na potrzeby zarządzania poświadczeniami użytkownika przy użyciu elementów pierwotnych, które udostępnia zestaw SDK dostawcy tożsamości.

Zestawy SDK dostawców tożsamości często używają magazynu tokenów do obsługi poświadczeń użytkowników przechowywanych na urządzeniu. Jeśli do kontenera usługi zostanie dodany element pierwotny magazynu tokenów zestawu SDK, należy użyć tego elementu pierwotnego zestawu SDK w aplikacji.

Platforma Blazor nie ma informacji o poświadczeniach uwierzytelniania użytkowników i w żaden sposób nie wchodzi w interakcje z poświadczeniami, więc można swobodnie wybrać dowolne podejście do zastosowania w kodzie aplikacji, najlepiej te uważane za najwygodniejsze. Jednak podczas implementowania kodu uwierzytelniania w aplikacji postępuj zgodnie z ogólnymi wskazówkami dotyczącymi zabezpieczeń podanymi w następnej sekcji: Inne zagadnienia dotyczące zabezpieczeń uwierzytelniania.

Inne zagadnienia dotyczące zabezpieczeń uwierzytelniania

Proces uwierzytelniania ma wobec platformy Blazorcharakter zewnętrzny i w celu uzyskania dodatkowych wskazówek dotyczących zabezpieczeń zalecamy deweloperom korzystanie ze wskazówek dotyczących dostawcy tożsamości.

Podczas implementowania uwierzytelniania:

  • Unikaj uwierzytelniania w kontekście widoku Web View. Na przykład unikaj przeprowadzania przepływu uwierzytelniania za pomocą biblioteki OAuth języka JavaScript. W aplikacji jednostronicowej tokeny uwierzytelniania nie są ukryte w języku JavaScript, więc złośliwi użytkownicy mogą je łatwo wykrywać i wykorzystywać je do nieuczciwych celów. To ryzyko nie dotyczy aplikacji natywnych, ponieważ takie aplikacje mogą uzyskiwać tokeny tylko poza kontekstem przeglądarki, co oznacza, że nieautoryzowane skrypty zewnętrzne nie mogą kraść tokenów i naruszać aplikacji.
  • Unikaj samodzielnego implementowania przepływu pracy uwierzytelniania. W większości przypadków biblioteki platformy bezpiecznie obsługują przepływ pracy uwierzytelniania przy użyciu przeglądarki systemu zamiast używania niestandardowego widoku Web View, który jest podatny na przejęcie.
  • Unikaj przeprowadzania uwierzytelniania za pomocą kontrolki Web View platformy. Zamiast tego polegaj na przeglądarce systemu, jeśli jest to możliwe.
  • Unikaj przekazywania tokenów do kontekstu dokumentu (JavaScript). W niektórych sytuacjach do wykonania autoryzowanego wywołania usługi zewnętrznej jest wymagana biblioteka języka JavaScript w dokumencie. Zamiast udostępniania tokenu dla języka JavaScript za pośrednictwem JS międzyoperacji:
    • Przekaż wygenerowany token tymczasowy do biblioteki i w elemencie Web View.
    • Przechwyć wychodzące żądanie sieciowe w kodzie.
    • Zastąp token tymczasowy tokenem rzeczywistym i upewnij się, że miejsce docelowe żądania jest prawidłowe.

Dodatkowe zasoby

Uwierzytelnianie w aplikacjach Blazor Hybrid jest obsługiwane przez biblioteki platformy natywnej, ponieważ udostępniają one rozszerzone gwarancje zabezpieczeń, których nie może zapewnić piaskownica przeglądarki. Przy uwierzytelnianiu aplikacji natywnych używa się mechanizmu specyficznego dla systemu operacyjnego lub jest ono realizowane za pośrednictwem protokołu federacyjnego, takiego jak OpenID Connect (OIDC). Postępuj zgodnie ze wskazówkami dotyczącymi dostawcy tożsamości wybranego dla aplikacji, a następnie dodatkowo zintegruj tożsamość z platformą Blazor na podstawie wskazówek zawartych w tym artykule.

Integracja uwierzytelniania musi zapewnić osiągnięcie następujących celów dla usług i składników Razor:

  • Stosowanie abstrakcji w pakiecie Microsoft.AspNetCore.Components.Authorization, takich jak AuthorizeView.
  • Reagowanie na zmiany w kontekście uwierzytelniania.
  • Uzyskiwanie dostępu do poświadczeń aprowizowanych przez aplikację z poziomu dostawcy tożsamości, takich jak tokeny dostępu do wykonywania autoryzowanych wywołań interfejsu API.

Po dodaniu uwierzytelniania do aplikacji .NET MAUI, WPF lub Windows Forms i upewnieniu się, że użytkownicy mogą się zalogować i wylogować, zintegruj uwierzytelnianie z platformą Blazor w celu udostępnienia uwierzytelnionego użytkownika usługom i składnikom Razor. Wykonaj następujące kroki:

Aplikacje .NET MAUI używają składnika Xamarin.Essentials: Web Authenticator: klasa WebAuthenticator umożliwia aplikacji inicjowanie przepływów uwierzytelniania opartych na przeglądarce, które nasłuchują wywołania zwrotnego do określonego adresu URL zarejestrowanego w aplikacji.

Aby uzyskać dodatkowe wskazówki, zobacz następujące zasoby:

Aplikacje Windows Forms używają Platforma tożsamości Microsoft do integracji z firmą Microsoft Entra (ME-ID) i usługą AAD B2C. Aby uzyskać więcej informacji, zobacz Omówienie biblioteki Microsoft Authentication Library (MSAL).

Tworzenie niestandardowego dostawcy AuthenticationStateProvider bez aktualizacji zmian użytkownika

Jeśli aplikacja uwierzytelnia użytkownika bezpośrednio po jej uruchomieniu, a uwierzytelniony użytkownik pozostaje taki sam w całym okresie jej istnienia, powiadomienia o zmianie użytkownika nie są wymagane, a aplikacja udostępnia tylko informacje o uwierzytelnionym użytkowniku. W tym scenariuszu użytkownik loguje się do aplikacji po jej otwarciu, a ekran logowania jest ponownie wyświetlany po wylogowaniu użytkownika. Poniższy dostawca ExternalAuthStateProvider to przykład implementacji niestandardowego dostawcy AuthenticationStateProvider w tym scenariuszu uwierzytelniania.

Uwaga

Poniższy niestandardowy dostawca AuthenticationStateProvider nie zawiera deklaracji przestrzeni nazw, dzięki czemu ten przykład kodu może dotyczyć dowolnej aplikacji Blazor Hybrid. Jednak w przypadku implementowania tego przykładu w aplikacji produkcyjnej najlepszym rozwiązaniem jest podanie przestrzeni nazw aplikacji.

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

W poniższych krokach opisano, jak to zrobić:

  • Dodaj wymagane przestrzenie nazw.
  • Dodaj usługi autoryzacji i abstrakcje Blazor do kolekcji usług.
  • Skompiluj kolekcję usług.
  • Rozwiąż problem z usługą AuthenticatedUser, aby ustawić podmiot zabezpieczeń oświadczeń uwierzytelnianego użytkownika. Aby uzyskać szczegółowe informacje, zobacz dokumentację dostawcy tożsamości.
  • Zwróć skompilowanego hosta.

W metodzie MauiProgram.CreateMauiApp pliku MauiProgram.cs dodaj przestrzenie nazw dla Microsoft.AspNetCore.Components.Authorization i System.Security.Claims:

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

Usuń następujący wiersz kodu, który zwraca skompilowaną aplikację Microsoft.Maui.Hosting.MauiApp:

- return builder.Build();

Zastąp poprzedni wiersz kodu następującym kodem. Dodaj kod OpenID/MSAL, aby uwierzytelnić użytkownika. Aby uzyskać szczegółowe informacje, zobacz dokumentację dostawcy tożsamości.

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;

W poniższych krokach opisano, jak to zrobić:

  • Dodaj wymagane przestrzenie nazw.
  • Dodaj usługi autoryzacji i abstrakcje Blazor do kolekcji usług.
  • Skompiluj kolekcję usług i dodaj tę skompilowaną kolekcję usług jako zasób do słownika ResourceDictionary aplikacji.
  • Rozwiąż problem z usługą AuthenticatedUser, aby ustawić podmiot zabezpieczeń oświadczeń uwierzytelnianego użytkownika. Aby uzyskać szczegółowe informacje, zobacz dokumentację dostawcy tożsamości.
  • Zwróć skompilowanego hosta.

W konstruktorze MainWindow(MainWindow.xaml.cs) dodaj przestrzenie nazw dla Microsoft.AspNetCore.Components.Authorization i System.Security.Claims:

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

Usuń następujący wiersz kodu, który dodaje skompilowaną kolekcję usług jako zasób do słownika ResourceDictionary aplikacji:

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

Zastąp poprzedni wiersz kodu następującym kodem. Dodaj kod OpenID/MSAL, aby uwierzytelnić użytkownika. Aby uzyskać szczegółowe informacje, zobacz dokumentację dostawcy tożsamości.

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;

W poniższych krokach opisano, jak to zrobić:

  • Dodaj wymagane przestrzenie nazw.
  • Dodaj usługi autoryzacji i abstrakcje Blazor do kolekcji usług.
  • Skompiluj kolekcję usług i dodaj tę skompilowaną kolekcję usług do dostawcy usług aplikacji.
  • Rozwiąż problem z usługą AuthenticatedUser, aby ustawić podmiot zabezpieczeń oświadczeń uwierzytelnianego użytkownika. Aby uzyskać szczegółowe informacje, zobacz dokumentację dostawcy tożsamości.

W konstruktorze Form1(Form1.cs) dodaj przestrzenie nazw dla Microsoft.AspNetCore.Components.Authorization i System.Security.Claims:

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

Usuń następujący wiersz kodu, który ustawia skompilowaną kolekcję usług jako dostawcę usług aplikacji:

- blazorWebView1.Services = services.BuildServiceProvider();

Zastąp poprzedni wiersz kodu następującym kodem. Dodaj kod OpenID/MSAL, aby uwierzytelnić użytkownika. Aby uzyskać szczegółowe informacje, zobacz dokumentację dostawcy tożsamości.

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;

Tworzenie niestandardowego dostawcy AuthenticationStateProvider z aktualizacjami zmian użytkownika

Aby zaktualizować użytkownika w trakcie działania aplikacji Blazor, wywołaj metodę NotifyAuthenticationStateChanged w ramach implementacji dostawcy AuthenticationStateProvider przy użyciu jednej z następujących metod:

Sygnalizowanie aktualizacji uwierzytelniania spoza widoku BlazorWebView (opcja 1)

Niestandardowy dostawca AuthenticationStateProvider może używać usługi globalnej do sygnalizowania aktualizacji uwierzytelniania. Zalecamy, aby usługa udostępniała zdarzenie, które dostawca AuthenticationStateProvider może zasubskrybować, a zdarzenie to wywoływało metodę NotifyAuthenticationStateChanged.

Uwaga

Poniższy niestandardowy dostawca AuthenticationStateProvider nie zawiera deklaracji przestrzeni nazw, dzięki czemu ten przykład kodu może dotyczyć dowolnej aplikacji Blazor Hybrid. Jednak w przypadku implementowania tego przykładu w aplikacji produkcyjnej najlepszym rozwiązaniem jest podanie przestrzeni nazw aplikacji.

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

W metodzie MauiProgram.CreateMauiApp pliku MauiProgram.cs dodaj przestrzeń nazw dla Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Dodaj usługi autoryzacji i abstrakcje Blazor do kolekcji usług:

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

W konstruktorze MainWindow(MainWindow.xaml.cs) dodaj przestrzeń nazw dla Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Dodaj usługi autoryzacji i abstrakcje Blazor do kolekcji usług:

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

W konstruktorze Form1(Form1.cs) dodaj przestrzeń nazw dla Microsoft.AspNetCore.Components.Authorization:

using Microsoft.AspNetCore.Components.Authorization;

Dodaj usługi autoryzacji i abstrakcje Blazor do kolekcji usług:

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

Wszędzie tam, gdzie aplikacja uwierzytelnia użytkownika, zapewnij rozstrzygnięcie usługi ExternalAuthService:

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

Wykonaj niestandardowy kod OpenID/MSAL, aby uwierzytelnić użytkownika. Aby uzyskać szczegółowe informacje, zobacz dokumentację dostawcy tożsamości. Uwierzytelniony użytkownik (w poniższym przykładzie: authenticatedUser) jest nowym podmiotem ClaimsPrincipal opartym na nowej tożsamości ClaimsIdentity.

Ustaw uwierzytelnionego użytkownika jako bieżącego użytkownika:

authService.CurrentUser = authenticatedUser;

Alternatywą dla powyższego podejścia jest ustawienie podmiotu zabezpieczeń użytkownika w obszarze System.Threading.Thread.CurrentPrincipal zamiast ustawiania go za pośrednictwem usługi, co pozwala uniknąć użycia kontenera wstrzykiwania zależności:

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

W przypadku tego alternatywnego podejścia do kolekcji usług dodawane są tylko usługi autoryzacji (AddAuthorizationCore) i dostawca CurrentThreadUserAuthenticationStateProvider (.AddScoped<AuthenticationStateProvider, CurrentThreadUserAuthenticationStateProvider>()).

Obsługa uwierzytelniania w obrębie widoku BlazorWebView (opcja 2)

Niestandardowy dostawca AuthenticationStateProvider może zawierać dodatkowe metody wyzwalania logowania i wylogowywania oraz aktualizowania użytkownika.

Uwaga

Poniższy niestandardowy dostawca AuthenticationStateProvider nie zawiera deklaracji przestrzeni nazw, dzięki czemu ten przykład kodu może dotyczyć dowolnej aplikacji Blazor Hybrid. Jednak w przypadku implementowania tego przykładu w aplikacji produkcyjnej najlepszym rozwiązaniem jest podanie przestrzeni nazw aplikacji.

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

W powyższym przykładzie:

  • Wywołanie LogInAsyncCore wyzwala proces logowania.
  • Wywołanie metody NotifyAuthenticationStateChanged powiadamia o tym, że w toku jest aktualizacja, co umożliwia aplikacji udostępnienie tymczasowego interfejsu użytkownika podczas procesu logowania lub wylogowania.
  • Zwracany element loginTask zwraca zadanie, dzięki czemu składnik, który wyzwolił logowanie, może czekać i zareagować po zakończeniu tego zadania.
  • Deweloper implementuje metodę LoginWithExternalProviderAsync na potrzeby zalogowania użytkownika przy użyciu zestawu SDK dostawcy tożsamości. Aby uzyskać więcej informacji, zobacz dokumentację dostawcy tożsamości. Uwierzytelniony użytkownik (authenticatedUser) jest nowym podmiotem ClaimsPrincipal opartym na nowej tożsamości ClaimsIdentity.

W metodzie MauiProgram.CreateMauiApp pliku MauiProgram.cs dodaj usługi autoryzacji i abstrakcje Blazor do kolekcji usług:

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

W konstruktorze MainWindow (MainWindow.xaml.cs) dodaj usługi autoryzacji i abstrakcje Blazor do kolekcji usług:

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

W konstruktorze Form1 (Form1.cs) dodaj usługi autoryzacji i abstrakcje Blazor do kolekcji usług:

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

Na podstawie poniższego składnika LoginComponent pokazano, jak zalogować użytkownika. W typowej aplikacji składnik LoginComponent jest wyświetlany w składniku nadrzędnym tylko w przypadku, gdy użytkownik nie jest zalogowany do aplikacji.

Shared/LoginComponent.razor:

@inject AuthenticationStateProvider AuthenticationStateProvider

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

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

Na podstawie poniższego składnika LogoutComponent pokazano, jak wylogować użytkownika. W typowej aplikacji składnik LogoutComponent jest wyświetlany w składniku nadrzędnym tylko w przypadku, gdy użytkownik jest zalogowany do aplikacji.

Shared/LogoutComponent.razor:

@inject AuthenticationStateProvider AuthenticationStateProvider

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

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

Uzyskiwanie dostępu do innych informacji uwierzytelniania

Platforma Blazor nie definiuje abstrakcji do obsługi innych poświadczeń, takich jak tokeny dostępu stosowane przy żądaniach HTTP do internetowych interfejsów API. Zalecamy przestrzeganie wskazówek dotyczących dostawcy tożsamości na potrzeby zarządzania poświadczeniami użytkownika przy użyciu elementów pierwotnych, które udostępnia zestaw SDK dostawcy tożsamości.

Zestawy SDK dostawców tożsamości często używają magazynu tokenów do obsługi poświadczeń użytkowników przechowywanych na urządzeniu. Jeśli do kontenera usługi zostanie dodany element pierwotny magazynu tokenów zestawu SDK, należy użyć tego elementu pierwotnego zestawu SDK w aplikacji.

Platforma Blazor nie ma informacji o poświadczeniach uwierzytelniania użytkowników i w żaden sposób nie wchodzi w interakcje z poświadczeniami, więc można swobodnie wybrać dowolne podejście do zastosowania w kodzie aplikacji, najlepiej te uważane za najwygodniejsze. Jednak podczas implementowania kodu uwierzytelniania w aplikacji postępuj zgodnie z ogólnymi wskazówkami dotyczącymi zabezpieczeń podanymi w następnej sekcji: Inne zagadnienia dotyczące zabezpieczeń uwierzytelniania.

Inne zagadnienia dotyczące zabezpieczeń uwierzytelniania

Proces uwierzytelniania ma wobec platformy Blazorcharakter zewnętrzny i w celu uzyskania dodatkowych wskazówek dotyczących zabezpieczeń zalecamy deweloperom korzystanie ze wskazówek dotyczących dostawcy tożsamości.

Podczas implementowania uwierzytelniania:

  • Unikaj uwierzytelniania w kontekście widoku Web View. Na przykład unikaj przeprowadzania przepływu uwierzytelniania za pomocą biblioteki OAuth języka JavaScript. W aplikacji jednostronicowej tokeny uwierzytelniania nie są ukryte w języku JavaScript, więc złośliwi użytkownicy mogą je łatwo wykrywać i wykorzystywać je do nieuczciwych celów. To ryzyko nie dotyczy aplikacji natywnych, ponieważ takie aplikacje mogą uzyskiwać tokeny tylko poza kontekstem przeglądarki, co oznacza, że nieautoryzowane skrypty zewnętrzne nie mogą kraść tokenów i naruszać aplikacji.
  • Unikaj samodzielnego implementowania przepływu pracy uwierzytelniania. W większości przypadków biblioteki platformy bezpiecznie obsługują przepływ pracy uwierzytelniania przy użyciu przeglądarki systemu zamiast używania niestandardowego widoku Web View, który jest podatny na przejęcie.
  • Unikaj przeprowadzania uwierzytelniania za pomocą kontrolki Web View platformy. Zamiast tego polegaj na przeglądarce systemu, jeśli jest to możliwe.
  • Unikaj przekazywania tokenów do kontekstu dokumentu (JavaScript). W niektórych sytuacjach do wykonania autoryzowanego wywołania usługi zewnętrznej jest wymagana biblioteka języka JavaScript w dokumencie. Zamiast udostępniania tokenu dla języka JavaScript za pośrednictwem JS międzyoperacji:
    • Przekaż wygenerowany token tymczasowy do biblioteki i w elemencie Web View.
    • Przechwyć wychodzące żądanie sieciowe w kodzie.
    • Zastąp token tymczasowy tokenem rzeczywistym i upewnij się, że miejsce docelowe żądania jest prawidłowe.

Dodatkowe zasoby