具有 SMS 和电子邮件双因素身份验证的 ASP.NET MVC 5 应用

作者:Rick Anderson

本教程介绍如何使用双因素身份验证生成 ASP.NET MVC 5 Web 应用。 在继续操作之前,应先完成创建具有登录、电子邮件确认和密码重置功能的安全 ASP.NET MVC 5 Web 应用。 可以在此处下载已完成的应用程序。 下载包含调试帮助程序,以便测试电子邮件确认和短信,而无需设置电子邮件或短信提供程序。

本教程由 Rick Anderson(Twitter:@RickAndMSFT)撰写。

创建 ASP.NET MVC 应用

首先安装和运行 Visual Studio Express 2013 for Web 或更高版本。

注意

警告:在继续操作之前,应先完成创建具有登录、电子邮件确认和密码重置功能的安全 ASP.NET MVC 5 Web 应用。 必须安装 Visual Studio 2013 Update 3 或更高版本才能完成本教程。

  1. 创建新的 ASP.NET Web 项目并选择 MVC 模板。 Web Forms 还支持 ASP.NET 标识,因此可以在 Web Forms 应用中执行类似的步骤。
    显示“新建 ASP.NET 项目”窗口的屏幕截图。默认身份验证“个人用户账户”已突出显示。
  2. 将默认身份验证保留为个人用户帐户。 如果要在 Azure 中托管应用,请选中此复选框。 在本教程的后面部分,我们将部署到 Azure。 可以免费注册 Azure 帐户
  3. 项目设置为使用 SSL

为双因素身份验证设置 SMS

本教程提供有关使用 Twilio 或 ASPSMS 的说明,但可以使用任何其他短信提供程序。

  1. 使用短信提供程序创建用户帐户

    创建 TwilioASPSMS 帐户。

  2. 安装其他包或添加服务引用

    Twilio:
    在“包管理器控制台”中,输入以下命令:
    Install-Package Twilio

    ASPSMS:
    需要添加以下服务引用:

    显示“添加服务引用”窗口的屏幕截图。地址和命名空间输入栏将突出显示。

    地址:
    https://webservice.aspsms.com/aspsmsx2.asmx?WSDL

    命名空间:
    ASPSMSX2

  3. 确定 SMS 提供商用户凭据

建议使用最安全的身份验证选项。 有关部署到 Azure 的 .NET 应用,请参阅:

Azure Key Vault 和 .NET Aspire 提供了存储和检索机密的最安全方法。 Azure 密钥保管库是一种云服务,用于保护加密密钥和机密(例如证书、连接字符串和密码)。 有关 .NET Aspire,请参阅 托管与客户端集成之间的安全通信

避免资源所有者密码凭据授予,因为这:

  • 向客户端公开用户的密码。
  • 存在重大安全风险。
  • 仅当其他身份验证流不可用时才能使用。

将应用部署到测试服务器时,可以使用环境变量将连接字符串设置为测试数据库服务器。 环境变量通常以未加密纯文本的形式进行存储。 如果计算机或进程遭到入侵,那么不受信任方可访问环境变量。 建议不要使用环境变量来存储生产连接字符串,因为这不是最安全的方法。

配置数据指南:

  • 请勿在配置提供程序代码或纯文本配置文件中存储密码或其他敏感数据。
  • 不要在开发或测试环境中使用生产机密。
  • 请在项目外部指定机密,避免将其意外提交到源代码存储库。

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

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

稍后,我们将在密钥 "SMSAccountIdentification""SMSAccountPassword"web.config 文件中存储这些值。 4. 指定 SenderID/发信方

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

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

稍后,我们将在密钥 "SMSAccountFrom"web.config 文件中存储此值。 5. 将短信提供程序凭据传输到应用

向应用提供凭据和发送方电话号码。 为了简便起见,我们会将这些值存储在 web.config 文件中。 部署到 Azure 时,我们可以将值安全地存储在网站配置选项卡的应用设置部分中。

