共用方式為


ASP.NET Core 中的帳戶確認與密碼重設 Blazor

注意

這不是這篇文章的最新版本。 如需目前的版本,請參閱 本文的 .NET 9 版本。

重要

這些發行前產品的相關資訊在產品正式發行前可能會有大幅修改。 Microsoft 對此處提供的資訊,不做任何明確或隱含的瑕疵擔保。

如需目前的版本,請參閱 本文的 .NET 9 版本。

本文說明如何使用電子郵件確認與密碼重設來設定 ASP.NET Core Blazor Web App。

注意

本文僅適用於 Blazor Web Apps。 若要使用 ASP.NET Core 為獨立Blazor WebAssembly應用程式實作電子郵件確認和密碼復原,請參閱使用 ASP.NET Core ASP.NET CoreBlazor WebAssembly Identity中的帳戶確認和密碼復原。Identity

Namespace

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

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

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

建立類別來保存秘密電子郵件提供者 API 金鑰。 本文中的範例會使用名為 AuthMessageSenderOptionsEmailAuthKey 類別搭配 屬性來保存索引鍵。

AuthMessageSenderOptions.cs

namespace BlazorSample;

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

Program 檔案註冊 AuthMessageSenderOptions 設定執行個體:

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

為提供者的安全性金鑰設定使用者祕密

如果項目已經針對秘密管理員工具初始化,它就會在其項目檔中已經有應用程式秘密識別碼 (<AppSecretsId>.csproj) 。 在 Visual Studio 中,您可以在 方案總管選取專案時查看 [屬性] 面板,來判斷應用程式秘密標識碼是否存在。 如果應用程式尚未初始化,請在開啟至專案目錄的命令殼層中執行下列命令。 在 Visual Studio 中,您可以使用開發人員 PowerShell 命令提示字元。

dotnet user-secrets init

使用秘密管理員工具設定 API 金鑰。 在下列範例中,索引鍵名稱會 EmailAuthKey 比對 AuthMessageSenderOptions.EmailAuthKey,而索引鍵則以 {KEY} 佔位元表示。 使用 API 金鑰執行下列命令:

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

如果使用 Visual Studio,您可以用滑鼠右鍵按兩下 方案總管 中的伺服器專案,然後選取 [管理用戶密碼],以確認密碼已設定。

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

警告

請勿在用戶端程式代碼中儲存應用程式密碼、連接字串、認證、密碼、個人標識元(PIN)、私人 C#/.NET 程式代碼或私鑰/令牌,這一律不安全。 在測試/預備和生產環境中,伺服器端 Blazor 程序代碼和 Web API 應該使用安全驗證流程,以避免在專案程式代碼或組態檔內維護認證。 在本機開發測試之外,建議您避免使用環境變數來儲存敏感數據,因為環境變數不是最安全的方法。 針對本機開發測試, 建議使用秘密管理員工具 來保護敏感數據。 如需詳細資訊,請參閱 安全地維護敏感數據和認證

實作 IEmailSender

下列範例是以 Mailchimp 的交易式 API 為基礎,使用 Mandrill.net。 對於不同的提供者,請參閱其如何實作傳送電子郵件訊息的檔。

Mandrill.net NuGet 套件新增至專案。

新增下列 EmailSender 類別以實作 IEmailSender<TUser>。 在下列範例中, ApplicationUserIdentityUser。 您可以進一步自訂訊息 HTML 標記。 只要 message 傳遞至 以 MandrillMessage 字元開頭 < ,Mandrill.net API 就會假設訊息本文是以 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);
    }
}

注意

郵件內文內容可能需要電子郵件服務提供者的特殊編碼方式。 如果郵件本文中的鏈接無法在電子郵件訊息中追蹤,請參閱服務提供者的檔,以針對問題進行疑難解答。

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

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

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

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

RegisterConfirmation 元件 (Components/Account/Pages/RegisterConfirmation.razor)中,移除 @code 區塊中用於檢查 EmailSender 是否為 IdentityNoOpEmailSender有條件的區塊:

- 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 天。 下列程式碼會將無活動逾時設定為五天,並可滑動過期:

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

變更所有 ASP.NET Core 資料保護權杖生命週期

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

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 等。
  • 請嘗試傳送至不同的電子郵件帳戶。

其他資源