Dela via


Kontobekräftelse och lösenordsåterställning i ASP.NET Core Blazor

Obs

Det här är inte den senaste versionen av den här artikeln. Den aktuella versionen finns i den .NET 9-versionen av den här artikeln.

Viktig

Den här informationen gäller en förhandsversionsprodukt som kan ändras avsevärt innan den släpps kommersiellt. Microsoft lämnar inga garantier, uttryckliga eller underförstådda, med avseende på den information som tillhandahålls här.

Den aktuella versionen finns i den .NET 9-versionen av den här artikeln.

Den här artikeln beskriver hur du konfigurerar en ASP.NET Core-Blazor Web App med e-postbekräftelse och lösenordsåterställning.

Obs

Den här artikeln gäller endast för Blazor Web Apps. Information om hur du implementerar e-postbekräftelse och lösenordsåterställning för fristående Blazor WebAssembly-appar med ASP.NET Core Identityfinns i Kontobekräftelse och lösenordsåterställning i ASP.NET Core Blazor WebAssembly med ASP.NET Core Identity.

Namespace

Appens namnområde som används av exemplet i den här artikeln är BlazorSample. Uppdatera kodexemplen för att använda namnområdet för din app.

Välj och konfigurera en e-postprovider

I den här artikeln används Mailchimps transaktions-API via Mandrill.net för att skicka e-post. Vi rekommenderar att du använder en e-posttjänst för att skicka e-post i stället för SMTP. SMTP är svårt att konfigurera och skydda korrekt. Oavsett vilken e-posttjänst du använder får du åtkomst till deras vägledning för .NET-appar, skapar ett konto, konfigurerar en API-nyckel för tjänsten och installerar alla NuGet-paket som krävs.

Skapa en klass som innehåller API-nyckeln för den hemliga e-postleverantören. I exemplet i den här artikeln används en klass med namnet AuthMessageSenderOptions med en EmailAuthKey-egenskap för att lagra nyckeln.

AuthMessageSenderOptions.cs:

namespace BlazorSample;

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

Registrera AuthMessageSenderOptions-konfigurationsinstansen i Program-filen:

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

Konfigurera en hemlighet för e-postleverantörens säkerhetsnyckel

Ta emot e-postleverantörens säkerhetsnyckel från providern och använd den i följande vägledning.

Använd någon av eller båda av följande metoder för att tillhandahålla hemligheten till appen:

  • Secret Manager-verktyget: Secret Manager-verktyget lagrar privata data på den lokala datorn och används endast under lokal utveckling.
  • Azure Key Vault-: Du kan lagra hemligheten i ett nyckelvalv för användning i alla miljöer, inklusive för utvecklingsmiljön när du arbetar lokalt. Vissa utvecklare föredrar att använda nyckelvalv för mellanlagrings- och produktionsdistributioner och använder verktyget Secret Manager för lokal utveckling.

Vi rekommenderar starkt att du undviker att lagra hemligheter i projektkod eller konfigurationsfiler. Använd säkra autentiseringsflöden, till exempel någon av eller båda metoderna i det här avsnittet.

Secret Manager-verktyget

Om projektet redan har initierats för verktyget Secret Managerhar det redan en apphemlighetsidentifierare (<AppSecretsId>) i projektfilen (.csproj). I Visual Studio kan du se om apphemlighets-ID:t finns genom att titta på panelen Egenskaper när projektet har valts i Solution Explorer. Om appen inte har initierats kör du följande kommando i ett kommandogränssnitt som öppnas i projektets katalog. I Visual Studio kan du använda kommandotolken Developer PowerShell.

dotnet user-secrets init

Ange API-nyckeln med verktyget Secret Manager. I följande exempel är nyckelnamnet EmailAuthKey för att matcha AuthMessageSenderOptions.EmailAuthKey, och nyckeln representeras av platshållaren {KEY}. Kör följande kommando med API-nyckeln:

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

Om du använder Visual Studio kan du bekräfta att hemligheten har angetts genom att högerklicka på serverprojektet i Solution Explorer och välja Hantera användarhemligheter.

Mer information finns i Säker lagring av apphemligheter under utveckling i ASP.NET Core.

Varning