[!code-xml[Main](aspnet-mvc-5-app-with-sms-and-email-two-factor-authentication/samples/sample1.xml?highlight=8-10)]

> [!WARNING]
> Security - Never store sensitive data in your source code. The account and credentials are added to the code above to keep the sample simple. See [Best practices for deploying passwords and other sensitive data to ASP.NET and Azure](../../../identity/overview/features-api/best-practices-for-deploying-passwords-and-other-sensitive-data-to-aspnet-and-azure.md).
  1. 实现到 SMS 提供商的数据传输

    App_Start\IdentityConfig.cs 文件中配置 SmsService 类。

    根据所使用的 SMS 提供商,激活 TwilioASPSMS 部分:

    public class SmsService : IIdentityMessageService
    {
        public Task SendAsync(IdentityMessage message)
        {
            // Twilio Begin
            //var accountSid = ConfigurationManager.AppSettings["SMSAccountIdentification"];
            //var authToken = ConfigurationManager.AppSettings["SMSAccountPassword"];
            //var fromNumber = ConfigurationManager.AppSettings["SMSAccountFrom"];
    
            //TwilioClient.Init(accountSid, authToken);
    
            //MessageResource result = MessageResource.Create(
                //new PhoneNumber(message.Destination),
                //from: new PhoneNumber(fromNumber),
               //body: message.Body
            //);
    
            ////Status is one of Queued, Sending, Sent, Failed or null if the number is not valid
             //Trace.TraceInformation(result.Status.ToString());
            ////Twilio doesn't currently have an async API, so return success.
             //return Task.FromResult(0);    
            // Twilio End
    
            // ASPSMS Begin 
            // var soapSms = new MvcPWx.ASPSMSX2.ASPSMSX2SoapClient("ASPSMSX2Soap");
            // soapSms.SendSimpleTextSMS(
            //   System.Configuration.ConfigurationManager.AppSettings["SMSAccountIdentification"],
            //   System.Configuration.ConfigurationManager.AppSettings["SMSAccountPassword"],
            //   message.Destination,
            //   System.Configuration.ConfigurationManager.AppSettings["SMSAccountFrom"],
            //   message.Body);
            // soapSms.Close();
            // return Task.FromResult(0);
            // ASPSMS End
        }
    }
    
  2. 更新 Views\Manage\Index.cshtml Razor 视图:(备注:不要仅移除现有代码中的注释,请使用下面的代码。)

    @model MvcPWy.Models.IndexViewModel
    @{
       ViewBag.Title = "Manage";
    }
    <h2>@ViewBag.Title.</h2>
    <p class="text-success">@ViewBag.StatusMessage</p>
    <div>
       <h4>Change your account settings</h4>
       <hr />
       <dl class="dl-horizontal">
          <dt>Password:</dt>
          <dd>
             [
             @if (Model.HasPassword)
             {
                @Html.ActionLink("Change your password", "ChangePassword")
             }
             else
             {
                @Html.ActionLink("Create", "SetPassword")
             }
             ]
          </dd>
          <dt>External Logins:</dt>
          <dd>
             @Model.Logins.Count [
             @Html.ActionLink("Manage", "ManageLogins") ]
          </dd>
            <dt>Phone Number:</dt>
          <dd>
             @(Model.PhoneNumber ?? "None") [
             @if (Model.PhoneNumber != null)
             {
                @Html.ActionLink("Change", "AddPhoneNumber")
                @: &nbsp;|&nbsp;
                @Html.ActionLink("Remove", "RemovePhoneNumber")
             }
             else
             {
                @Html.ActionLink("Add", "AddPhoneNumber")
             }
             ]
          </dd>
          <dt>Two-Factor Authentication:</dt> 
          <dd>
             @if (Model.TwoFactor)
             {
                using (Html.BeginForm("DisableTwoFactorAuthentication", "Manage", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
                {
                   @Html.AntiForgeryToken()
                   <text>Enabled
                      <input type="submit" value="Disable" class="btn btn-link" />
                   </text>
                }
             }
             else
             {
                using (Html.BeginForm("EnableTwoFactorAuthentication", "Manage", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
                {
                   @Html.AntiForgeryToken()
                   <text>Disabled
                      <input type="submit" value="Enable" class="btn btn-link" />
                   </text>
                }
             }
          </dd>
       </dl>
    </div>
    
  3. 验证 ManageController 中的 EnableTwoFactorAuthenticationDisableTwoFactorAuthentication 操作方法是否具有 [ValidateAntiForgeryToken] 属性:

    //
    // POST: /Manage/EnableTwoFactorAuthentication
    [HttpPost,ValidateAntiForgeryToken]
    public async Task<ActionResult> EnableTwoFactorAuthentication()
    {
        await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), true);
        var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
        if (user != null)
        {
            await SignInAsync(user, isPersistent: false);
        }
        return RedirectToAction("Index", "Manage");
    }
    //
    // POST: /Manage/DisableTwoFactorAuthentication
    [HttpPost, ValidateAntiForgeryToken]
    public async Task<ActionResult> DisableTwoFactorAuthentication()
    {
        await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), false);
        var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
        if (user != null)
        {
            await SignInAsync(user, isPersistent: false);
        }
        return RedirectToAction("Index", "Manage");
    }
    
  4. 运行应用并使用之前注册的帐户登录。

  5. 单击您的用户 ID,这将激活 Index 控制器中的 Manage 操作方法。
    显示 ASP.NET 应用主页的屏幕截图。示例用户 ID 突出显示。

  6. 单击“添加” 。
    显示 ASP. NET 应用的“帐户设置”页的屏幕截图。突出显示“电话号码”部分旁边的“未添加”。

  7. AddPhoneNumber 操作方法显示一个对话框,用于输入可以接收短信的电话号码。

    // GET: /Account/AddPhoneNumber
    public ActionResult AddPhoneNumber()
    {
       return View();
    }
    

    显示 ASP .NET 应用的“添加电话号码”页的屏幕截图。示例电话号码下方填写了“发送验证码”按钮。

  8. 几秒钟后,将收到包含验证码的短信。 输入并按下“提交”
    显示 ASP.NET 应用的“添加电话号码”页的屏幕截图,其中显示的输入栏填写了示例验证码,并且其下方有“提交”按钮。

  9. 管理视图显示你的电话号码已被添加。

