帐户确认和 ASP.NET Core 中的密码恢复Account confirmation and password recovery in ASP.NET Core

作者: Rick AndersonPonantJoe AudetteBy Rick Anderson, Ponant, and Joe Audette

本教程介绍如何使用电子邮件确认和密码重置构建 ASP.NET Core 应用。This tutorial shows how to build an ASP.NET Core app with email confirmation and password reset. 本教程是开始主题。This tutorial is not a beginning topic. 您应熟悉:You should be familiar with:

有关 ASP.NET Core 1.1 版本,请参阅此 PDF 文件See this PDF file for the ASP.NET Core 1.1 version.

先决条件Prerequisites

.NET Core 3.0 SDK 或更高版本.NET Core 3.0 SDK or later

创建和测试使用身份验证的 web 应用Create and test a web app with authentication

运行以下命令,创建具有身份验证的 web 应用。Run the following commands to create a web app with authentication.

dotnet new webapp -au Individual -uld -o WebPWrecover
cd WebPWrecover
dotnet run

运行应用,选择 "注册" 链接,然后注册用户。Run the app, select the Register link, and register a user. 注册后,你会被重定向到/Identity/Account/RegisterConfirmation "目标" 页,其中包含用于模拟电子邮件确认的链接:Once registered, you are redirected to the to /Identity/Account/RegisterConfirmation page which contains a link to simulate email confirmation:

  • Click here to confirm your account选择链接。Select the Click here to confirm your account link.
  • 选择 "登录" 链接,并以相同的凭据登录。Select the Login link and sign-in with the same credentials.
  • 选择将Hello YourEmail@provider.com!您重定向/Identity/Account/Manage/PersonalData到页面的链接。Select the Hello YourEmail@provider.com! link, which redirects you to the /Identity/Account/Manage/PersonalData page.
  • 选择左侧的 "个人数据" 选项卡,然后选择 "删除"。Select the Personal data tab on the left, and then select Delete.

配置电子邮件提供程序Configure an email provider

在本教程中,使用SendGrid发送电子邮件。In this tutorial, SendGrid is used to send email. 需要使用 SendGrid 帐户和密钥来发送电子邮件。You need a SendGrid account and key to send email. 您可以使用其他电子邮件提供程序。You can use other email providers. 建议使用 SendGrid 或其他电子邮件服务发送电子邮件。We recommend you use SendGrid or another email service to send email. SMTP 难于保护和正确设置。SMTP is difficult to secure and set up correctly.

创建一个类以获取安全电子邮件密钥。Create a class to fetch the secure email key. 对于本示例,请创建服务/AuthMessageSenderOptionsFor this sample, create Services/AuthMessageSenderOptions.cs:

public class AuthMessageSenderOptions
{
    public string SendGridUser { get; set; }
    public string SendGridKey { get; set; }
}

配置 SendGrid 用户机密Configure SendGrid user secrets

用机密管理器工具设置SendGridKey 和。SendGridUserSet the SendGridUser and SendGridKey with the secret-manager tool. 例如:For example:

dotnet user-secrets set SendGridUser RickAndMSFT
dotnet user-secrets set SendGridKey <key>

Successfully saved SendGridUser = RickAndMSFT to the secret store.

在 Windows 上,机密管理器将密钥/值对存储在%APPDATA%/Microsoft/UserSecrets/<WebAppName-userSecretsId>目录中的一个 secret 文件中。On Windows, Secret Manager stores keys/value pairs in a secrets.json file in the %APPDATA%/Microsoft/UserSecrets/<WebAppName-userSecretsId> directory.

不会对机密 json文件的内容进行加密。The contents of the secrets.json file aren't encrypted. 以下标记显示了机密的 json文件。The following markup shows the secrets.json file. SendGridKey删除该值。The SendGridKey value has been removed.

{
  "SendGridUser": "RickAndMSFT",
  "SendGridKey": "<key removed>"
}

有关详细信息,请参阅Options 模式配置For more information, see the Options pattern and configuration.

安装 SendGridInstall SendGrid

本教程介绍如何通过SendGrid添加电子邮件通知,但你可以使用 SMTP 和其他机制发送电子邮件。This tutorial shows how to add email notifications through SendGrid, but you can send email using SMTP and other mechanisms.

