ASP.NET Core 中的帳戶確認與密碼重設 Blazor
注意
這不是這篇文章的最新版本。 如需目前的版本,請參閱 本文的 .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 金鑰。 本文中的範例會使用名為 AuthMessageSenderOptions
的 EmailAuthKey
類別搭配 屬性來保存索引鍵。
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>。 在下列範例中, ApplicationUser
是 IdentityUser。 您可以進一步自訂訊息 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 等。
- 請嘗試傳送至不同的電子郵件帳戶。