启用双因素身份验证

在模板生成的应用中,需要使用 UI 启用双因素身份验证 (2FA)。 若要启用 2FA,请单击导航栏中的用户 ID(电子邮件别名)。

显示 ASP.NET 应用主页的屏幕截图。突出显示了示例 USER ID。

单击“启用 2FA”。

显示 ASP .NET 应用的“帐户设置”页的屏幕截图。突出显示“双因素身份验证:已使用启用链接禁用”部分。

注销,然后重新登录。 如果已启用电子邮件(请参阅上一教程),则可以选择用于 2FA 的短信或电子邮件。

显示“ASP.NET 应用程序发送验证码”页的屏幕截图。选中了显示“手机验证码”和“邮箱验证码”的下拉菜单。

将显示“验证代码”页,可以在其中输入代码(来自短信或电子邮件)。

屏幕截图,显示了 ASP.NET 应用程序的 2FA 验证页面。在示例代码下方,突出显示了“记住此浏览器”复选框。

单击“记住此浏览器”复选框后,在使用选中该框的浏览器和设备时将无需使用 2FA 登录。 只要恶意用户无法访问设备,启用 2FA 并单击“记住此浏览器”,将提供方便的单步密码访问,同时仍为来自非受信任设备的所有访问保留强大的 2FA 保护。 可以在经常使用的任何专用设备上进行此操作。

本教程简要介绍了如何在新的 ASP.NET MVC 应用中启用 2FA。 我的教程使用 SMS 和 ASP.NET Identity 的双因素身份验证详细介绍了示例背后的代码。

其他资源