SendGrid安装 NuGet 包:Install the SendGrid NuGet package:

在 "包管理器控制台" 中,输入以下命令:From the Package Manager Console, enter the following command:

Install-Package SendGrid

请参阅SendGrid 的入门免费版,注册免费 SendGrid 帐户。See Get Started with SendGrid for Free to register for a free SendGrid account.

实现 IEmailSenderImplement IEmailSender

若要IEmailSender实现,请创建具有类似于下面的代码的服务/EmailSenderTo Implement IEmailSender, create Services/EmailSender.cs with code similar to the following:

using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.Extensions.Options;
using SendGrid;
using SendGrid.Helpers.Mail;
using System.Threading.Tasks;

namespace WebPWrecover.Services
{
    public class EmailSender : IEmailSender
    {
        public EmailSender(IOptions<AuthMessageSenderOptions> optionsAccessor)
        {
            Options = optionsAccessor.Value;
        }

        public AuthMessageSenderOptions Options { get; } //set only via Secret Manager

        public Task SendEmailAsync(string email, string subject, string message)
        {
            return Execute(Options.SendGridKey, subject, message, email);
        }

        public Task Execute(string apiKey, string subject, string message, string email)
        {
            var client = new SendGridClient(apiKey);
            var msg = new SendGridMessage()
            {
                From = new EmailAddress("Joe@contoso.com", Options.SendGridUser),
                Subject = subject,
                PlainTextContent = message,
                HtmlContent = message
            };
            msg.AddTo(new EmailAddress(email));

            // Disable click tracking.
            // See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
            msg.SetClickTracking(false, false);

            return client.SendEmailAsync(msg);
        }
    }
}

配置启动以支持电子邮件Configure startup to support email

将以下代码添加到ConfigureServices Startup.cs文件中的方法:Add the following code to the ConfigureServices method in the Startup.cs file:

  • 添加EmailSender为暂时性服务。Add EmailSender as a transient service.
  • AuthMessageSenderOptions注册配置实例。Register the AuthMessageSenderOptions configuration instance.
