Share via


Proteggere ASP.NET Core Blazor WebAssembly con ASP.NET Core Identity

Le app autonome Blazor WebAssembly possono essere protette con ASP.NET Core Identity seguendo le indicazioni riportate in questo articolo.

Endpoint per la registrazione, l'accesso e la disconnessione

Anziché usare l'interfaccia utente predefinita fornita da ASP.NET Core Identity per spa e Blazor app, basata su Razor Pages, chiamare MapIdentityApi in un'API back-end per aggiungere JSendpoint API ON per la registrazione e l'accesso degli utenti con ASP.NET Core Identity. Identity Gli endpoint API supportano anche funzionalità avanzate, ad esempio l'autenticazione a due fattori e la verifica della posta elettronica.

Nel client chiamare l'endpoint /register per registrare un utente con l'indirizzo di posta elettronica e la password:

var result = await _httpClient.PostAsJsonAsync(
    "register", new
    {
        email,
        password
    });

Nel client accedere a un utente con cookie l'autenticazione usando l'endpoint con useCookies la /login stringa di query impostata su true:

var result = await _httpClient.PostAsJsonAsync(
    "login?useCookies=true", new
    {
        email,
        password
    });

L'API del server back-end stabilisce cookie l'autenticazione con una chiamata a AddIdentityCookies nel generatore di autenticazione:

builder.Services
    .AddAuthentication(IdentityConstants.ApplicationScheme)
    .AddIdentityCookies();

Autenticazione tramite token

Per scenari nativi e mobili in cui alcuni client non supportano cookie, l'API di accesso fornisce un parametro per richiedere i token. Viene generato un token personalizzato (proprietario della piattaforma ASP.NET Core Identity ) che può essere usato per autenticare le richieste successive. Il token deve essere passato nell'intestazione Authorization come token di connessione. Viene fornito anche un token di aggiornamento. Questo token consente all'app di richiedere un nuovo token alla scadenza di quella precedente senza forzare l'accesso dell'utente.

I token non sono token ON (JWT) standard JS. L'uso di token personalizzati è intenzionale, perché l'API predefinita Identity è destinata principalmente a scenari semplici. L'opzione token non è progettata per essere un provider di servizi di identità o un server token di identità completo, ma un'alternativa all'opzione cookie per i client che non possono usare cookies.

Il materiale sussidiario seguente inizia il processo di implementazione dell'autenticazione basata su token con l'API di accesso. Il codice personalizzato è necessario per completare l'implementazione. Per altre informazioni, vedere Usare Identity per proteggere un back-end dell'API Web per le applicazioni a pagina singola.

Anziché l'API del server back-end che stabilisce cookie l'autenticazione con una chiamata a AddIdentityCookies nel generatore di autenticazione, l'API del server configura l'autenticazione del token di connessione con il AddBearerToken metodo di estensione. Specificare lo schema per i token di autenticazione di connessione con IdentityConstants.BearerScheme.

In Backend/Program.csmodificare i servizi di autenticazione e la configurazione nel modo seguente:

builder.Services
    .AddAuthentication()
    .AddBearerToken(IdentityConstants.BearerScheme);

In BlazorWasmAuth/Identity/CookieAuthenticationStateProvider.csrimuovere il useCookies parametro della stringa di query nel LoginAsync metodo di CookieAuthenticationStateProvider:

- login?useCookies=true
+ login

A questo punto, è necessario fornire codice personalizzato per analizzare AccessTokenResponse nel client e gestire i token di accesso e aggiornamento. Per altre informazioni, vedere Usare Identity per proteggere un back-end dell'API Web per le applicazioni a pagina singola.

Scenari aggiuntivi Identity

Per altri Identity scenari forniti dall'API, vedere Usare Identity per proteggere un back-end dell'API Web per i contratti a pagina singola:

  • Proteggere gli endpoint selezionati
  • Autenticazione tramite token
  • Autenticazione a due fattori (2FA)
  • Codici di ripristino
  • Gestione delle informazioni utente

App di esempio

In questo articolo le app di esempio fungono da riferimento per le app autonome Blazor WebAssembly che accedono a ASP.NET Core Identity tramite un'API Web back-end. La dimostrazione include due app:

  • Backend: un'app per le API Web back-end che gestisce un archivio delle identità utente per ASP.NET Core Identity.
  • BlazorWasmAuth: un'app front-end autonoma Blazor WebAssembly con autenticazione utente.

