在 ASP.NET Core SMS 的双因素身份验证Two-factor authentication with SMS in ASP.NET Core

通过Rick Anderson瑞士开发人员By Rick Anderson and Swiss-Devs

警告

两个身份验证 (2FA) 身份验证器应用程序,使用基于时间的一次性密码算法 (TOTP),是推荐的方法用于 2FA 的行业。Two factor authentication (2FA) authenticator apps, using a Time-based One-time Password Algorithm (TOTP), are the industry recommended approach for 2FA. 2FA 使用 TOTP 优于 SMS 2FA。2FA using TOTP is preferred to SMS 2FA. 有关详细信息,请参阅TOTP 中 ASP.NET Core 的身份验证器应用启用 QR 代码生成ASP.NET Core 2.0 及更高版本。For more information, see Enable QR Code generation for TOTP authenticator apps in ASP.NET Core for ASP.NET Core 2.0 and later.

本教程演示如何设置双因素身份验证 (2FA) 使用短信。This tutorial shows how to set up two-factor authentication (2FA) using SMS. 说明提供有关twilioASPSMS,但你可以使用任何其他 SMS 提供程序。Instructions are given for twilio and ASPSMS, but you can use any other SMS provider. 我们建议你先完成帐户确认和密码恢复之前开始学习本教程。We recommend you complete Account Confirmation and Password Recovery before starting this tutorial.

查看或下载示例代码View or download sample code. 如何下载How to download.

创建新的 ASP.NET Core 项目Create a new ASP.NET Core project

创建新的 ASP.NET Core web 应用名为Web2FA与单个用户帐户。Create a new ASP.NET Core web app named Web2FA with individual user accounts. 按照中强制实施 HTTPS 在 ASP.NET Core的说明进行操作,设置并要求 HTTPS。Follow the instructions in 强制实施 HTTPS 在 ASP.NET Core to set up and require HTTPS.

创建 SMS 帐户Create an SMS account

创建一个 SMS 帐户,例如,从twilioASPSMSCreate an SMS account, for example, from twilio or ASPSMS. 为 ASPSMS 记录身份验证凭据(对于 twilio: accountSid 和 authToken):用户密钥和 Password)。Record the authentication credentials (for twilio: accountSid and authToken, for ASPSMS: Userkey and Password).

找出 SMS 提供程序凭据Figuring out SMS Provider credentials

TwilioTwilio:

在 Twilio 帐户的 "仪表板" 选项卡中,复制 "帐户 SID " 和 "身份验证令牌"。From the Dashboard tab of your Twilio account, copy the Account SID and Auth token.

ASPSMS:ASPSMS:

从帐户设置中,导航到用户密钥,并将其与密码一起复制。From your account settings, navigate to Userkey and copy it together with your Password.

我们将更高版本存储中密钥的机密管理器工具使用这些值SMSAccountIdentificationSMSAccountPasswordWe will later store these values in with the secret-manager tool within the keys SMSAccountIdentification and SMSAccountPassword.

指定 SenderID / 原始发件人Specifying SenderID / Originator

Twilio从 "数字" 选项卡中,复制 Twilio 的电话号码Twilio: From the Numbers tab, copy your Twilio phone number.

ASPSMS: 在 "解锁工作项" 菜单中,解锁一个或多个发信方,或选择一个字母数字发信方(并非所有网络都支持)。ASPSMS: Within the Unlock Originators Menu, unlock one or more Originators or choose an alphanumeric Originator (Not supported by all networks).

我们稍后将存储此值与中密钥的机密管理器工具SMSAccountFromWe will later store this value with the secret-manager tool within the key SMSAccountFrom.

SMS 服务提供的凭据Provide credentials for the SMS service

我们将使用选项模式访问的用户帐户和密钥设置。We'll use the Options pattern to access the user account and key settings.

  • 创建一个类来提取安全 SMS 项。Create a class to fetch the secure SMS key. 此示例中,对于SMSoptions中创建类Services/SMSoptions.cs文件。For this sample, the SMSoptions class is created in the Services/SMSoptions.cs file.
namespace Web2FA.Services
{
    public class SMSoptions
    {
        public string SMSAccountIdentification { get; set; }
        public string SMSAccountPassword { get; set; }
        public string SMSAccountFrom { get; set; }
    }
}

设置SMSAccountIdentificationSMSAccountPasswordSMSAccountFrom机密管理器工具Set the SMSAccountIdentification, SMSAccountPassword and SMSAccountFrom with the secret-manager tool. 例如:For example:

C:/Web2FA/src/WebApp1>dotnet user-secrets set SMSAccountIdentification 12345
info: Successfully saved SMSAccountIdentification = 12345 to the secret store.
  • SMS 提供程序添加 NuGet 包。Add the NuGet package for the SMS provider. 从包管理器控制台 (PMC) 运行:From the Package Manager Console (PMC) run:

TwilioTwilio:

Install-Package Twilio

ASPSMS:ASPSMS:

Install-Package ASPSMS

  • 将代码中的添加Services/MessageServices.cs文件以启用短信。Add code in the Services/MessageServices.cs file to enable SMS. 使用 Twilio 或 ASPSMS 部分:Use either the Twilio or the ASPSMS section:

TwilioTwilio:

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

将启动要使用配置 SMSoptionsConfigure startup to use SMSoptions

添加SMSoptions对中的服务容器ConfigureServices中的方法Startup.cs:Add SMSoptions to the service container in the ConfigureServices method in the Startup.cs:

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

启用双因素身份验证Enable two-factor authentication

打开Views/管理/索引。 cshtml Razor 视图文件并删除注释字符(因此不会注释掉标记)。Open the Views/Manage/Index.cshtml Razor view file and remove the comment characters (so no markup is commented out).

使用双因素身份验证登录Log in with two-factor authentication

  • 运行应用并注册一个新用户Run the app and register a new user

Web 应用程序注册 Microsoft Edge 中打开视图

  • 点击您的用户名称,这便激活Index管理控制器中的操作方法。Tap on your user name, which activates the Index action method in Manage controller. 然后点击的电话号码添加链接。Then tap the phone number Add link.

管理视图-点击"添加"链接

  • 添加电话号码,它将接收验证代码,并点击发送验证码Add a phone number that will receive the verification code, and tap Send verification code.

添加电话号码页

  • 则会使用验证码的短信。You will get a text message with the verification code. 输入它,然后点击提交Enter it and tap Submit

验证电话号码页

如果没有收到短信,请参阅 twilio 日志页。If you don't get a text message, see twilio log page.

  • 管理视图显示已成功添加你的电话号码。The Manage view shows your phone number was added successfully.

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

  • 点击启用若要启用双因素身份验证。Tap Enable to enable two-factor authentication.

管理视图: 启用双因素身份验证

测试双因素身份验证Test two-factor authentication

  • 注销。Log off.

  • 登录。Log in.

  • 用户帐户已启用双因素身份验证,因此你必须提供身份验证的第二个因素。The user account has enabled two-factor authentication, so you have to provide the second factor of authentication . 在本教程中已启用电话验证。In this tutorial you have enabled phone verification. 内置的模板还可以设置电子邮件作为第二个因素。The built in templates also allow you to set up email as the second factor. 您可以设置其他身份验证的 QR 代码如第二个因素。You can set up additional second factors for authentication such as QR codes. 点击提交Tap Submit.

发送验证代码视图

  • 输入你将获得的 SMS 消息中的代码。Enter the code you get in the SMS message.

  • 单击记住此浏览器复选框将免除你无需使用 2FA 登录时使用相同的设备和浏览器。Clicking on the Remember this browser check box will exempt you from needing to use 2FA to log on when using the same device and browser. 启用 2FA,并单击记住此浏览器将为您提供强 2FA 保护免受恶意用户尝试访问你的帐户,只要它们不能访问你的设备。Enabling 2FA and clicking on Remember this browser will provide you with strong 2FA protection from malicious users trying to access your account, as long as they don't have access to your device. 可以在您经常使用任何专用设备上执行此操作。You can do this on any private device you regularly use. 通过设置记住此浏览器,请从设备不经常使用的情况下,获取 2FA 提高的安全性,您可以方便地在无需在自己的设备上经过 2FA。By setting Remember this browser, you get the added security of 2FA from devices you don't regularly use, and you get the convenience on not having to go through 2FA on your own devices.

验证视图

用于防范暴力破解攻击的帐户锁定Account lockout for protecting against brute force attacks

帐户锁定,建议使用 2FA。Account lockout is recommended with 2FA. 用户登录后通过本地帐户或社交帐户,存储在 2FA 每次失败的尝试。Once a user signs in through a local account or social account, each failed attempt at 2FA is stored. 如果达到了最大失败访问尝试次数,则用户将被锁定(默认值:5分钟在访问尝试失败后锁定5分钟)。If the maximum failed access attempts is reached, the user is locked out (default: 5 minute lockout after 5 failed access attempts). 成功的身份验证失败的访问尝试计数重置并重置时钟。A successful authentication resets the failed access attempts count and resets the clock. 最大失败访问尝试,可使用设置锁定时间MaxFailedAccessAttemptsDefaultLockoutTimeSpanThe maximum failed access attempts and lockout time can be set with MaxFailedAccessAttempts and DefaultLockoutTimeSpan. 以下 10 分钟后访问尝试失败 10 次配置帐户锁定:The following configures account lockout for 10 minutes after 10 failed access attempts:

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设置lockoutOnFailuretrue:Confirm that PasswordSignInAsync sets lockoutOnFailure to true:

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