public void ConfigureServices(IServiceCollection services)
{

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(
                  options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();

    // requires
    // using Microsoft.AspNetCore.Identity.UI.Services;
    // using WebPWrecover.Services;
    services.AddTransient<IEmailSender, EmailSender>();
    services.Configure<AuthMessageSenderOptions>(Configuration);

    services.AddRazorPages();
}

注册、确认电子邮件并重置密码Register, confirm email, and reset password

运行 web 应用,并测试帐户确认和密码恢复流。Run the web app, and test the account confirmation and password recovery flow.

  • 运行应用并注册一个新用户Run the app and register a new user
  • 检查电子邮件中的 "帐户确认" 链接。Check your email for the account confirmation link. 如果没有收到电子邮件,请参阅调试电子邮件See Debug email if you don't get the email.
  • 单击链接以确认你的电子邮件。Click the link to confirm your email.
  • 用电子邮件和密码登录。Sign in with your email and password.
  • 注销。Sign out.

测试密码重置Test password reset

  • 如果已登录,请选择 "注销"。If you're signed in, select Logout.
  • 选择 "登录" 链接,然后选择 "忘记了密码?" 链接。Select the Log in link and select the Forgot your password? link.
  • 输入用于注册该帐户的电子邮件。Enter the email you used to register the account.
  • 发送了一封电子邮件,其中包含用于重置密码的链接。An email with a link to reset your password is sent. 检查你的电子邮件,然后单击链接以重置你的密码。Check your email and click the link to reset your password. 密码重置成功后,可以用电子邮件和新密码登录。After your password has been successfully reset, you can sign in with your email and new password.

更改电子邮件和活动超时Change email and activity timeout

默认的非活动超时为14天。The default inactivity timeout is 14 days. 下面的代码将非活动超时设置为5天:The following code sets the inactivity timeout to 5 days:

services.ConfigureApplicationCookie(o => {
    o.ExpireTimeSpan = TimeSpan.FromDays(5);
    o.SlidingExpiration = true;
});

更改所有数据保护令牌 lifespansChange all data protection token lifespans

以下代码将所有数据保护令牌超时期限更改为3小时:The following code changes all data protection tokens timeout period to 3 hours:

public void ConfigureServices(IServiceCollection services)
{

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(
                  options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();

    services.Configure<DataProtectionTokenProviderOptions>(o =>
       o.TokenLifespan = TimeSpan.FromHours(3));

    services.AddTransient<IEmailSender, EmailSender>();
    services.Configure<AuthMessageSenderOptions>(Configuration);

    services.AddRazorPages();
}

内置标识用户令牌(请参阅AspNetCore/src/Identity/extension/src/src/TokenOptions )具有一天的超时时间The built in Identity user tokens (see AspNetCore/src/Identity/Extensions.Core/src/TokenOptions.cs )have a one day timeout.

更改电子邮件令牌的生命周期Change the email token lifespan

标识用户令牌的默认令牌生存期为1 天The default token lifespan of the Identity user tokens is one day. 本部分介绍如何更改电子邮件令牌的生命周期。This section shows how to change the email token lifespan.

添加自定义DataProtectorTokenProvider<TUser >DataProtectionTokenProviderOptionsAdd a custom DataProtectorTokenProvider<TUser> and DataProtectionTokenProviderOptions:

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

将自定义提供程序添加到服务容器:Add the custom provider to the service container:

public void ConfigureServices(IServiceCollection services)
{

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(config =>
    {
        config.SignIn.RequireConfirmedEmail = true;
        config.Tokens.ProviderMap.Add("CustomEmailConfirmation",
            new TokenProviderDescriptor(
                typeof(CustomEmailConfirmationTokenProvider<IdentityUser>)));
        config.Tokens.EmailConfirmationTokenProvider = "CustomEmailConfirmation";
      }).AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddTransient<CustomEmailConfirmationTokenProvider<IdentityUser>>();

    services.AddTransient<IEmailSender, EmailSender>();
    services.Configure<AuthMessageSenderOptions>(Configuration);

    services.AddRazorPages();
}

重新发送电子邮件确认Resend email confirmation

请参阅此 GitHub 问题See this GitHub issue.

调试电子邮件Debug email

如果无法使用电子邮件:If you can't get email working:

  • 在中EmailSender.Execute设置一个断点, SendGridClient.SendEmailAsync以验证调用。Set a breakpoint in EmailSender.Execute to verify SendGridClient.SendEmailAsync is called.
  • 创建一个控制台应用,用于使用类似的代码向EmailSender.Execute发送电子邮件。Create a console app to send email using similar code to EmailSender.Execute.
  • 查看电子邮件活动页。Review the Email Activity page.
  • 检查垃圾邮件文件夹。Check your spam folder.
  • 尝试使用其他电子邮件提供商(Microsoft、Yahoo、Gmail 等)中的另一个电子邮件别名Try another email alias on a different email provider (Microsoft, Yahoo, Gmail, etc.)
  • 尝试发送到不同的电子邮件帐户。Try sending to different email accounts.

最佳安全做法在测试和开发中使用生产机密。A security best practice is to not use production secrets in test and development. 如果将应用发布到 Azure,请在 Azure Web 应用门户中将 "SendGrid 机密" 设置为 "应用程序设置"。If you publish the app to Azure, set the SendGrid secrets as application settings in the Azure Web App portal. 配置系统设置以从环境变量读取密钥。The configuration system is set up to read keys from environment variables.

合并社会和本地登录帐户Combine social and local login accounts

若要完成本部分,必须首先启用外部身份验证提供程序。To complete this section, you must first enable an external authentication provider. 请参阅Facebook、Google 和外部提供程序身份验证See Facebook, Google, and external provider authentication.

可以通过单击电子邮件链接来合并本地帐户和社交帐户。You can combine local and social accounts by clicking on your email link. 按照以下顺序,"RickAndMSFT@gmail.com" 首先创建为本地登录名; 但是,你可以先将该帐户创建为社交登录名,然后添加本地登录名。In the following sequence, "RickAndMSFT@gmail.com" is first created as a local login; however, you can create the account as a social login first, then add a local login.

Web 应用程序RickAndMSFT@gmail.com :用户已进行身份验证

单击 "管理" 链接。Click on the Manage link. 请注意与此帐户关联的0个外部(社交登录)。Note the 0 external (social logins) associated with this account.

管理视图

单击指向另一登录服务的链接,并接受应用请求。Click the link to another login service and accept the app requests. 在下图中,Facebook 是外部身份验证提供程序:In the following image, Facebook is the external authentication provider:

管理你的外部登录名视图列表 Facebook

这两个帐户已组合在一起。The two accounts have been combined. 你可以用任一帐户登录。You are able to sign in with either account. 你可能希望用户在社交登录身份验证服务关闭时添加本地帐户,或者更可能的情况是他们失去了社交帐户的访问权限。You might want your users to add local accounts in case their social login authentication service is down, or more likely they've lost access to their social account.

在站点包含用户后启用帐户确认Enable account confirmation after a site has users

在具有用户的站点上启用帐户确认会锁定所有现有用户。Enabling account confirmation on a site with users locks out all the existing users. 现有用户被锁定,因为其帐户未得到确认。Existing users are locked out because their accounts aren't confirmed. 若要解决现有用户锁定,请使用以下方法之一:To work around existing user lockout, use one of the following approaches:

  • 更新数据库,将所有现有用户标记为已确认。Update the database to mark all existing users as being confirmed.
  • 确认现有用户。Confirm existing users. 例如,批处理-发送包含确认链接的电子邮件。For example, batch-send emails with confirmation links.

先决条件Prerequisites

.NET Core 2.2 SDK 或更高版本.NET Core 2.2 SDK or later

创建 web 应用和基架标识Create a web app and scaffold Identity

运行以下命令,创建具有身份验证的 web 应用。Run the following commands to create a web app with authentication.

dotnet new webapp -au Individual -uld -o WebPWrecover
cd WebPWrecover
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet tool install -g dotnet-aspnet-codegenerator
dotnet aspnet-codegenerator identity -dc WebPWrecover.Data.ApplicationDbContext --files "Account.Register;Account.Login;Account.Logout;Account.ConfirmEmail"
dotnet ef database drop -f
dotnet ef database update
dotnet run

测试新用户注册Test new user registration

运行应用,选择 "注册" 链接,然后注册用户。Run the app, select the Register link, and register a user. 此时,电子邮件的唯一验证是带有[EmailAddress]属性。At this point, the only validation on the email is with the [EmailAddress] attribute. 提交注册后,将登录到应用。After submitting the registration, you are logged into the app. 在本教程的后面部分,将更新代码,以便新用户在验证其电子邮件之前无法登录。Later in the tutorial, the code is updated so new users can't sign in until their email is validated.

查看标识数据库View the Identity database

  • 视图菜单中,选择SQL Server 对象资源管理器(SSOX)。From the View menu, select SQL Server Object Explorer (SSOX).
  • 导航到 (localdb) MSSQLLocalDB (SQL Server 13)Navigate to (localdb)MSSQLLocalDB(SQL Server 13). 右键单击dbo。AspNetUsers > 查看数据:Right-click on dbo.AspNetUsers > View Data:

在 AspNetUsers 表中 SQL Server 对象资源管理器的上下文菜单

请注意,表EmailConfirmed的字段False为。Note the table's EmailConfirmed field is False.

当应用发送确认电子邮件时,可能需要在下一步中再次使用此电子邮件。You might want to use this email again in the next step when the app sends a confirmation email. 右键单击该行,然后选择 "删除"。Right-click on the row and select Delete. 删除电子邮件别名可以简化以下步骤。Deleting the email alias makes it easier in the following steps.

需要确认电子邮件Require email confirmation

最佳做法是确认新用户注册的电子邮件。It's a best practice to confirm the email of a new user registration. 电子邮件确认有助于验证他们是否未模拟其他人(即,他们未注册其他人的电子邮件)。Email confirmation helps to verify they're not impersonating someone else (that is, they haven't registered with someone else's email). 假设你有讨论论坛,并且想要阻止 "yli@example.com" 注册为 "nolivetto@contoso.com"。Suppose you had a discussion forum, and you wanted to prevent "yli@example.com" from registering as "nolivetto@contoso.com". 如果未确认电子邮件nolivetto@contoso.com,"" 可能会从你的应用收到不需要的电子邮件。Without email confirmation, "nolivetto@contoso.com" could receive unwanted email from your app. 假设用户意外注册为 "ylo@example.com",但未注意到 "yli" 的拼写错误。Suppose the user accidentally registered as "ylo@example.com" and hadn't noticed the misspelling of "yli". 它们不能使用密码恢复,因为该应用没有正确的电子邮件。They wouldn't be able to use password recovery because the app doesn't have their correct email. 电子邮件确认为 bot 提供有限的保护。Email confirmation provides limited protection from bots. 电子邮件确认不会为具有多个电子邮件帐户的恶意用户提供保护。Email confirmation doesn't provide protection from malicious users with many email accounts.

通常,在用户确认电子邮件之前,会阻止新用户将任何数据发布到您的网站。You generally want to prevent new users from posting any data to your web site before they have a confirmed email.

更新Startup.ConfigureServices以要求确认电子邮件:Update Startup.ConfigureServices to require a confirmed email:

public void ConfigureServices(IServiceCollection services)
{

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));

    services.AddDefaultIdentity<IdentityUser>(config =>
    {
        config.SignIn.RequireConfirmedEmail = true;
    })
        .AddDefaultUI(UIFramework.Bootstrap4)
        .AddEntityFrameworkStores<ApplicationDbContext>();

    // requires
    // using Microsoft.AspNetCore.Identity.UI.Services;
    // using WebPWrecover.Services;
    services.AddTransient<IEmailSender, EmailSender>();
    services.Configure<AuthMessageSenderOptions>(Configuration);

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

config.SignIn.RequireConfirmedEmail = true;阻止注册的用户登录,直到其电子邮件得到确认。config.SignIn.RequireConfirmedEmail = true; prevents registered users from logging in until their email is confirmed.

配置电子邮件提供程序Configure email provider

在本教程中,使用SendGrid发送电子邮件。In this tutorial, SendGrid is used to send email. 需要使用 SendGrid 帐户和密钥来发送电子邮件。You need a SendGrid account and key to send email. 您可以使用其他电子邮件提供程序。You can use other email providers. ASP.NET Core 2.x 包括System.Net.Mail,这允许你从你的应用程序发送电子邮件。ASP.NET Core 2.x includes System.Net.Mail, which allows you to send email from your app. 建议使用 SendGrid 或其他电子邮件服务发送电子邮件。We recommend you use SendGrid or another email service to send email. SMTP 难于保护和正确设置。SMTP is difficult to secure and set up correctly.

创建一个类以获取安全电子邮件密钥。Create a class to fetch the secure email key. 对于本示例,请创建服务/AuthMessageSenderOptionsFor this sample, create Services/AuthMessageSenderOptions.cs:

public class AuthMessageSenderOptions
{
    public string SendGridUser { get; set; }
    public string SendGridKey { get; set; }
}

配置 SendGrid 用户机密Configure SendGrid user secrets

用机密管理器工具设置SendGridKey 和。SendGridUserSet the SendGridUser and SendGridKey with the secret-manager tool. 例如:For example:

C:/WebAppl>dotnet user-secrets set SendGridUser RickAndMSFT
info: Successfully saved SendGridUser = RickAndMSFT to the secret store.

在 Windows 上,机密管理器将密钥/值对存储在%APPDATA%/Microsoft/UserSecrets/<WebAppName-userSecretsId>目录中的一个 secret 文件中。On Windows, Secret Manager stores keys/value pairs in a secrets.json file in the %APPDATA%/Microsoft/UserSecrets/<WebAppName-userSecretsId> directory.

不会对机密 json文件的内容进行加密。The contents of the secrets.json file aren't encrypted. 以下标记显示了机密的 json文件。The following markup shows the secrets.json file. SendGridKey删除该值。The SendGridKey value has been removed.

{
  "SendGridUser": "RickAndMSFT",
  "SendGridKey": "<key removed>"
}

有关详细信息,请参阅Options 模式配置For more information, see the Options pattern and configuration.

安装 SendGridInstall SendGrid

本教程介绍如何通过SendGrid添加电子邮件通知,但你可以使用 SMTP 和其他机制发送电子邮件。This tutorial shows how to add email notifications through SendGrid, but you can send email using SMTP and other mechanisms.

SendGrid安装 NuGet 包:Install the SendGrid NuGet package:

在 "包管理器控制台" 中,输入以下命令:From the Package Manager Console, enter the following command:

Install-Package SendGrid

请参阅SendGrid 的入门免费版,注册免费 SendGrid 帐户。See Get Started with SendGrid for Free to register for a free SendGrid account.

实现 IEmailSenderImplement IEmailSender

若要IEmailSender实现,请创建具有类似于下面的代码的服务/EmailSenderTo Implement IEmailSender, create Services/EmailSender.cs with code similar to the following:

using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.Extensions.Options;
using SendGrid;
using SendGrid.Helpers.Mail;
using System.Threading.Tasks;

namespace WebPWrecover.Services
{
    public class EmailSender : IEmailSender
    {
        public EmailSender(IOptions<AuthMessageSenderOptions> optionsAccessor)
        {
            Options = optionsAccessor.Value;
        }

        public AuthMessageSenderOptions Options { get; } //set only via Secret Manager

        public Task SendEmailAsync(string email, string subject, string message)
        {
            return Execute(Options.SendGridKey, subject, message, email);
        }

        public Task Execute(string apiKey, string subject, string message, string email)
        {
            var client = new SendGridClient(apiKey);
            var msg = new SendGridMessage()
            {
                From = new EmailAddress("Joe@contoso.com", "Joe Smith"),
                Subject = subject,
                PlainTextContent = message,
                HtmlContent = message
            };
            msg.AddTo(new EmailAddress(email));

            // Disable click tracking.
            // See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
            msg.SetClickTracking(false, false);

            return client.SendEmailAsync(msg);
        }
    }
}

配置启动以支持电子邮件Configure startup to support email

将以下代码添加到ConfigureServices Startup.cs文件中的方法:Add the following code to the ConfigureServices method in the Startup.cs file:

  • 添加EmailSender为暂时性服务。Add EmailSender as a transient service.
  • AuthMessageSenderOptions注册配置实例。Register the AuthMessageSenderOptions configuration instance.
public void ConfigureServices(IServiceCollection services)
{

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));

    services.AddDefaultIdentity<IdentityUser>(config =>
    {
        config.SignIn.RequireConfirmedEmail = true;
    })
        .AddDefaultUI(UIFramework.Bootstrap4)
        .AddEntityFrameworkStores<ApplicationDbContext>();

    // requires
    // using Microsoft.AspNetCore.Identity.UI.Services;
    // using WebPWrecover.Services;
    services.AddTransient<IEmailSender, EmailSender>();
    services.Configure<AuthMessageSenderOptions>(Configuration);

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