Lagra inte apphemligheter, anslutningssträngar, autentiseringsuppgifter, lösenord, personliga identifieringsnummer (PIN),privat C#/.NET-kod eller privata nycklar/token i kod på klientsidan, vilket är alltid osäker. I test-/mellanlagrings- och produktionsmiljöer bör Blazor kod på serversidan och webb-API:er använda säkra autentiseringsflöden som undviker att underhålla autentiseringsuppgifter i projektkod eller konfigurationsfiler. Förutom testning av lokal utveckling rekommenderar vi att du undviker användning av miljövariabler för att lagra känsliga data, eftersom miljövariabler inte är den säkraste metoden. För testning av lokal utveckling rekommenderas verktyget Secret Manager för att skydda känsliga data. Mer information finns i På ett säkert sätt underhålla känsliga data och autentiseringsuppgifter.

Azure 密钥保管库

Azure Key Vault- är en säker metod för att tillhandahålla appens klienthemlighet till appen.

Information om hur du skapar ett nyckelvalv och anger en hemlighet finns i Om Azure Key Vault-hemligheter (Azure-dokumentation), som korslänkar resurser för att komma igång med Azure Key Vault. I exemplet i det här avsnittet är det hemliga namnet "EmailAuthKey".

När du upprättar nyckelvalvet i Entra- eller Azure-portalen:

  • Konfigurera nyckelvalvet så att det använder rollbaserad åtkomstkontroll i Azure (RABC). Om du inte arbetar i ett virtuellt Azure-nätverk, inklusive för lokal utveckling och testning, bekräftar du att offentlig åtkomst i nätverkssteget är aktiverad (markerad). Om du aktiverar offentlig åtkomst exponeras endast nyckelvalvets slutpunkt. Autentiserade konton krävs fortfarande för åtkomst.

  • Skapa en Azure Managed Identity (eller lägg till en roll i den befintliga Managed Identity som du planerar att använda) med rollen Användare av Key Vault-hemligheter. Tilldela den hanterade Identity till den Azure App Service som är värd för distributionen: Inställningar>Identity>Användartilldelad>Lägg till.

    Obs

    Om du även planerar att köra en app lokalt med en behörig användare för åtkomst till nyckelvalvet med hjälp av Azure CLI eller Visual Studio Azure Service Authentication lägger du till ditt Azure-användarkonto för utvecklare i Åtkomstkontroll (IAM) med rollen Key Vault Secrets User . Om du vill använda Azure CLI via Visual Studio, kör kommandot az login från Utvecklar-PowerShell-panelen och följ anvisningarna för att autentisera med tenant.

Om du vill implementera koden i det här avsnittet registrerar du nyckelvalvets URI (exempel: "https://contoso.vault.azure.net/", avslutande snedstreck krävs) och det hemliga namnet (exempel: "EmailAuthKey") från Azure när du skapar nyckelvalvet och hemligheten.

Viktig

En nyckelvalvshemlighet skapas med ett förfallodatum. Se till att spåra när en nyckelvalvshemlighet upphör att gälla och skapa en ny hemlighet för appen innan det datumet passerar.

Lägg till följande AzureHelper-klass i serverprojektet. Metoden GetKeyVaultSecret hämtar en hemlighet från ett nyckelvalv. Justera namnområdet (BlazorSample.Helpers) så att det matchar projektets namnområdesschema.

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

Om tjänster registreras i serverprojektets Program-fil hämtar och binder du hemligheten med konfigurationen Alternativ:

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

Obs

I miljöer som inte är produktionsmiljöer används DefaultAzureCredential föregående exempel för att förenkla autentiseringen vid utveckling av appar som distribueras till Azure genom att kombinera autentiseringsuppgifter som används i Azure-värdmiljöer med autentiseringsuppgifter som används i lokal utveckling. Mer information finns i Autentisera Azure-hostade .NET-appar till Azure-resurser med en systemtilldelad hanterad identitet.

Föregående exempel innebär att det hanterade Identity klient-ID:t ({MANAGED IDENTITY CLIENT ID}), katalog-ID (klient)-ID ({TENANT ID}) och nyckelvalvs-URI ({VAULT URI}t.ex. https://contoso.vault.azure.net/, avslutande snedstreck krävs) tillhandahålls av hårdkodade värden. Alla eller alla dessa värden kan anges från konfigurationen av appinställningar. Följande hämtar till exempel valv-URI:n från AzureAd noden i en appinställningsfil och vaultUri kan användas i anropet till GetKeyVaultSecret i föregående exempel:

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

Mer information finns i ASP.NET Core Blazor configuration.

Implementera IEmailSender

Följande exempel baseras på Mailchimps transaktions-API med hjälp av Mandrill.net. För en annan leverantör, hänvisa till deras dokumentation om hur man implementerar att skicka ett e-postmeddelande.

Lägg till Mandrill.net NuGet-paketet i projektet.

Lägg till följande EmailSender-klass för att implementera IEmailSender<TUser>. I följande exempel är ApplicationUser en IdentityUser. Meddelandet HTML-kod kan anpassas ytterligare. Så länge message skickas till MandrillMessage börjar med <-tecknet förutsätter Mandrill.net-API:et att meddelandetexten består av 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);
    }
}

