ASP.NET Core Blazor 中的帐户确认和密码恢复
本文介绍如何为 ASP.NET Core Blazor Web 应用配置电子邮件确认和密码恢复。
命名空间
本文中示例使用的应用命名空间为 BlazorSample
。 更新这些代码示例以使用你的应用的命名空间。
选择并配置电子邮件提供程序
本文中使用 Mailchimp 的事务 API 通过 Mandrill.net 发送电子邮件。 建议使用电子邮件服务(而不是 SMTP)来发送电子邮件。 SMTP 难以正确配置和保护。 无论使用哪种电子邮件服务,都请访问其 .NET 应用指南、创建帐户、为其服务配置 API 密钥,并安装所需的任何 NuGet 包。
创建一个类以获取安全电子邮件 API 密钥。 本文中的示例将一个名为 AuthMessageSenderOptions
的类与一个 EmailAuthKey
属性配合使用,以便保存密钥。
AuthMessageSenderOptions
:
namespace BlazorSample;
public class AuthMessageSenderOptions
{
public string? EmailAuthKey { get; set; }
}
在 Program
文件中注册 AuthMessageSenderOptions
配置实例:
builder.Services.Configure<AuthMessageSenderOptions>(builder.Configuration);
为提供程序的安全密钥配置用户机密
使用机密管理器工具设置密钥。 在下面的示例中,密钥名称为 EmailAuthKey
,并且密钥由 {KEY}
占位符表示。 在命令行界面中,导航到应用的根文件夹,并使用 API 密钥执行以下命令:
dotnet user-secrets set "EmailAuthKey" "{KEY}"
有关详细信息,请参阅在 ASP.NET Core 开发中安全存储应用机密。
实现 IEmailSender
为提供程序实现 IEmailSender
。 以下示例基于 Mailchimp 的事务 API,该 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
组件 (Components/Account/Pages/RegisterConfirmation.razor
) 中,删除 @code
块中用于检查 EmailSender
是否是 IdentityNoOpEmailSender
的条件块:
- else if (EmailSender is IdentityNoOpEmailSender)
- {
- ...
- }
此外,在 RegisterConfirmation
组件中,删除用于检查 emailConfirmationLink
字段的 Razor 标记和代码,只留下指示用户检查其电子邮件的行。
- @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) 的超时为 1 天。
注意
指向 .NET 参考源的文档链接通常会加载存储库的默认分支,该分支表示针对下一个 .NET 版本的当前开发。 若要为特定版本选择标记,请使用“切换分支或标记”下拉列表。 有关详细信息,请参阅如何选择 ASP.NET Core 源代码的版本标记 (dotnet/AspNetCore.Docs #26205)。
更改电子邮件令牌的使用期限
Identity 用户令牌的默认令牌有效期是 1 天。
注意
指向 .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 应用门户中将机密设为应用程序设置。 配置系统设置为从环境变量读取密钥。
在站点具有用户后启用帐户确认
在具有用户的站点上启用帐户确认会锁定所有现有用户。 现有用户被锁定,因为未确认其帐户。 若要解决现有用户锁定问题,请使用以下方法之一:
- 更新数据库以将所有现有用户标记为已经过确认。
- 确认现有用户。 例如,批量发送包含确认链接的电子邮件。
其他资源
反馈
https://aka.ms/ContentUserFeedback。
即将发布:在整个 2024 年,我们将逐步淘汰作为内容反馈机制的“GitHub 问题”,并将其取代为新的反馈系统。 有关详细信息,请参阅:提交和查看相关反馈