启用帐户确认和密码恢复Enable account confirmation and password recovery

该模板包含用于帐户确认和密码恢复的代码。The template has the code for account confirmation and password recovery. 区域/标识/页/帐户/注册. .cs中查找方法。OnPostAsyncFind the OnPostAsync method in Areas/Identity/Pages/Account/Register.cshtml.cs.

通过注释掉以下行,阻止新注册的用户自动登录:Prevent newly registered users from being automatically signed in by commenting out the following line:

await _signInManager.SignInAsync(user, isPersistent: false);

将显示完整的方法,其中突出显示了已更改的行:The complete method is shown with the changed line highlighted:

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    if (ModelState.IsValid)
    {
        var user = new IdentityUser { UserName = Input.Email, Email = Input.Email };
        var result = await _userManager.CreateAsync(user, Input.Password);
        if (result.Succeeded)
        {
            _logger.LogInformation("User created a new account with password.");

            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            var callbackUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { userId = user.Id, code = code },
                protocol: Request.Scheme);

            await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

            //await _signInManager.SignInAsync(user, isPersistent: false);
            return LocalRedirect(returnUrl);
        }
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

注册、确认电子邮件并重置密码Register, confirm email, and reset password

运行 web 应用,并测试帐户确认和密码恢复流。Run the web app, and test the account confirmation and password recovery flow.

  • 运行应用并注册一个新用户Run the app and register a new user
  • 检查电子邮件中的 "帐户确认" 链接。Check your email for the account confirmation link. 如果没有收到电子邮件,请参阅调试电子邮件See Debug email if you don't get the email.
  • 单击链接以确认你的电子邮件。Click the link to confirm your email.
  • 用电子邮件和密码登录。Sign in with your email and password.
  • 注销。Sign out.