Accedere alle app di esempio tramite la cartella della versione più recente dalla radice del repository con il collegamento seguente. Gli esempi sono disponibili per .NET 8 o versione successiva. Per informazioni su come eseguire le app di esempio, vedere il README file nella BlazorWebAssemblyStandaloneWithIdentity cartella .

Visualizzare o scaricare il codice di esempio (procedura per il download)

Pacchetti e codice dell'app per le API Web back-end

L'app per le API Web back-end gestisce un archivio delle identità utente per ASP.NET Core Identity.

Pacchetti

L'app usa i pacchetti NuGet seguenti:

Se l'app usa un provider di database diverso EF Core dal provider in memoria, non creare un riferimento al pacchetto nell'app per Microsoft.EntityFrameworkCore.InMemory.

Nel file di progetto dell'app (.csproj) viene configurata la globalizzazione invariante.

Codice dell'app di esempio

Le impostazioni dell'app configurano GLI URL back-end e front-end:

  • Backend app (BackendUrl): https://localhost:7211
  • BlazorWasmAuth app (FrontendUrl): https://localhost:7171

Il Backend.http file può essere usato per testare la richiesta di dati meteo. Si noti che l'app BlazorWasmAuth deve essere in esecuzione per testare l'endpoint e che l'endpoint sia hardcoded nel file. Per altre informazioni, vedere Usare file .http in Visual Studio 2022.

La configurazione e la configurazione seguenti sono disponibili nel file dell'app.Program

L'identità utente con cookie autenticazione viene aggiunta chiamando AddAuthentication e AddIdentityCookies. I servizi per i controlli di autorizzazione vengono aggiunti da una chiamata a AddAuthorizationBuilder.

Consigliato solo per le dimostrazioni, l'app usa il EF Core provider di database in memoria per la registrazione del contesto del database (AddDbContext). Il provider di database in memoria semplifica il riavvio dell'app e il test dei flussi utente di registrazione e accesso. Ogni esecuzione inizia con un nuovo database, ma l'app include codice dimostrativo di seeding utente di test, descritto più avanti in questo articolo. Se il database viene modificato in SQLite, gli utenti vengono salvati tra sessioni, ma il database deve essere creato tramite migrazioni, come illustrato nell'esercitazione EF Coreintroduttiva. È possibile usare altri provider relazionali, ad esempio SQL Server, per il codice di produzione.

Configurare Identity per usare il EF Core database ed esporre gli Identity endpoint tramite le chiamate a AddIdentityCore, AddEntityFrameworkStorese AddApiEndpoints.

Viene stabilito un criterio CORS (Cross-Origin Resource Sharing) per consentire le richieste provenienti dalle app front-end e back-end. Gli URL di fallback sono configurati per i criteri CORS se le impostazioni dell'app non le forniscono:

  • Backend app (BackendUrl): https://localhost:5001
  • BlazorWasmAuth app (FrontendUrl): https://localhost:5002

I servizi e gli endpoint per Swagger/OpenAPI sono inclusi per la documentazione e il test di sviluppo dell'API Web. Per altre informazioni su NSwag, vedere Introduzione a NSwag e ASP.NET Core.

Le attestazioni del ruolo utente vengono inviate da un'API minima nell'endpoint/roles.

Le route vengono mappate per Identity gli endpoint chiamando MapIdentityApi<AppUser>().

Un endpoint di disconnessione (/Logout) è configurato nella pipeline middleware per disconnettere gli utenti.

Per proteggere un endpoint, aggiungere il RequireAuthorization metodo di estensione alla definizione di route. Per un controller, aggiungere l'attributo [Authorize] al controller o all'azione.

Per altre informazioni sui modelli di base per l'inizializzazione e la configurazione di un'istanza DbContext di , vedere DbContext Lifetime, Configuration e Initialization nella EF Core documentazione.

Pacchetti e codice dell'app autonoma Blazor WebAssembly front-end

Un'app front-end autonoma Blazor WebAssembly illustra l'autenticazione utente e l'autorizzazione per accedere a una pagina Web privata.

Pacchetti

L'app usa i pacchetti NuGet seguenti:

Codice dell'app di esempio

La Models cartella contiene i modelli dell'app:

L'interfaccia IAccountManagement (Identity/CookieHandler.cs) fornisce servizi di gestione degli account.

La CookieAuthenticationStateProvider classe (Identity/CookieAuthenticationStateProvider.cs) gestisce lo stato per cookiel'autenticazione basata su e fornisce implementazioni del servizio di gestione degli account descritte dall'interfaccia IAccountManagement . Il LoginAsync metodo abilita cookie in modo esplicito l'autenticazione tramite il useCookies valore della stringa di query di true. La classe gestisce anche la creazione di attestazioni di ruolo per gli utenti autenticati.

