ASP.NET Core 中的帳戶確認和密碼復原 Blazor

本文說明如何使用電子郵件確認和密碼復原來設定 ASP.NET Core Blazor Web 應用程式。

Namespace

本文範例所使用的應用程式命名空間為 BlazorSample。 更新程式代碼範例以使用您應用程式的命名空間。

選取並設定電子郵件提供者

在本文中, Mailchimp 的交易式 API 是透過 Mandrill.net 來傳送電子郵件。 我們建議使用電子郵件服務來傳送電子郵件,而不是 SMTP。 SMTP 難以正確設定及保護。 無論您使用哪一個電子郵件服務、存取其 .NET 應用程式的指引、建立帳戶、為其服務設定 API 金鑰,以及安裝所需的任何 NuGet 套件。

建立類別以擷取安全的電子郵件 API 金鑰。 本文中的範例會使用名為 AuthMessageSenderOptionsEmailAuthKey 類別搭配 屬性來保存索引鍵。

AuthMessageSenderOptions

namespace BlazorSample;

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

AuthMessageSenderOptions 檔案中 Program 註冊組態實例:

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

為提供者的安全性金鑰設定用戶密碼

使用 秘密管理員工具設定金鑰。 在下列範例中,索引鍵名稱為 EmailAuthKey ,而索引鍵則以 {KEY} 佔位元表示。 在命令殼層中,瀏覽至應用程式的根資料夾,並使用 API 金鑰執行下列命令:

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

如需詳細資訊,請參閱 ASP.NET Core 中開發中的應用程式密碼安全儲存

實作 IEmailSender

IEmailSender針對提供者實作 。 下列範例是以 Mailchimp 的交易式 API 為基礎,使用 Mandrill.net。 如需不同的提供者,請參閱其檔,以瞭解如何在方法中 Execute 實作傳送訊息。

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", 
        $"Please confirm your account by " +
        "<a href='{confirmationLink}'>clicking here</a>.");

    public Task SendPasswordResetLinkAsync(ApplicationUser user, string email, 
        string resetLink) => SendEmailAsync(email, "Reset your password", 
        $"Please reset your password by <a href='{resetLink}'>clicking here</a>.");

    public Task SendPasswordResetCodeAsync(ApplicationUser user, string email, 
        string resetCode) => SendEmailAsync(email, "Reset your password", 
        $"Please reset your password using the following code: {resetCode}");

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

注意

郵件的本文內容可能需要電子郵件服務提供者的特殊編碼方式。 如果無法遵循訊息本文中的連結,請參閱服務提供者的檔。

設定應用程式以支援電子郵件

在檔案中 Program ,將電子郵件寄件者實作變更為 EmailSender

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

IdentityNoOpEmailSender 從應用程式移除 (Components/Account/IdentityNoOpEmailSender.cs)。

在元件 () 中RegisterConfirmation,移除 區塊中的@code條件式區塊,以檢查 是否EmailSenderIdentityNoOpEmailSenderComponents/Account/Pages/RegisterConfirmation.razor

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

此外,在元件中 RegisterConfirmation ,移除 Razor 標記和程式代碼來檢查 emailConfirmationLink 字段,只留下一行指示使用者檢查其電子郵件...

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

@code {
-    private string? emailConfirmationLink;

     ...
}

電子郵件和活動逾時

預設無活動逾時為 14 天。 下列程式代碼會將無活動逾時設定為5天,並具有滑動到期日:

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

變更所有資料保護權杖生命週期

下列程式碼會將所有資料保護權杖逾時期間變更為 3 小時:

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

內 Identity 建的使用者令牌 (AspNetCore/src/Identity/Extensions.Core/src/TokenOptions.cs) 有一 天的逾時

注意

.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 如需詳細資訊,請參閱如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤

變更電子郵件權杖生命週期

使用者令牌的預設令牌存留期Identity為一天

注意

.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 如需詳細資訊,請參閱如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤

若要變更電子郵件權杖生命週期,請新增自訂 DataProtectorTokenProvider<TUser>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);
    }
}

設定服務以在檔案中使用 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>>();

疑難排解

如果您無法讓電子郵件運作:

  • EmailSender.Execute 中設定中斷點以確認 SendEmailAsync 已呼叫。
  • 建立主控台應用程式,以使用類似 EmailSender.Execute 對問題進行偵錯的程式代碼來傳送電子郵件。
  • 檢閱電子郵件提供者網站上的帳戶電子郵件記錄頁面。
  • 檢查您的垃圾郵件資料夾是否有郵件。
  • 在不同的電子郵件提供者上嘗試另一個電子郵件別名,例如 Microsoft、Yahoo 或 Gmail。
  • 請嘗試傳送至不同的電子郵件帳戶。

警告

請勿在測試和開發中使用生產秘密。 如果您將應用程式發佈至 Azure,請在 Azure Web 應用程式入口網站中將秘密設定為應用程式設定。 設定系統已設為從環境變數讀取金鑰。

在網站有使用者之後啟用帳戶確認

在有使用者的網站上啟用帳戶確認,會鎖定所有現有的使用者。 現有的使用者因為未確認其帳戶而遭到鎖定。 若要解決現有的使用者鎖定問題,請使用下列其中一種方法:

  • 更新資料庫,將所有現有的用戶標示為已確認。
  • 確認現有的使用者。 例如,批次傳送具有確認連結的電子郵件。

其他資源