查看 "管理" 页View the manage page

在浏览器中选择你的用户名,并在浏览器窗口中选择用户名Select your user name in the browser: browser window with user name

将显示 "管理" 页,并选中 "配置文件" 选项卡。The manage page is displayed with the Profile tab selected. 电子邮件将显示一个复选框,指示已确认电子邮件。The Email shows a check box indicating the email has been confirmed.

测试密码重置Test password reset

  • 如果已登录,请选择 "注销"。If you're signed in, select Logout.
  • 选择 "登录" 链接,然后选择 "忘记了密码?" 链接。Select the Log in link and select the Forgot your password? link.
  • 输入用于注册该帐户的电子邮件。Enter the email you used to register the account.
  • 发送了一封电子邮件,其中包含用于重置密码的链接。An email with a link to reset your password is sent. 检查你的电子邮件,然后单击链接以重置你的密码。Check your email and click the link to reset your password. 密码重置成功后,可以用电子邮件和新密码登录。After your password has been successfully reset, you can sign in with your email and new password.

更改电子邮件和活动超时Change email and activity timeout

默认的非活动超时为14天。The default inactivity timeout is 14 days. 下面的代码将非活动超时设置为5天:The following code sets the inactivity timeout to 5 days:

services.ConfigureApplicationCookie(o => {
    o.ExpireTimeSpan = TimeSpan.FromDays(5);
    o.SlidingExpiration = true;
});