La CookieHandler classe (Identity/CookieHandler.cs) garantisce che cookie le credenziali vengano inviate con ogni richiesta all'API Web back-end, che gestisce Identity e gestisce l'archivio Identity dati.

fornisce wwwroot/appsettings.file endpoint URL back-end e front-end.

Il App componente espone lo stato di autenticazione come parametro a catena. Per altre informazioni, vedere autenticazione e autorizzazione di base ASP.NETBlazor.

Il MainLayout componente e NavMenu il componente usano il AuthorizeView componente per visualizzare in modo selettivo il contenuto in base allo stato di autenticazione dell'utente.

I componenti seguenti gestiscono le attività di autenticazione utente comuni, che usano IAccountManagement i servizi:

Il PrivatePage componente (Components/Pages/PrivatePage.razor) richiede l'autenticazione e mostra le attestazioni dell'utente.

I servizi e la Program configurazione vengono forniti nel file (Program.cs):

  • Il cookie gestore viene registrato come servizio con ambito.
  • I servizi di autorizzazione vengono registrati.
  • Il provider di stato di autenticazione personalizzato viene registrato come servizio con ambito.
  • L'interfaccia di gestione degli account (IAccountManagement) è registrata.
  • L'URL host di base è configurato per un'istanza client HTTP registrata.
  • L'URL back-end di base è configurato per un'istanza client HTTP registrata usata per le interazioni di autenticazione con l'API Web back-end. Il client HTTP usa il cookie gestore per assicurarsi che cookie le credenziali vengano inviate con ogni richiesta.

Chiamare AuthenticationStateProvider.NotifyAuthenticationStateChanged quando lo stato di autenticazione dell'utente cambia. Per un esempio, vedere i metodi e della classe ().For an example, see the LoginAsync and LogoutAsync methods of the CookieAuthenticationStateProvider class (Identity/CookieAuthenticationStateProvider.cs).

Avviso

Il componente AuthorizeView visualizza in modo selettivo il contenuto dell'interfaccia utente a seconda del fatto che l'utente sia autorizzato. Tutto il contenuto all'interno di un'app Blazor WebAssembly inserita in un AuthorizeView componente è individuabile senza autenticazione, quindi il contenuto sensibile deve essere ottenuto da un'API Web basata su server back-end dopo che l'autenticazione ha esito positivo. Per ulteriori informazioni, vedi le seguenti risorse:

Dimostrazione del seeding dell'utente di test

La SeedData classe (SeedData.cs) illustra come creare utenti di test per lo sviluppo. L'utente di test, denominato Leela, accede all'app con l'indirizzo di leela@contoso.composta elettronica . La password dell'utente è impostata su Passw0rd!. Leela viene assegnata Administrator e Manager i ruoli per l'autorizzazione, che consente all'utente di accedere alla pagina manager all'indirizzo /private-manager-page ma non alla pagina dell'editor all'indirizzo /private-editor-page.

Avviso

Non consentire mai l'esecuzione del codice utente di test in un ambiente di produzione. SeedData.InitializeAsync viene chiamato solo nell'ambiente Development nel Program file :

if (builder.Environment.IsDevelopment())
{
    await using var scope = app.Services.CreateAsyncScope();
    await SeedData.InitializeAsync(scope.ServiceProvider);
}

Ruoli

