Teilen über


Kontobestätigung und Kennwortwiederherstellung in ASP.NET Core Blazor

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Die aktuelle Version finden Sie in der .NET 10-Version dieses Artikels.

Dieser Artikel erklärt, wie Sie ein ASP.NET Core Blazor Web App mit E-Mail-Bestätigung und Kennwortwiederherstellung konfigurieren.

Hinweis

Dieser Artikel gilt nur für Blazor Web App. Um die E-Mail-Bestätigung und Kennwortwiederherstellung für eigenständige „Blazor WebAssembly“-Apps mit ASP.NET Core „Identity“ zu implementieren, siehe „“ Konto-Bestätigung und Kennwortwiederherstellung in ASP.NET Core „Blazor WebAssembly“ mit ASP.NET Core „Identity“.

Namespace

Im Beispiel in diesem Artikel wird der App-Namespace BlazorSample verwendet. Aktualisieren Sie die Codebeispiele, um den Namespace Ihrer App zu verwenden.

Auswählen und Konfigurieren eines E-Mail-Anbieters

In diesem Artikel wird die Transactional API von Mailchimp über Mandrill.net zum Senden von E-Mails verwendet. Es wird empfohlen, einen E-Mail-Dienst statt SMTP zum Senden von E-Mails zu verwenden. SMTP ist schwer zu konfigurieren und zu schützen. Je nachdem, welchen E-Mail-Dienst Sie verwenden, befolgen Sie dessen Anleitungen für .NET-Apps, erstellen ein Konto, konfigurieren einen API-Schlüssel für den Dienst und installieren alle erforderlichen NuGet-Pakete.

Erstellen Sie eine Klasse, um den geheimen Anbieter-API-Schlüssel der E-Mail zu speichern. Das Beispiel in diesem Artikel verwendet eine Klasse mit dem Namen AuthMessageSenderOptions und einer EmailAuthKey-Eigenschaft, um den Schlüssel zu halten.

AuthMessageSenderOptions.cs:

namespace BlazorSample;

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

Registrieren Sie die AuthMessageSenderOptions-Konfigurationsinstanz in der Program-Datei:

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

Konfigurieren eines geheimen Schlüssels für den Sicherheitsschlüssel des E-Mail-Anbieters

Empfangen Sie den Sicherheitsschlüssel des E-Mail-Anbieters vom Anbieter, und verwenden Sie ihn in den folgenden Anleitungen.

Verwenden Sie entweder oder beide der folgenden Ansätze, um den geheimen Schlüssel für die App zu liefern:

  • Geheime Manager-Tool: Das Tool "Geheimer Manager" speichert private Daten auf dem lokalen Computer und wird nur während der lokalen Entwicklung verwendet.
  • Azure Key Vault: Sie können den geheimen Schlüssel in einem Schlüsseltresor speichern, um sie in jeder Umgebung zu verwenden, einschließlich für die Entwicklungsumgebung, wenn Sie lokal arbeiten. Einige Entwickler*innen ziehen es vor, für Staging- und Produktionsbereitstellungen Schlüsseltresore und für die lokale Entwicklung den Geheimnis-Manager zu verwenden.

Wir empfehlen dringend, Geheimnisse nicht im Projektcode oder in Konfigurationsdateien zu speichern. Verwenden Sie sichere Authentifizierungsflows, wie z. B. einen der in diesem Abschnitt beschriebenen Ansätze oder beide davon.

Geheimer Manager-Tool

Wenn das Projekt bereits für das Tool "Geheimer Manager" initialisiert wurde, verfügt es bereits über einen Bezeichner für geheime Benutzerschlüssel (<UserSecretsId>) in der Projektdatei (.csproj). In Visual Studio können Sie feststellen, ob die Benutzergeheimnisse-ID vorhanden ist, indem Sie sich den Eigenschaftenbereich ansehen, wenn das Projekt im Projektmappen-Explorer ausgewählt ist. Wenn die App nicht initialisiert wurde, führen Sie den folgenden Befehl in einer zur Projektverzeichnis geöffneten Shell aus. In Visual Studio können Sie die Developer PowerShell-Eingabeaufforderung verwenden.

dotnet user-secrets init

Setzen Sie den API-Schlüssel mit dem Geheimnis-Manager-Tool. Im folgenden Beispiel ist der Schlüssel-Name „EmailAuthKey“ zur Übereinstimmung mit „AuthMessageSenderOptions.EmailAuthKey“, und der Schlüssel wird durch den „{KEY}“-Platzhalter zugesichert. Führen Sie den folgenden Befehl mit dem API-Schlüssel aus:

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