更改所有数据保护令牌 lifespansChange all data protection token lifespans

以下代码将所有数据保护令牌超时期限更改为3小时:The following code changes all data protection tokens timeout period to 3 hours:

public void ConfigureServices(IServiceCollection services)
{

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));

    services.AddDefaultIdentity<IdentityUser>(config =>
    {
        config.SignIn.RequireConfirmedEmail = true;
    })
        .AddDefaultUI(UIFramework.Bootstrap4)
        .AddEntityFrameworkStores<ApplicationDbContext>();

    services.Configure<DataProtectionTokenProviderOptions>(o =>
                o.TokenLifespan = TimeSpan.FromHours(3));

    services.AddTransient<IEmailSender, EmailSender>();
    services.Configure<AuthMessageSenderOptions>(Configuration);

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

内置标识用户令牌(请参阅AspNetCore/src/Identity/extension/src/src/TokenOptions )具有一天的超时时间The built in Identity user tokens (see AspNetCore/src/Identity/Extensions.Core/src/TokenOptions.cs )have a one day timeout.

更改电子邮件令牌的生命周期Change the email token lifespan

标识用户令牌的默认令牌生存期为1 天The default token lifespan of the Identity user tokens is one day. 本部分介绍如何更改电子邮件令牌的生命周期。This section shows how to change the email token lifespan.