A causa di un problema di progettazione del framework (dotnet/aspnetcore #50037), le attestazioni del ruolo non vengono inviate dall'endpoint manage/info per creare attestazioni utente per gli utenti dell'app BlazorWasmAuth . Le attestazioni del ruolo vengono gestite in modo indipendente tramite una richiesta separata nel GetAuthenticationStateAsync metodo dellaCookieAuthenticationStateProviderclasse (Identity/CookieAuthenticationStateProvider.cs) dopo l'autenticazione dell'utente nel Backend progetto.

CookieAuthenticationStateProviderIn , viene effettuata una richiesta di ruoli all'endpoint /roles del Backend progetto API server. La risposta viene letta in una stringa chiamando ReadAsStringAsync(). JsonSerializer.Deserialize deserializza la stringa in una matrice personalizzata RoleClaim . Infine, le attestazioni vengono aggiunte alla raccolta di attestazioni dell'utente.

Backend Nel file dell'API del Program server un'API minima gestisce l'endpoint/roles. Le attestazioni di vengono selezionate in un tipo anonimo e serializzate per tornare al BlazorWasmAuth progetto con TypedResults.Json.RoleClaimType

L'endpoint dei ruoli richiede l'autorizzazione chiamando RequireAuthorization. Se si decide di non usare API minime a favore dei controller per gli endpoint API server sicuri, assicurarsi di impostare l'attributo [Authorize] sui controller o sulle azioni.

Hosting tra domini (configurazione dello stesso sito)

Le app di esempio sono configurate per l'hosting di entrambe le app nello stesso dominio. Se si ospita l'app Backend in un dominio diverso da quello dell'app BlazorWasmAuth , rimuovere il commento dal codice che configura cookie (ConfigureApplicationCookie) nel Backend file dell'app Program . I valori predefiniti sono:

Modificare i valori in:

- options.Cookie.SameSite = SameSiteMode.Lax;
- options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
+ options.Cookie.SameSite = SameSiteMode.None;
+ options.Cookie.SecurePolicy = CookieSecurePolicy.Always;

Per altre informazioni sulle impostazioni dello stesso sito cookie , vedere le risorse seguenti:

Supporto antiforgerato

Solo l'endpoint di disconnessione (/logout) nell'app Backend richiede attenzione per attenuare la minaccia della richiesta cross-site forgery (CSRF).

L'endpoint di disconnessione verifica la presenza di un corpo vuoto per evitare attacchi CSRF. Richiedendo un corpo, la richiesta deve essere effettuata da JavaScript, che è l'unico modo per accedere all'autenticazione cookie. L'endpoint di disconnessione non può essere accessibile da un POST basato su form. In questo modo si impedisce a un sito dannoso di disconnettere l'utente.

Inoltre, l'endpoint è protetto dall'autorizzazione (RequireAuthorization) per impedire l'accesso anonimo.

L'app BlazorWasmAuth client è semplicemente necessaria per passare un oggetto {} vuoto nel corpo della richiesta.

All'esterno dell'endpoint di disconnessione, la mitigazione antiforgery è necessaria solo quando si inviano i dati del modulo al server codificati come application/x-www-form-urlencoded, multipart/form-datao text/plain. Blazor gestisce la mitigazione CSRF per i moduli nella maggior parte dei casi. Per altre informazioni, vedere ASP.NET Blazor Panoramica dell'autenticazione e dell'autorizzazione e dei moduli di base di ASP.NET CoreBlazor.

Le richieste ad altri endpoint API server (API Web) con application/jsoncontenuto codificato e CORS abilitato non richiedono la protezione CSRF. Questo è il motivo per cui non è necessaria alcuna protezione CSRF per l'endpoint Backend di elaborazione dati (/data-processing) dell'app. L'endpoint dei ruoli (/roles) non richiede la protezione CSRF perché si tratta di un endpoint GET che non modifica alcuno stato.

Risoluzione dei problemi

Registrazione

Per abilitare la registrazione di debug o traccia per Blazor WebAssembly l'autenticazione, vedere ASP.NET Registrazione coreBlazor.

Errori comuni

Controllare la configurazione di ogni progetto. Verificare che gli URL siano corretti:

  • Backend Progetto
    • appsettings.json
      • BackendUrl
      • FrontendUrl
    • Backend.http: Backend_HostAddress
  • BlazorWasmAuth Progetto: wwwroot/appsettings.json
    • BackendUrl
    • FrontendUrl

Se la configurazione è corretta:

  • Analizzare i log delle applicazioni.

  • Esaminare il traffico di rete tra l'app e Backend l'app BlazorWasmAuth con gli strumenti di sviluppo del browser. Spesso, un messaggio di errore esatto o un messaggio con un indizio sulla causa del problema viene restituito al client dall'app back-end dopo aver effettuato una richiesta. Strumenti di sviluppo materiale sussidiario sono disponibili negli articoli seguenti:

  • Google Chrome (documentazione di Google)

  • Microsoft Edge

  • Mozilla Firefox (documentazione di Mozilla)

Il team della documentazione risponde al feedback e ai bug dei documenti negli articoli. Aprire un problema usando il collegamento Apri un problema di documentazione nella parte inferiore dell'articolo. Il team non è in grado di fornire supporto tecnico. Sono disponibili diversi forum di supporto pubblico per facilitare la risoluzione dei problemi di un'app. Consigliamo quanto segue:

I forum precedenti non sono di proprietà o controllati da Microsoft.

Per i report sui bug del framework non riservati, non sensibili e non riservati, aprire un problema con l'unità del prodotto ASP.NET Core. Non aprire un problema con l'unità di prodotto fino a quando non hai approfondito la causa di un problema e non puoi risolverlo autonomamente e con l'aiuto della community in un forum di supporto pubblico. L'unità prodotto non è in grado di risolvere i problemi relativi alle singole app interrotte a causa di semplici errori di configurazione o casi d'uso che coinvolgono servizi di terze parti. Se un report è sensibile o riservato in natura o descrive un potenziale difetto di sicurezza nel prodotto che gli utenti malintenzionati possono sfruttare, vedere Segnalazione di problemi di sicurezza e bug (dotnet/aspnetcorerepository GitHub).

Cookies e i dati del sito

Cookies e i dati del sito possono persistere tra gli aggiornamenti delle app e interferire con i test e la risoluzione dei problemi. Cancellare quanto segue quando si apportano modifiche al codice dell'app, modifiche all'account utente o modifiche alla configurazione dell'app:

  • Account di accesso cookieutente
  • App cookies
  • Dati del sito memorizzati nella cache e archiviati

Un approccio per evitare che i dati del sito e di s persistenti cookieinterferiscano con i test e la risoluzione dei problemi consiste nel:

  • Configurare un browser
    • Usare un browser per i test che è possibile configurare per eliminare tutti i cookie dati del sito e ogni volta che il browser viene chiuso.
    • Assicurarsi che il browser venga chiuso manualmente o dall'IDE per qualsiasi modifica apportata alla configurazione dell'app, dell'utente di test o del provider.
  • Usare un comando personalizzato per aprire un browser in modalità InPrivate o In incognito in Visual Studio:
    • Aprire la finestra di dialogo Sfoglia con dal pulsante Esegui di Visual Studio.
    • Seleziona il pulsante Aggiungi.
    • Specificare il percorso del browser nel campo Programma . I percorsi eseguibili seguenti sono percorsi di installazione tipici per Windows 10. Se il browser è installato in un percorso diverso o non si usa Windows 10, specificare il percorso dell'eseguibile del browser.
      • 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
    • Nel campo Argomenti specificare l'opzione della riga di comando usata dal browser per aprire in modalità InPrivate o Incognito. Alcuni browser richiedono l'URL dell'app.
      • Microsoft Edge: usare -inprivate.
      • Google Chrome: usare --incognito --new-window {URL}, dove il segnaposto {URL} è l'URL da aprire (ad esempio, https://localhost:5001).
      • Mozilla Firefox: usare -private -url {URL}, dove il segnaposto {URL} è l'URL da aprire (ad esempio, https://localhost:5001).
    • Specificare un nome nel campo Nome descrittivo. Ad esempio: Firefox Auth Testing.
    • Selezionare il pulsante OK.
    • Per evitare di dover selezionare il profilo del browser per ogni iterazione di test con un'app, impostare il profilo come predefinito con il pulsante Imposta come predefinito .
    • Assicurarsi che il browser sia chiuso dall'IDE per qualsiasi modifica apportata all'app, all'utente di test o alla configurazione del provider.

Aggiornamenti di app

Un'app funzionante potrebbe non riuscire immediatamente dopo l'aggiornamento di .NET Core SDK nel computer di sviluppo o la modifica delle versioni dei pacchetti all'interno dell'app. In alcuni casi i pacchetti incoerenti possono interrompere un'app quando si eseguono aggiornamenti principali. La maggior parte di questi problemi può essere risolta attenendosi alle istruzioni seguenti:

  1. Cancellare le cache dei pacchetti NuGet del sistema locale eseguendo dotnet nuget locals all --clear da una shell dei comandi.
  2. Eliminare le cartelle e obj del bin progetto.
  3. Ripristinare e ricompilare il progetto.
  4. Eliminare tutti i file nella cartella di distribuzione nel server prima di ridistribuire l'app.

Nota

L'uso di versioni del pacchetto incompatibili con il framework di destinazione dell'app non è supportato. Per informazioni su un pacchetto, usare La raccolta NuGet o Esplora pacchetti FuGet.

Esaminare le attestazioni dell'utente

Per risolvere i problemi relativi alle attestazioni utente, il componente seguente UserClaims può essere usato direttamente nelle app o funge da base per un'ulteriore personalizzazione.

UserClaims.razor:

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

<PageTitle>User Claims</PageTitle>

<h1>User Claims</h1>

**Name**: @AuthenticatedUser?.Identity?.Name

<h2>Claims</h2>

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

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

    public ClaimsPrincipal? AuthenticatedUser { get; set; }

    protected override async Task OnInitializedAsync()
    {
        if (AuthenticationState is not null)
        {
            var state = await AuthenticationState;
            AuthenticatedUser = state.User;
        }
    }
}

Risorse aggiuntive