Wenn Sie Visual Studio verwenden, können Sie das Geheimnis bestätigen, indem Sie mit der rechten Maustaste auf das Serverprojekt im Projektmappen-Explorer klicken und Benutzergeheimnisse verwalten auswählen.

Weitere Informationen finden Sie unter Sichere Speicherung von App-Geheimnissen bei der Entwicklung in ASP.NET Core.

Warnung

Speichern Sie keine App-Geheimnisse, Verbindungszeichenfolgen, Anmeldeinformationen, Kennwörter, Geheimzahlen (Personal Identication Numbers, PINs), privaten C#-/.NET-Code oder private Schlüssel/Token im clientseitigen Code. Dies ist immer unsicher. In Test- oder Stagingumgebungen und Produktionsumgebungen sollten serverseitiger Blazor-Code und Web-APIs sichere Authentifizierungsflows verwenden, die die Verwaltung von Anmeldeinformationen innerhalb von Projektcode oder Konfigurationsdateien vermeiden. Außerhalb der lokalen Entwicklungstests wird empfohlen, die Verwendung von Umgebungsvariablen zum Speichern vertraulicher Daten zu vermeiden, da Umgebungsvariablen nicht der sicherste Ansatz sind. Für lokale Entwicklungstests wird der Geheimnis-Manager zum Schützen vertraulicher Daten empfohlen. Weitere Informationen finden Sie unter Sichere Verwaltung von vertraulichen Daten und Anmeldeinformationen.

Azure Key Vault (ein Dienst zur sicheren Verwaltung kryptografischer Schlüssel)

Azure Key Vault bietet einen sicheren Ansatz, um das Client-Geheimnis der App für die App bereitzustellen.

Informationen zum Erstellen eines Schlüsseltresors und zum Festlegen eines Geheimnisses finden Sie unter Informationen zu Azure Key Vault-Geheimnissen (Azure-Dokumentation) sowie in den darin aufgeführten Ressourcen für den Einstieg in Azure Key Vault. Für das Beispiel in diesem Abschnitt lautet der geheime Name "EmailAuthKey."

Beim Einrichten des Schlüsseltresors im Entra- oder Azure-Portal:

  • Konfigurieren Sie den Schlüsseltresor für die Verwendung der rollenbasierten Zugriffssteuerung (Azure Role-Based Access Control, RABC). Wenn Sie nicht in einem virtuellen Azure-Netzwerk arbeiten, einschließlich für lokale Entwicklung und Tests, bestätigen Sie, dass der öffentliche Zugriff auf den Netzwerkschrittaktiviert ist (aktiviert). Das Aktivieren des öffentlichen Zugriffs macht nur den Schlüsseltresor-Endpunkt verfügbar. Authentifizierte Konten sind weiterhin für den Zugriff erforderlich.

  • Erstellen Sie eine Azure verwaltete Identity (oder fügen Sie eine Rolle zur bestehenden verwalteten Identity hinzu, die Sie zur Nutzung planen) mit der Key Vault Secrets Benutzer Rolle. Weisen Sie das verwaltete Identity dem Azure App-Dienst zu, der die Bereitstellung hostet: Einstellungen>Identity>Vom Benutzer zugewiesen>Hinzufügen.

    Hinweis

    Wenn Sie auch beabsichtigen, eine App lokal mit einem autorisierten Benutzer für den Schlüsseltresorzugriff mithilfe der Azure CLI oder der Azure Service Authentication von Visual Studio auszuführen, fügen Sie Ihr Entwickler-Azure-Benutzerkonto in Access Control (IAM) mit der Rolle " Schlüsseltresorschlüsselbenutzer " hinzu. Wenn Sie die Azure CLI über Visual Studio verwenden möchten, führen Sie den az login Befehl im PowerShell-Bereich "Entwicklertools" aus, und folgen Sie den Anweisungen, um sich beim Mandanten zu authentifizieren.