添加自定义DataProtectorTokenProvider<TUser >DataProtectionTokenProviderOptionsAdd a custom DataProtectorTokenProvider<TUser> and DataProtectionTokenProviderOptions:

public class CustomEmailConfirmationTokenProvider<TUser> 
                                       : DataProtectorTokenProvider<TUser> where TUser : class
{
    public CustomEmailConfirmationTokenProvider(IDataProtectionProvider dataProtectionProvider,
        IOptions<EmailConfirmationTokenProviderOptions> options) 
                                                        : base(dataProtectionProvider, options)
    {

    }
}
public class EmailConfirmationTokenProviderOptions : DataProtectionTokenProviderOptions
{
    public EmailConfirmationTokenProviderOptions()
    {
        Name = "EmailDataProtectorTokenProvider";
        TokenLifespan = TimeSpan.FromHours(4);
    }
}

将自定义提供程序添加到服务容器:Add the custom provider to the service container:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));

    services.AddDefaultIdentity<IdentityUser>(config =>
    {
        config.SignIn.RequireConfirmedEmail = true;
        config.Tokens.ProviderMap.Add("CustomEmailConfirmation",
            new TokenProviderDescriptor(
                typeof(CustomEmailConfirmationTokenProvider<IdentityUser>)));
        config.Tokens.EmailConfirmationTokenProvider = "CustomEmailConfirmation";                
    })
        .AddDefaultUI(UIFramework.Bootstrap4)
        .AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddTransient<CustomEmailConfirmationTokenProvider<IdentityUser>>();
    services.AddTransient<IEmailSender, EmailSender>();            
    services.Configure<AuthMessageSenderOptions>(Configuration); // For SendGrid key.

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