Obs

Brödtextinnehåll för meddelanden kan kräva särskild kodning för e-posttjänstleverantören. Om länkar i meddelandetexten inte kan följas i e-postmeddelandet läser du serviceleverantörens dokumentation för att felsöka problemet.

Konfigurera appen för att stödja e-post

I filen Program ändrar du implementeringen av e-postsändaren till EmailSender:

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

Ta bort IdentityNoOpEmailSender (Components/Account/IdentityNoOpEmailSender.cs) från appen.

I komponenten RegisterConfirmation (Components/Account/Pages/RegisterConfirmation.razor) tar du bort det villkorsstyrda blocket i @code-blocket som kontrollerar om EmailSender är en IdentityNoOpEmailSender:

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

Även i RegisterConfirmation-komponenten, ta bort Razor-markeringen och koden för att kontrollera emailConfirmationLink-fältet, lämna bara raden som instruerar användaren att kontrollera sin e-post ...

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

@code {
-    private string? emailConfirmationLink;

     ...
}

Aktivera kontobekräftelse när en webbplats har användare

Aktivering av kontobekräftelse på en webbplats med användare låser alla befintliga användare. Befintliga användare är utelåst eftersom deras konton inte är bekräftade. Använd någon av följande metoder för att kringgå befintlig användarutelåsning:

  • Uppdatera databasen för att markera alla befintliga användare som bekräftade.
  • Bekräfta befintliga användare. Till exempel batch-skicka e-postmeddelanden med bekräftelselänkar.

Tidsgräns för e-post och aktivitet

Standardtidsgränsen för inaktivitet är 14 dagar. Följande kod anger tidsgränsen för inaktivitet till fem dagar med glidande förfallotid:

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

Ändra alla ASP.NET Core Data Protection-tokenlivsintervall

Följande kod ändrar tidsgränsen för dataskyddstoken till tre timmar:

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

De inbyggda Identity användartoken (AspNetCore/src/Identity/Extensions.Core/src/TokenOptions.cs) har en tidsgräns på en dag.

Obs

Dokumentationslänkar till .NET-referenskällan läser vanligtvis in lagringsplatsens standardgren, vilket representerar den aktuella utvecklingen för nästa version av .NET. Om du vill välja en tagg för en specifik version använder du listrutan Växla grenar eller taggar. Mer information finns i Så här väljer du en versionstagg för ASP.NET Core-källkod (dotnet/AspNetCore.Docs #26205).

Ändra livslängden för e-posttoken

Standardtokens livslängd för Identity användartoken är en dag.

Obs

Dokumentationslänkar till .NET-referenskällan läser vanligtvis in lagringsplatsens standardgren, vilket representerar den aktuella utvecklingen för nästa version av .NET. Om du vill välja en tagg för en specifik version använder du listrutan Växla grenar eller taggar. Mer information finns i Så här väljer du en versionstagg för ASP.NET Core-källkod (dotnet/AspNetCore.Docs #26205).

Om du vill ändra livslängden för e-posttoken lägger du till en anpassad DataProtectorTokenProvider<TUser> och 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);
    }
}

Konfigurera tjänsterna så att de använder den anpassade tokenprovidern i filen Program:

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

Felsöka

Om du inte kan få e-post att fungera:

  • Ange en brytpunkt i EmailSender.Execute för att verifiera att SendEmailAsync anropas.
  • Skapa en konsolapp för att skicka e-post med kod som liknar EmailSender.Execute för att felsöka problemet.
  • Granska kontots e-posthistoriksidor på e-postleverantörens webbplats.
  • Sök efter meddelanden i skräppostmappen.
  • Prova ett annat e-postalias på en annan e-postleverantör, till exempel Microsoft, Yahoo eller Gmail.
  • Prova att skicka till olika e-postkonton.

Ytterligare resurser