Um den Code in diesem Abschnitt zu implementieren, erfassen Sie die Schlüsseltresor-URI (Beispiel: https://contoso.vault.azure.net/, nachstehender Schrägstrich erforderlich) und der Geheimnisname (Beispiel: EmailAuthKey") von Azure, wenn Sie den Schlüsseltresor und das Geheimnis erstellen.

Wichtig

Ein Schlüsseltresorgeheimnis wird mit einem Ablaufdatum erstellt. Achten Sie darauf, nachzuverfolgen, wann ein Schlüsseltresorgeheimnis abläuft, und erstellen Sie vor dem Ablauf dieses Datums ein neues Geheimnis für die App.

Fügen Sie dem Serverprojekt die folgende AzureHelper Klasse hinzu. Die GetKeyVaultSecret-Methode ruft einen geheimen Schlüssel aus einem Schlüsseltresor ab. Passen Sie den Namespace (BlazorSample.Helpers) an Ihr Projekt-Namespace-Schema an.

Helpers/AzureHelper.cs:

using Azure.Core;
using Azure.Security.KeyVault.Secrets;

namespace BlazorSample.Helpers;

public static class AzureHelper
{
    public static string GetKeyVaultSecret(string vaultUri, 
        TokenCredential credential, string secretName)
    {
        var client = new SecretClient(new Uri(vaultUri), credential);
        var secret = client.GetSecretAsync(secretName).Result;

        return secret.Value.Value;
    }
}

Wenn Dienste in der Program-Datei des Serverprojekts registriert sind, abrufen und binden Sie das Geheimnis mit der Optionskonfiguration:

TokenCredential? credential;

if (builder.Environment.IsProduction())
{
    credential = new ManagedIdentityCredential("{MANAGED IDENTITY CLIENT ID}");
}
else
{
    // Local development and testing only
    DefaultAzureCredentialOptions options = new()
    {
        // Specify the tenant ID to use the dev credentials when running the app locally
        // in Visual Studio.
        VisualStudioTenantId = "{TENANT ID}",
        SharedTokenCacheTenantId = "{TENANT ID}"
    };

    credential = new DefaultAzureCredential(options);
}

var emailAuthKey = AzureHelper.GetKeyVaultSecret("{VAULT URI}", credential, 
    "EmailAuthKey");

var authMessageSenderOptions = 
    new AuthMessageSenderOptions() { EmailAuthKey = emailAuthKey };
builder.Configuration.GetSection(authMessageSenderOptions.EmailAuthKey)
    .Bind(authMessageSenderOptions);

Hinweis

In Nicht-Produktionsumgebungen wird im vorherigen Beispiel die Authentifizierung mithilfe von DefaultAzureCredential vereinfacht. Dabei werden Anmeldeinformationen kombiniert, die sowohl in Azure-Hostingumgebungen als auch in der lokalen Entwicklung verwendet werden, während Apps entwickelt werden, die in Azure bereitgestellt werden. Weitere Informationen finden Sie unter Authentifizieren von von Azure gehosteten .NET-Apps für Azure-Ressourcen mithilfe einer vom System zugewiesenen verwalteten Identität.

Das vorhergehende Beispiel impliziert, dass die verwaltete Identity Client-ID ({MANAGED IDENTITY CLIENT ID}), Verzeichnis (Mandant)-ID ({TENANT ID}) und Schlüsseltresor-URI ({VAULT URI}, Beispiel: https://contoso.vault.azure.net/nachstehender Schrägstrich erforderlich) werden durch hartcodierte Werte bereitgestellt. Alle oder alle diese Werte können aus der Konfiguration der App-Einstellungen bereitgestellt werden. Zum Beispiel wird die Tresor-URI aus dem AzureAd-Knoten einer App-Einstellungsdatei abgerufen, und vaultUri kann im Aufruf von GetKeyVaultSecret im vorhergehenden Beispiel verwendet werden:

var vaultUri = builder.Configuration.GetValue<string>("AzureAd:VaultUri")!;

Weitere Informationen finden Sie unter ASP.NET Core Blazor Configuration.

Implementieren Sie IEmailSender.

Das folgende Beispiel basiert auf der Transactional API von Mailchimp unter Verwendung von Mandrill.net. Für einen anderen Anbieter, lesen Sie deren Dokumentation zur Implementierung des Sendens einer E-Mail.

Fügen Sie das Mandrill.net NuGet-Paket dem Projekt hinzu.

Fügen Sie die folgende EmailSender Implementierungsklasse hinzu, um IEmailSender<TUser> zu implementieren. Im folgenden Beispiel ist ApplicationUser ein IdentityUser. Das HTML-Markup der Nachricht kann weiter angepasst werden. Solange das message an MandrillMessage übergebene Element mit dem < Zeichen beginnt, geht die Mandrill.net-API davon aus, dass der Nachrichtentext in HTML verfasst ist.

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

Hinweis

Textkörperinhalte für Nachrichten erfordern möglicherweise eine spezielle Codierung für den E-Mail-Dienstanbieter. Wenn Verknüpfungen im Nachrichtentext der E-Mail nicht gefolgt werden können, konsultieren Sie die Dokumentation des Dienstanbieters, um das Problem zu beheben.

Konfigurieren der App zur Unterstützung von E-Mails

Ändern Sie in der Datei Program die Implementierung des E-Mail-Absenders in EmailSender:

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

Entfernen Sie IdentityNoOpEmailSender (Components/Account/IdentityNoOpEmailSender.cs) aus der App.

Entfernen Sie in der Komponente RegisterConfirmation (Components/Account/Pages/RegisterConfirmation.razor) den Bedingungsblock aus dem @code-Block, der überprüft, ob EmailSender ein IdentityNoOpEmailSender-Element ist:

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

Entfernen Sie in der Komponente RegisterConfirmation ebenfalls das Razor-Markup und den Code zum Überprüfen des Felds emailConfirmationLink, und lassen Sie nur die Zeile, in der oder die Benutzer*in angewiesen wird, seine oder ihre E-Mail zu überprüfen.

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

@code {
-    private string? emailConfirmationLink;

     ...
}

Aktivieren der Kontobestätigung, nachdem eine Website Benutzer*innen enthält

Durch Aktivieren der Kontobestätigung auf einer Website mit Benutzer*innen werden alle vorhandenen Benutzer*innen gesperrt. Vorhandene Benutzer werden gesperrt, da ihre Konten nicht bestätigt wurden. Verwenden Sie einen der folgenden Ansätze, um die Sperrung vorhandener Benutzer*innen zu umgehen:

  • Aktualisieren Sie die Datenbank, um alle vorhandenen Benutzer*innen als bestätigt zu kennzeichnen.
  • Bestätigen Sie die vorhandenen Benutzer*innen. Senden Sie beispielsweise Batch-E-Mails mit Bestätigungslinks.

Timeout für E-Mails und Aktivitäten

Der Standardzeitraum für die Inaktivität bis zum Timeout beträgt 14 Tage. Der folgende Code setzt das Inaktivitäts-Timeout auf fünf Tage mit gleitendem Ablauf:

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

Ändern Sie alle ASP.NET Core Schutz von Daten Token-Lebensdauern

Der folgende Code ändert das Zeitlimit für Schutz von Daten-Token auf drei Stunden.

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

Die integrierten Identity Benutzer-Token (AspNetCore/src/Identity/Extensions.Core/src/TokenOptions.cs) haben ein Zeitlimit von einem Tag.

Hinweis

Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).

Ändern der Lebensdauer von E-Mail-Token

Die Standard-Token-Lebensdauer der „Identity“-Benutzertokens„“ beträgt „“einen Tag„“.

Hinweis

Dokumentationslinks zur .NET-Referenzquelle laden in der Regel den Standardbranch des Repositorys, der die aktuelle Entwicklung für das nächste Release von .NET darstellt. Um ein Tag für ein bestimmtes Release auszuwählen, wählen Sie diesen mit der Dropdownliste Switch branches or tags (Branches oder Tags wechseln) aus. Weitere Informationen finden Sie unter How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205) (Auswählen eines Versionstags von ASP.NET Core-Quellcode (dotnet/AspNetCore.Docs #26205)).

Um die Lebensdauer des E-Mail-Tokens zu ändern, fügen Sie eine benutzerdefinierte DataProtectorTokenProvider<TUser> und DataProtectionTokenProviderOptions hinzu.

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

Konfigurieren Sie die Dienste in der Datei Program für die Verwendung des benutzerdefinierten Tokenanbieters:

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

Problembehandlung

Wenn E-Mails nicht funktionieren:

  • Legen Sie einen Breakpoint in EmailSender.Execute fest, um zu überprüfen, ob SendEmailAsync aufgerufen wird.
  • Erstellen Sie eine Konsolen-App, um E-Mails mithilfe von Code zu senden, der EmailSender.Execute zum Debuggen des Problems ähnelt.
  • Überprüfen Sie die E-Mail-Verlaufsseiten des Kontos auf der Website des E-Mail-Anbieters.
  • Überprüfen Sie Ihren Spamordner auf Nachrichten.
  • Versuchen Sie es mit einem anderen E-Mail-Alias bei einem anderen E-Mail-Anbieter, zum Beispiel Microsoft, Yahoo oder Gmail.
  • Versuchen Sie, die E-Mail an andere E-Mail-Konten zu senden.

Zusätzliche Ressourcen