Condividi tramite


Conferma dell'account e ripristino della password in ASP.NET Core Blazor

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.

Questo articolo illustra come configurare un ASP.NET Core Blazor Web App con la conferma tramite posta elettronica e il ripristino della password.

Nota

Questo articolo si applica solo a Blazor Web Apps. Per implementare la conferma tramite posta elettronica e il ripristino delle password per le app autonome Blazor WebAssembly con ASP.NET Core Identity, vedere Conferma dell'account e ripristino delle password in ASP.NET Core Blazor WebAssembly con ASP.NET Core Identity.

Spazio dei nomi

Lo spazio dei nomi dell'app usato dall'esempio in questo articolo è BlazorSample. Aggiornare gli esempi di codice per usare lo spazio dei nomi dell'app.

Selezionare e configurare un provider di posta elettronica

In questo articolo l'API transazionale di Mailchimp viene usata tramite Mandrill.net per inviare messaggi di posta elettronica. È consigliabile usare un servizio di posta elettronica per inviare messaggi di posta elettronica anziché SMTP. SMTP è difficile da configurare e proteggere correttamente. Indipendentemente dal servizio di posta elettronica usato, accedere alle linee guida per le app .NET, creare un account, configurare una chiave API per il servizio e installare i pacchetti NuGet necessari.

Creare una classe per contenere la chiave API del provider di posta elettronica segreto. Nell'esempio riportato in questo articolo viene usata una classe denominata AuthMessageSenderOptions con una EmailAuthKey proprietà per contenere la chiave.

AuthMessageSenderOptions.cs:

namespace BlazorSample;

public class AuthMessageSenderOptions
{
    public string? EmailAuthKey { get; set; }
}

Registrare l'istanza AuthMessageSenderOptions di configurazione nel Program file :

builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);

Configurare un segreto utente per la chiave di sicurezza del provider

Se il progetto è già stato inizializzato per lo strumento Secret Manager, avrà già un identificatore dei segreti dell'app (<AppSecretsId>) nel file di progetto (.csproj). In Visual Studio è possibile stabilire se l'ID dei segreti dell'app è presente esaminando il pannello Proprietà quando il progetto viene selezionato in Esplora soluzioni. Se l'app non è stata inizializzata, eseguire il comando seguente in una shell dei comandi aperta nella directory del progetto. In Visual Studio è possibile usare il prompt dei comandi di PowerShell per sviluppatori.

dotnet user-secrets init

Impostare la chiave API con lo strumento Secret Manager. Nell'esempio seguente il nome della chiave deve EmailAuthKey corrispondere AuthMessageSenderOptions.EmailAuthKeya e la chiave è rappresentata dal {KEY} segnaposto. Eseguire il comando seguente con la chiave API:

dotnet user-secrets set "EmailAuthKey" "{KEY}"

Se si usa Visual Studio, è possibile verificare che il segreto sia impostato facendo clic con il pulsante destro del mouse sul progetto server in Esplora soluzioni e scegliendo Gestisci segreti utente.

Per altre informazioni, vedere Archiviazione sicura dei segreti delle app in fase di sviluppo in ASP.NET Core.

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.

Implementare IEmailSender

L'esempio seguente si basa sull'API transazionale di Mailchimp usando Mandrill.net. Per un provider diverso, fare riferimento alla relativa documentazione su come implementare l'invio di un messaggio di posta elettronica.

Aggiungere il pacchetto NuGet Mandrill.net al progetto.

Aggiungere la classe seguente EmailSender per implementare IEmailSender<TUser>. Nell'esempio seguente è ApplicationUser un oggetto IdentityUser. Il markup HTML del messaggio può essere ulteriormente personalizzato. Se l'oggetto message passato a MandrillMessage inizia con il < carattere , l'API Mandrill.net presuppone che il corpo del messaggio sia composto in HTML.

Components/Account/EmailSender.cs:

using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using Mandrill;
using Mandrill.Model;
using BlazorSample.Data;

namespace BlazorSample.Components.Account;