重新发送电子邮件确认Resend email confirmation

请参阅此 GitHub 问题See this GitHub issue.

调试电子邮件Debug email

如果无法使用电子邮件:If you can't get email working:

  • 在中EmailSender.Execute设置一个断点, SendGridClient.SendEmailAsync以验证调用。Set a breakpoint in EmailSender.Execute to verify SendGridClient.SendEmailAsync is called.
  • 创建一个控制台应用,用于使用类似的代码向EmailSender.Execute发送电子邮件。Create a console app to send email using similar code to EmailSender.Execute.
  • 查看电子邮件活动页。Review the Email Activity page.
  • 检查垃圾邮件文件夹。Check your spam folder.
  • 尝试使用其他电子邮件提供商(Microsoft、Yahoo、Gmail 等)中的另一个电子邮件别名Try another email alias on a different email provider (Microsoft, Yahoo, Gmail, etc.)
  • 尝试发送到不同的电子邮件帐户。Try sending to different email accounts.

最佳安全做法在测试和开发中使用生产机密。A security best practice is to not use production secrets in test and development. 如果将应用发布到 Azure,则可以在 Azure Web 应用门户中将 SendGrid 机密设置为应用程序设置。If you publish the app to Azure, you can set the SendGrid secrets as application settings in the Azure Web App portal. 配置系统设置以从环境变量读取密钥。The configuration system is set up to read keys from environment variables.

合并社会和本地登录帐户Combine social and local login accounts

若要完成本部分,必须首先启用外部身份验证提供程序。To complete this section, you must first enable an external authentication provider. 请参阅Facebook、Google 和外部提供程序身份验证See Facebook, Google, and external provider authentication.

可以通过单击电子邮件链接来合并本地帐户和社交帐户。You can combine local and social accounts by clicking on your email link. 按照以下顺序,"RickAndMSFT@gmail.com" 首先创建为本地登录名; 但是,你可以先将该帐户创建为社交登录名,然后添加本地登录名。In the following sequence, "RickAndMSFT@gmail.com" is first created as a local login; however, you can create the account as a social login first, then add a local login.

Web 应用程序RickAndMSFT@gmail.com :用户已进行身份验证

单击 "管理" 链接。Click on the Manage link. 请注意与此帐户关联的0个外部(社交登录)。Note the 0 external (social logins) associated with this account.

管理视图

单击指向另一登录服务的链接,并接受应用请求。Click the link to another login service and accept the app requests. 在下图中,Facebook 是外部身份验证提供程序:In the following image, Facebook is the external authentication provider:

管理你的外部登录名视图列表 Facebook

这两个帐户已组合在一起。The two accounts have been combined. 你可以用任一帐户登录。You are able to sign in with either account. 你可能希望用户在社交登录身份验证服务关闭时添加本地帐户,或者更可能的情况是他们失去了社交帐户的访问权限。You might want your users to add local accounts in case their social login authentication service is down, or more likely they've lost access to their social account.

在站点包含用户后启用帐户确认Enable account confirmation after a site has users

在具有用户的站点上启用帐户确认会锁定所有现有用户。Enabling account confirmation on a site with users locks out all the existing users. 现有用户被锁定,因为其帐户未得到确认。Existing users are locked out because their accounts aren't confirmed. 若要解决现有用户锁定,请使用以下方法之一:To work around existing user lockout, use one of the following approaches:

  • 更新数据库,将所有现有用户标记为已确认。Update the database to mark all existing users as being confirmed.
  • 确认现有用户。Confirm existing users. 例如,批处理-发送包含确认链接的电子邮件。For example, batch-send emails with confirmation links.