在 ASP.NET Core 中使用短信进行双重身份验证

作者: Rick AndersonSwiss-Devs

警告

使用基于时间的一次性密码算法(TOTP)的双重身份验证(2FA)验证器应用是 2FA 的行业推荐方法。 相较于 SMS 2FA,更推荐使用 TOTP 的 2FA。 有关详细信息,请参阅 ASP.NET Core for ASP.NET Core 2.0 或更高版本中为 TOTP 验证器应用启用 QR 码生成

本教程演示如何使用短信设置双因素身份验证(2FA)。 说明适用于 twilio 和 ASPSMS(https://www.aspsms.com/asp.net/identity/core/testcredits/),但你可以使用任何其他 SMS 提供程序。 建议在开始本教程之前完成 帐户确认和密码恢复

查看或下载示例代码如何下载

创建新的 ASP.NET Core 项目

创建一个新的 ASP.NET 核心 Web 应用,其中包含 Web2FA 单个帐户。 按照ASP.NET Core 中的强制 HTTPS中的说明进行设置,并强制使用 HTTPS。

创建短信帐户

创建短信帐户,例如来自twilio或ASPSMS(https://www.aspsms.com/asp.net/identity/core/testcredits/)。 记录身份验证凭据(对于 twilio:accountSid 和 authToken,对于 ASPSMS:Userkey 和 Password)。

找出 SMS 提供程序凭据

Twilio:

在 Twilio 帐户的“仪表板”选项卡中,复制 帐户 SID身份验证令牌

ASPSMS:

在帐户设置中,导航到 Userkey 并将其与 密码一起复制。

稍后,我们将这些值与密钥SMSAccountIdentificationSMSAccountPassword中的机密管理器工具一起存储。

指定发送者身份/发件人

Twilio: 在“号码”选项卡中,复制 Twilio 电话号码

ASPSMS: 在“解锁发起方”菜单中,解锁一个或多个发起方或选择一个字母数字发起方(并非所有网络支持)。

稍后,我们将使用密钥 SMSAccountFrom中的机密管理器工具存储此值。

请提供短信服务的凭据

我们将使用 “选项”模式 访问用户帐户和密钥设置。

  • 创建一个类来提取安全短信密钥。 对于此示例,将在 SMSoptions 文件中创建 Services/SMSoptions.cs 类。
namespace Web2FA.Services
{
    public class SMSoptions
    {
        public string SMSAccountIdentification { get; set; }
        public string SMSAccountPassword { get; set; }
        public string SMSAccountFrom { get; set; }
    }
}

设置 SMSAccountIdentificationSMSAccountPasswordSMSAccountFrom,并使用 机密管理器工具。 例如:

C:/Web2FA/src/WebApp1>dotnet user-secrets set SMSAccountIdentification 12345
info: Successfully saved SMSAccountIdentification = 12345 to the secret store.
  • 为 SMS 提供程序添加 NuGet 包。 请在软件包管理控制台(PMC)中运行:

Twilio:

Install-Package Twilio

ASPSMS:

Install-Package ASPSMS

  • Services/MessageServices.cs 文件中添加代码以启用短信。 使用 Twilio 或 ASPSMS 的对应部分:

Twilio:

using Microsoft.Extensions.Options;
using System.Threading.Tasks;
using Twilio;
using Twilio.Rest.Api.V2010.Account;
using Twilio.Types;

namespace Web2FA.Services
{
    // This class is used by the application to send Email and SMS
    // when you turn on two-factor authentication in ASP.NET Identity.
    // For more details see this link https://go.microsoft.com/fwlink/?LinkID=532713
    public class AuthMessageSender : IEmailSender, ISmsSender
    {
        public AuthMessageSender(IOptions<SMSoptions> optionsAccessor)
        {
            Options = optionsAccessor.Value;
        }

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

        public Task SendEmailAsync(string email, string subject, string message)
        {
            // Plug in your email service here to send an email.
            return Task.FromResult(0);
        }

        public Task SendSmsAsync(string number, string message)
        {
            // Plug in your SMS service here to send a text message.
            // Your Account SID from twilio.com/console
            var accountSid = Options.SMSAccountIdentification;
            // Your Auth Token from twilio.com/console
            var authToken = Options.SMSAccountPassword;

            TwilioClient.Init(accountSid, authToken);

            return MessageResource.CreateAsync(
              to: new PhoneNumber(number),
              from: new PhoneNumber(Options.SMSAccountFrom),
              body: message);
        }
    }
}

ASPSMS:

using Microsoft.Extensions.Options;
using System.Threading.Tasks;

namespace Web2FA.Services
{
    // This class is used by the application to send Email and SMS
    // when you turn on two-factor authentication in ASP.NET Identity.
    // For more details see this link https://go.microsoft.com/fwlink/?LinkID=532713
    public class AuthMessageSender : IEmailSender, ISmsSender
    {
        public AuthMessageSender(IOptions<SMSoptions> optionsAccessor)
        {
            Options = optionsAccessor.Value;
        }

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

        public Task SendEmailAsync(string email, string subject, string message)
        {
            // Plug in your email service here to send an email.
            return Task.FromResult(0);
        }

        public Task SendSmsAsync(string number, string message)
        {
            ASPSMS.SMS SMSSender = new ASPSMS.SMS();

            SMSSender.Userkey = Options.SMSAccountIdentification;
            SMSSender.Password = Options.SMSAccountPassword;
            SMSSender.Originator = Options.SMSAccountFrom;

            SMSSender.AddRecipient(number);
            SMSSender.MessageData = message;

            SMSSender.SendTextSMS();

            return Task.FromResult(0);
        }
    }
}

配置启动以使用 SMSoptions

请在Startup.csConfigureServices方法中将SMSoptions添加到服务容器:

    // Add application services.
    services.AddTransient<IEmailSender, AuthMessageSender>();
    services.AddTransient<ISmsSender, AuthMessageSender>();
    services.Configure<SMSoptions>(Configuration);
}

启用双因素身份验证

打开Views/Manage/Index.cshtmlRazor视图文件并删除注释字符,这样就没有任何标记被注释掉。

使用双重身份验证登录

  • 运行应用并注册新用户

在 Microsoft Edge 中打开的 Web 应用注册视图

  • 点击您的用户名,该动作会在管理控制器中的Index操作方法中激活。 然后点击电话号码 “添加 ”链接。

管理视图 - 点击“添加”链接

  • 添加一个将接收验证码的电话号码,然后点击 “发送验证码”。

“添加电话号码”页

  • 将收到包含验证码的短信。 输入并点击 “提交”

验证电话号码页面

如果未收到短信,请参阅 twilio 日志页。

  • “管理”视图显示已成功添加电话号码。

管理视图 - 已成功添加电话号码

  • 点击 “启用” 以启用双重身份验证。

管理视图 - 启用双重身份验证

测试双因素身份验证

  • 登出。

  • 登录。

  • 用户帐户已启用双重身份验证,因此必须提供第二重身份验证。 在本教程中,你已启用电话验证。 内置模板还允许将电子邮件设置为第二个因素。 可以为身份验证设置其他第二个因素,例如 QR 码。 点击“提交”。

发送验证码视图

  • 输入在短信中获取的代码。

  • 单击“ 记住此浏览器 ”复选框将免除在使用相同设备和浏览器时需要使用 2FA 登录。 启用 2FA 并单击“ 记住”此浏览器 将为你提供针对尝试访问帐户的恶意用户的强 2FA 保护,前提是他们无权访问你的设备。 可以在经常使用的任何专用设备上进行此操作。 通过设置 “记住此浏览器”,可以从不经常使用的设备获取 2FA 的附加安全性,并且无需在自己的设备上通过 2FA 即可获得便利。

验证视图

用于防范暴力攻击的帐户锁定

建议使用 2FA 锁定帐户。 用户通过本地帐户或社交帐户登录后,将存储 2FA 的每个失败尝试。 如果达到最大失败的访问尝试次数,则用户将被锁定(默认值:5 分钟锁定后 5 次失败的访问尝试)。 成功的身份验证会重置失败的访问尝试计数并重置时钟。 可以通过设置MaxFailedAccessAttemptsDefaultLockoutTimeSpan来确定最大失败访问尝试次数和锁定时间。 以下配置将在 10 次访问尝试失败后将帐户锁定 10 分钟:

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

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddMvc();

    services.Configure<IdentityOptions>(options =>
    {
        options.Lockout.MaxFailedAccessAttempts = 10;
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(10);
    });

    // Add application services.
    services.AddTransient<IEmailSender, AuthMessageSender>();
    services.AddTransient<ISmsSender, AuthMessageSender>();
    services.Configure<SMSoptions>(Configuration);
}

请确认PasswordSignInAsync是否将lockoutOnFailure设置为true

var result = await _signInManager.PasswordSignInAsync(
                 Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true);