public class EmailSender(IOptions<AuthMessageSenderOptions> optionsAccessor,
    ILogger<EmailSender> logger) : IEmailSender<ApplicationUser>
{
    private readonly ILogger logger = logger;

    public AuthMessageSenderOptions Options { get; } = optionsAccessor.Value;

    public Task SendConfirmationLinkAsync(AppUser user, string email,
        string confirmationLink) => SendEmailAsync(email, "Confirm your email",
        "<html lang=\"en\"><head></head><body>Please confirm your account by " +
        $"<a href='{confirmationLink}'>clicking here</a>.</body></html>");

    public Task SendPasswordResetLinkAsync(AppUser user, string email,
        string resetLink) => SendEmailAsync(email, "Reset your password",
        "<html lang=\"en\"><head></head><body>Please reset your password by " +
        $"<a href='{resetLink}'>clicking here</a>.</body></html>");

    public Task SendPasswordResetCodeAsync(AppUser user, string email,
        string resetCode) => SendEmailAsync(email, "Reset your password",
        "<html lang=\"en\"><head></head><body>Please reset your password " +
        $"using the following code:<br>{resetCode}</body></html>");

    public async Task SendEmailAsync(string toEmail, string subject, string message)
    {
        if (string.IsNullOrEmpty(Options.EmailAuthKey))
        {
            throw new Exception("Null EmailAuthKey");
        }

        await Execute(Options.EmailAuthKey, subject, message, toEmail);
    }

    public async Task Execute(string apiKey, string subject, string message, 
        string toEmail)
    {
        var api = new MandrillApi(apiKey);
        var mandrillMessage = new MandrillMessage("sarah@contoso.com", toEmail, 
            subject, message);
        await api.Messages.SendAsync(mandrillMessage);

        logger.LogInformation("Email to {EmailAddress} sent!", toEmail);
    }
}

Nota

Il contenuto del corpo per i messaggi potrebbe richiedere una codifica speciale per il provider di servizi di posta elettronica. Se i collegamenti nel corpo del messaggio non possono essere seguiti nel messaggio di posta elettronica, consultare la documentazione del provider di servizi per risolvere il problema.

Configurare l'app per supportare la posta elettronica

Program Nel file modificare l'implementazione del mittente del messaggio di posta elettronica in EmailSender:

- builder.Services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();
+ builder.Services.AddSingleton<IEmailSender<ApplicationUser>, EmailSender>();

Rimuovere (IdentityNoOpEmailSenderComponents/Account/IdentityNoOpEmailSender.cs) dall'app.

RegisterConfirmation Nel componente (Components/Account/Pages/RegisterConfirmation.razor) rimuovere il blocco condizionale nel @code blocco che controlla se EmailSender è un IdentityNoOpEmailSenderoggetto :

- else if (EmailSender is IdentityNoOpEmailSender)
- {
-     ...
- }

Anche nel RegisterConfirmation componente rimuovere il markup e il Razor codice per controllare il emailConfirmationLink campo, lasciando solo la riga che indica all'utente di controllare il proprio messaggio di posta elettronica ...

- @if (emailConfirmationLink is not null)
- {
-     ...
- }
- else
- {
     <p>Please check your email to confirm your account.</p>
- }

@code {
-    private string? emailConfirmationLink;

     ...
}

Abilitare la conferma dell'account dopo che un sito ha utenti

L'abilitazione della conferma dell'account in un sito con gli utenti blocca tutti gli utenti esistenti. Gli utenti esistenti vengono bloccati perché gli account non vengono confermati. Per aggirare il blocco utente esistente, usare uno degli approcci seguenti:

  • Aggiornare il database per contrassegnare tutti gli utenti esistenti come confermati.
  • Confermare gli utenti esistenti. Ad esempio, inviare messaggi di posta elettronica in batch con collegamenti di conferma.

Timeout di posta elettronica e attività

Il timeout di inattività predefinito è 14 giorni. Il codice seguente imposta il timeout di inattività su cinque giorni con scadenza scorrevole:

builder.Services.ConfigureApplicationCookie(options => {
    options.ExpireTimeSpan = TimeSpan.FromDays(5);
    options.SlidingExpiration = true;
});

Modificare tutta la durata del token di protezione dei dati di base ASP.NET

Il codice seguente modifica il periodo di timeout dei token di protezione dei dati a tre ore:

builder.Services.Configure<DataProtectionTokenProviderOptions>(options =>
    options.TokenLifespan = TimeSpan.FromHours(3));

I token utente predefiniti Identity (AspNetCore/src//IdentityExtensions.Core/src/TokenOptions.cs) hanno un timeout di un giorno.

Nota

I collegamenti della documentazione all'origine del riferimento .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo corrente per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare l'elenco a discesa Switch branches or tags. Per altre informazioni, vedere How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Come selezionare un tag di versione del codice sorgente di ASP.NET - dotnet/AspNetCore.Docs #26205).

Modificare la durata del token di posta elettronica

La durata del token predefinita dei token utente è un giorno.Identity

Nota

I collegamenti della documentazione all'origine del riferimento .NET in genere caricano il ramo predefinito del repository, che rappresenta lo sviluppo corrente per la versione successiva di .NET. Per selezionare un tag per una versione specifica, usare l'elenco a discesa Switch branches or tags. Per altre informazioni, vedere How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Come selezionare un tag di versione del codice sorgente di ASP.NET - dotnet/AspNetCore.Docs #26205).

Per modificare la durata del token di posta elettronica, aggiungere un oggetto personalizzato DataProtectorTokenProvider<TUser> e DataProtectionTokenProviderOptions.

CustomTokenProvider.cs:

using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;

namespace BlazorSample;

public class CustomEmailConfirmationTokenProvider<TUser>
    : DataProtectorTokenProvider<TUser> where TUser : class
{
    public CustomEmailConfirmationTokenProvider(
        IDataProtectionProvider dataProtectionProvider,
        IOptions<EmailConfirmationTokenProviderOptions> options,
        ILogger<DataProtectorTokenProvider<TUser>> logger)
        : base(dataProtectionProvider, options, logger)
    {
    }
}

public class EmailConfirmationTokenProviderOptions 
    : DataProtectionTokenProviderOptions
{
    public EmailConfirmationTokenProviderOptions()
    {
        Name = "EmailDataProtectorTokenProvider";
        TokenLifespan = TimeSpan.FromHours(4);
    }
}

public class CustomPasswordResetTokenProvider<TUser> 
    : DataProtectorTokenProvider<TUser> where TUser : class
{
    public CustomPasswordResetTokenProvider(
        IDataProtectionProvider dataProtectionProvider,
        IOptions<PasswordResetTokenProviderOptions> options,
        ILogger<DataProtectorTokenProvider<TUser>> logger)
        : base(dataProtectionProvider, options, logger)
    {
    }
}

public class PasswordResetTokenProviderOptions : 
    DataProtectionTokenProviderOptions
{
    public PasswordResetTokenProviderOptions()
    {
        Name = "PasswordResetDataProtectorTokenProvider";
        TokenLifespan = TimeSpan.FromHours(3);
    }
}

Configurare i servizi per l'uso del provider di token personalizzato nel Program file:

builder.Services.AddIdentityCore<ApplicationUser>(options =>
    {
        options.SignIn.RequireConfirmedAccount = true;
        options.Tokens.ProviderMap.Add("CustomEmailConfirmation",
            new TokenProviderDescriptor(
                typeof(CustomEmailConfirmationTokenProvider<ApplicationUser>)));
        options.Tokens.EmailConfirmationTokenProvider = 
            "CustomEmailConfirmation";
    })
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddSignInManager()
    .AddDefaultTokenProviders();

builder.Services
    .AddTransient<CustomEmailConfirmationTokenProvider<ApplicationUser>>();

Risoluzione dei problemi

Se non è possibile ricevere messaggi di posta elettronica funzionanti:

  • Impostare un punto di interruzione in EmailSender.Execute per verificare SendEmailAsync che venga chiamato .
  • Creare un'app console per inviare messaggi di posta elettronica usando codice simile al EmailSender.Execute debug del problema.
  • Esaminare le pagine della cronologia dell'account nel sito Web del provider di posta elettronica.
  • Controllare la cartella della posta indesiderata per i messaggi.
  • Provare un altro alias di posta elettronica su un provider di posta elettronica diverso, ad esempio Microsoft, Yahoo o Gmail.
  • Provare a inviare a account di posta elettronica diversi.

Risorse aggiuntive