Condividi tramite


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

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Importante

Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.

Per la versione corrente, vedere la versione .NET 9 di questo articolo.

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

Invece di usare l'interfaccia utente predefinita fornita da ASP.NET Core Identity per le app SPA e Blazor, che si basa su Razor Pages, chiamando MapIdentityApi in un'API di back-end per aggiungere endpoint API JSON 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
    });

Sul client, accedi a un utente con l'autenticazione utilizzando l'endpoint e la 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 i cookie, l'API di accesso fornisce un parametro per richiedere i token.

Avviso

È consigliabile usare i cookie per le app basate su browser anziché i token perché il browser gestisce i cookie senza esporli a JavaScript. Se si sceglie di usare la sicurezza basata su token nelle app Web, si è responsabili della sicurezza dei 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 portatore. 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 JSON Web Token (JWT) standard. 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 di token completo, ma un'alternativa all'opzione cookie per i client che non possono usare i cookie.

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 backend dell'API Web per le SPA.

Invece di stabilire l'autenticazione tramite l'API del server back-end con una chiamata a AddIdentityCookies nel costruttore di autenticazione, l'API del server configura l'autenticazione con token di tipo bearer con il metodo di estensione AddBearerToken. Specificare lo schema per i token di autenticazione del portatore 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 applicazioni a pagina singola.

Scenari aggiuntivi Identity

Scenari coperti dal set di Blazor documentazione:

Per informazioni su scenari aggiuntivi Identity forniti dall'API, vedere Usare Identity per proteggere un back-end dell'API Web per le SPA (Single Page Applications):

  • Proteggere gli endpoint selezionati
  • Gestione delle informazioni utente

Usare flussi di autenticazione sicuri per mantenere i dati sensibili e le credenziali

Avviso

Non archiviare segreti dell'app, stringa di connessione, credenziali, password, numeri di identificazione personale (PIN), codice C#/.NET privato o chiavi/token privati nel codice lato client, che è sempre non sicuro. Negli ambienti di test/gestione temporanea e produzione, il codice lato Blazor server e le API Web devono usare flussi di autenticazione sicuri che evitano di mantenere le credenziali all'interno del codice del progetto o dei file di configurazione. Al di fuori dei test di sviluppo locali, è consigliabile evitare l'uso di variabili di ambiente per archiviare i dati sensibili, perché le variabili di ambiente non sono l'approccio più sicuro. Per i test di sviluppo locali, lo strumento Secret Manager è consigliato per proteggere i dati sensibili. Per altre informazioni, vedere Gestire in modo sicuro dati e credenziali sensibili.

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 la tua app utilizza un provider di database diverso EF Core rispetto a quello in-memory, non aggiungere un riferimento al pacchetto nella tua 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 attiva per testare l'endpoint e che l'endpoint è hardcoded nel file. Per altre informazioni, vedere Usare file .http in Visual Studio 2022.

La seguente impostazione e configurazione si trovano nel file dell'applicazioneProgram

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

Consigliata solo per le dimostrazioni, l'app utilizza il provider per 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 per il popolamento dei dati di utenti 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.

Nota

†L'esercitazione EF Core introduttiva usa i comandi di PowerShell per eseguire migrazioni di database quando si usa Visual Studio. Un approccio alternativo in Visual Studio consiste nell'usare l'interfaccia utente di Servizi connessi:

In Esplora soluzioni fare doppio clic su Servizi connessi. In Dipendenze del servizio>SQL Server Express LocalDB, selezionare i puntini di sospensione (...) seguiti da Aggiungi migrazione per creare una migrazione o Aggiorna il database per aggiornare il database.

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 all'endpoint.

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 ulteriori informazioni sui modelli di base per l'inizializzazione e la configurazione di un'istanza di DbContext, consultare DbContext Lifetime, Configuration e Initialization nella documentazione di EF Core.

Pacchetti e codice dell'app Blazor WebAssembly autonoma di 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 dell'autenticazione basata su cookie e fornisce implementazioni del servizio di gestione degli account descritte dall'interfaccia IAccountManagement. Il metodo LoginAsync abilita in modo esplicito l'autenticazione cookie tramite il valore della stringa di query useCookies 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.

wwwroot/appsettings.file fornisce endpoint URL backend e frontend.

Il App componente espone lo stato di autenticazione come parametro a catena. Per ulteriori informazioni, vedere autenticazione e autorizzazione di ASP.NET Core Blazor.

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

I componenti seguenti gestiscono le attività di autenticazione utente comuni, che usano IAccountManagement 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 sono registrati.
  • Il provider di stato di autenticazione personalizzato viene registrato come servizio con ambito limitato.
  • 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 ().

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 vengono assegnati i ruoli Administrator e Manager per l'autorizzazione, che consentono 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

Le attestazioni di ruolo non vengono restituite 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 della CookieAuthenticationStateProvider classe (Identity/CookieAuthenticationStateProvider.cs) dopo l'autenticazione dell'utente nel Backend progetto.

Nel CookieAuthenticationStateProvider, una richiesta di ruoli viene effettuata all'endpoint /roles del progetto API server Backend. 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.

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

L'endpoint relativo ai 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 ospiti l'app Backend in un dominio diverso da quello dell'app BlazorWasmAuth, decommenta il codice che configura cookie (ConfigureApplicationCookie) nel file Backend 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 mitigare la minaccia della Cross-Site Request 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.

Al di fuori dell'endpoint di disconnessione, la mitigazione antifalsificazione è necessaria solo quando si inviano i dati del modulo al server codificati come application/x-www-form-urlencoded, multipart/form-data o text/plain. Blazor gestisce la mitigazione CSRF per i moduli nella maggior parte dei casi. Per ulteriori informazioni, vedere ASP.NET Core Blazor autenticazione e autorizzazione e panoramica dei moduli 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 Core Blazor logging.

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.

  • Esamina il traffico di rete tra l'app BlazorWasmAuth e l'app Backend 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. Gli strumenti di sviluppo si trovano 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. Segnalare 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 confidenziali, segnalare un problema all'unità di prodotto di 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 potrebbero sfruttare i cyberattacker, vedere Segnalazione di problemi e bug di sicurezza (dotnet/aspnetcorerepository GitHub).

Cookie e dati del sito

I cookie 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:

  • Cookie di accesso utente
  • Cookie dell'app
  • Dati del sito memorizzati nella cache e archiviati

Un approccio per evitare che i cookie e i dati del sito persistenti interferiscano 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:
    • Apri 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 {URL} segnaposto è l'URL da aprire (ad esempio, https://localhost:5001).
      • Mozilla Firefox: usare -private -url {URL}, dove il {URL} segnaposto è l'URL da aprire (ad esempio, https://localhost:5001).
    • Specificare un nome nel campo Friendly name. 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 funzionare immediatamente dopo l'aggiornamento dello .NET Core SDK sul computer di sviluppo o la modifica delle versioni dei pacchetti. 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 bin e obj del 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 .

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