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

作者:Rick Anderson

本教程介绍如何使用 Two-Factor 身份验证生成 ASP.NET MVC 5 Web 应用。 在继续操作之前,应完成 使用登录、电子邮件确认和密码重置 创建安全 ASP.NET MVC 5 Web 应用。 可以在此处下载已完成的应用程序 。 下载包含调试帮助程序,可让你在不设置电子邮件或短信提供程序的情况下测试电子邮件确认和短信。

本教程由 里克·安德森(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 窗体还支持 ASP.NET 标识,因此可以在 Web 窗体应用中执行类似的步骤。
    显示“新建 ASP.NET 项目”窗口的屏幕截图。默认身份验证“个人用户账户”已突出显示。
  2. 将默认身份验证保留为 单个用户帐户。 若要在 Azure 中托管应用,请选中复选框。 在本教程的后面部分,我们将部署到 Azure。 可以免费注册 Azure 帐户
  3. 项目设置为使用 SSL

为双因素身份验证设置 SMS

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

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

    创建 TwilioASPSMS 帐户。

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

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

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

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

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

    Namespace:
    ASPSMSX2

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

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

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

    稍后,我们将在密钥 "SMSAccountIdentification""SMSAccountPassword"web.config 文件中存储这些值。

  4. 指定 SenderID/发信方

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

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

    稍后,我们将在密钥 "SMSAccountFrom"web.config 文件中存储此值。

  5. 将 SMS 提供商凭据传输到应用

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

    </connectionStrings>
       <appSettings>
          <add key="webpages:Version" value="3.0.0.0" />
          <!-- Markup removed for clarity. -->
          <!-- SendGrid-->
          <add key="mailAccount" value="account" />
          <add key="mailPassword" value="password" />
          <add key="SMSAccountIdentification" value="My Identification" />
          <add key="SMSAccountPassword" value="My Password" />
          <add key="SMSAccountFrom" value="+12065551234" />
       </appSettings>
      <system.web>
    

    警告

    安全性 - 从不将敏感数据存储在源代码中。 帐户和凭据将添加到上面的代码中,使示例保持简单。 请参阅 将密码和其他敏感数据部署到 ASP.NET 和 Azure的最佳做法。

  6. 实现到 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
        }
    }
    
  7. 更新 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>
    
  8. 验证 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");
    }
    
  9. 运行应用并使用之前注册的帐户登录。

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

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

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

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

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

  13. 几秒钟后,将收到包含验证码的短信。 输入并按下“提交”
    “A S P dot NET 应用添加电话号码”页的屏幕截图,其中显示了一个输入栏,上面填充了示例验证码和“提交”按钮。

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

启用双重身份验证

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

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

单击“启用 2FA”。

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

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

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

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

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

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

本教程简要介绍了如何在新的 ASP.NET MVC 应用中启用 2FA。 我的教程 使用短信和包含 ASP.NET 标识的电子邮件进行双重身份验证, 详细介绍了示例背后的代码。

其他资源