ASP.NET MVC - 安全性



在本章中,我们将讨论如何在应用程序中实现安全功能。我们还将了解 ASP.NET 中包含的新成员资格功能,以及可供 ASP.NET MVC 使用的功能。在 ASP.NET 的最新版本中,我们可以使用以下方式管理用户标识:

  • SQL 数据库
  • 本地 Windows 活动目录

在本章中,我们还将了解作为 ASP.NET 一部分的新身份组件,并了解如何为我们的用户和角色自定义成员资格。

身份验证

用户身份验证是指验证用户身份。这非常重要。出于显而易见的原因,您可能需要仅向已验证的用户展示您的应用程序。

让我们创建一个新的 ASP.Net MVC 应用程序。

New MVC Application

单击“确定”继续。

当您启动一个新的 ASP.NET 应用程序时,该过程中的一个步骤是为应用程序需求配置身份验证服务。

选择 MVC 模板,您会看到“更改身份验证”按钮现在已启用。

Authentication Button Enabled

这是通过“新建项目”对话框中出现的“更改身份验证”按钮完成的。默认身份验证为“单个用户帐户”。

身份验证选项

单击“更改”按钮时,您将看到一个带有四个选项的对话框,如下所示。

无身份验证

第一个选项是“无身份验证”,当您想要构建一个不关心访问者是谁的网站时,可以使用此选项。

No Authentication

它对所有人开放,每个人都连接到每个页面。您始终可以稍后更改它,但“无身份验证”选项意味着不会有任何功能来识别访问网站的用户。

单个用户帐户

第二个选项是“单个用户帐户”,这是传统的基于表单的身份验证,用户可以访问网站。他们可以注册、创建登录名,并且默认情况下,他们的用户名使用一些新的 ASP.NET 身份功能存储在 SQL Server 数据库中,我们将在后面介绍。

Individual User Accounts

密码也存储在数据库中,但首先会进行哈希处理。由于密码已进行哈希处理,因此您无需担心数据库中存在明文密码。

此选项通常用于您想要建立用户身份的互联网站点。除了允许用户为您的站点创建本地登录名和密码外,您还可以启用来自第三方(如 Microsoft、Google、Facebook 和 Twitter)的登录。

这允许用户使用他们的 Live 帐户或 Twitter 帐户登录到您的站点,并且他们可以选择本地用户名,但您无需存储任何密码。

这是我们将在本模块中花费一些时间讨论的选项;“单个用户帐户”选项。

工作和学校帐户

第三个选项是使用组织帐户,这通常用于您将使用活动目录联合服务的业务应用程序。

Work School Accounts

您将设置 Office 365 或使用 Azure Active Directory 服务,并且您拥有内部应用程序和云应用程序的单点登录。

您还需要提供应用程序 ID,因此如果基于 Azure,则需要在 Windows Azure 管理门户中注册您的应用程序,并且应用程序 ID 将在所有可能注册的应用程序中唯一标识此应用程序。

Windows 身份验证

第四个选项是 Windows 身份验证,它适用于内联网应用程序。

Windows Authentication

用户登录到 Windows 桌面,然后可以启动浏览器访问位于同一防火墙内的应用程序。ASP.NET 可以自动获取用户身份,即由活动目录建立的身份。此选项不允许任何匿名访问站点,但这又是可以更改的配置设置。

让我们看看基于表单的身份验证,即名为“单个用户帐户”的身份验证。此应用程序将存储用户名和密码、旧密码在本地 SQL Server 数据库中,并且在创建此项目时,Visual Studio 还会添加 NuGet 包。

Forms-based Authentication

现在运行此应用程序,当您第一次访问此应用程序时,您将是匿名用户。

Anonymous User

您还没有可以登录的帐户,因此您需要在此站点上注册。

单击“注册”链接,您将看到以下视图。

Click Register Link

输入您的电子邮件 ID 和密码。

Enter EmailId Password

单击“注册”。现在,应用程序将识别您。

Click Register

它将能够显示您的姓名。在下面的屏幕截图中,您可以看到显示了“您好,[email protected]!”。您可以单击它,它是一个链接到您可以更改密码的页面的链接。

Display Your Name

您还可以注销、关闭、重新启动,一周后再回来,您应该能够使用之前使用的凭据登录。现在单击“注销”按钮,它将显示以下页面。

Click Logoff Button

再次单击“登录”链接,您将转到以下页面。

Click Login Link

您可以使用相同的凭据再次登录。

幕后做了很多工作才能达到这一点。但是,我们要做的是检查每个功能并了解此 UI 是如何构建的。是什么管理注销和登录过程?此信息存储在数据库中的哪个位置?

让我们从几个简单的基础开始。首先,我们将了解如何显示此用户名。在解决方案资源管理器中打开“视图/共享”文件夹中的 _Layout.cshtml。

<!DOCTYPE html>
<html>
   <head>
      <meta charset = "utf-8" />
      <meta name = "viewport" content = "width = device-width, initial-scale = 1.0">
      <title>@ViewBag.Title - My ASP.NET Application</title>
      @Styles.Render("~/Content/css")
      @Scripts.Render("~/bundles/modernizr")
   </head>
	
   <body>
      <div class = "navbar navbar-inverse navbar-fixed-top">
         <div class = "container">
			
            <div class = "navbar-header">
               <button type = "button" class = "navbar-toggle" datatoggle = "collapse"
                  data-target = ".navbar-collapse">
                     <span class = "icon-bar"></span>
                     <span class = "icon-bar"></span>
                     <span class = "icon-bar"></span>
               </button>
					
               @Html.ActionLink("Application name", "Index", "Home", new
               { area = "" }, new { @class = "navbar-brand" })
            </div>
				
            <div class = "navbar-collapse collapse">
               <ul class = "nav navbar-nav">
                  <li>@Html.ActionLink("Home", "Index", "Home")</li>
                  <li>@Html.ActionLink("About", "About", "Home")</li>
                  <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
               </ul>
					
               @Html.Partial("_LoginPartial")
            </div>
				
         </div>
			
      </div>
      <div class = "container body-content">
         @RenderBody()
         <hr />
         <footer>
            <p>© @DateTime.Now.Year - My ASP.NET Application</p>
         </footer>
      </div>
		
      @Scripts.Render("~/bundles/jquery")
      @Scripts.Render("~/bundles/bootstrap")
      @RenderSection("scripts", required: false)
		
   </body>
</html>

有一个公共导航栏、应用程序名称、菜单,以及一个名为 _loginpartial 的正在呈现的部分视图。这实际上是显示用户名或注册和登录名的视图。因此 _loginpartial.cshtml 也在共享文件夹中。

@using Microsoft.AspNet.Identity
@if (Request.IsAuthenticated) {
   using (Html.BeginForm("LogOff", "Account", FormMethod.Post,
      new { id = "logoutForm", @class = "navbar-right" })){
         @Html.AntiForgeryToken()
         <ul class = "nav navbar-nav navbar-right">
            <li>
               @Html.ActionLink("Hello " + User.Identity.GetUserName() + "!",
               "Index", "Manage", routeValues: null, htmlAttributes: new { title = "Manage" })
            </li>
				
            <li>
               <a href = "javascript:document.getElementById('logoutForm').submit()">Logoff</a>
            </li>
				
         </ul>
      }
}else{
   <ul class = "nav navbar-nav navbar-right">
      <li>@Html.ActionLink("Register", "Register", "Account", routeValues:
         null, htmlAttributes: new { id = "registerLink" })</li>
			
      <li>@Html.ActionLink("Log in", "Login", "Account", routeValues: null,
         htmlAttributes: new { id = "loginLink" })</li>
   </ul>
}

如上所示,存在 if/else 语句。如果我们不知道用户是谁,因为请求未经身份验证,则此视图将显示注册和登录链接。用户可以单击链接登录或注册。所有这些都是由帐户控制器完成的。

现在,我们要了解如何获取用户名,它位于 Request.IsAuthenticated 内部。您可以看到对 User.Identity.GetUserName 的调用。这将检索用户名,在本例中为“[email protected]

授权

假设我们有一些我们想要保护免遭未经身份验证的用户访问的信息。因此,让我们创建一个新的控制器来显示该信息,但仅在用户登录时显示。

右键单击控制器文件夹,然后选择“添加”→“控制器”。

User Logged in

选择 MVC 5 控制器 - 空控制器,然后单击“添加”。

输入名称 SecretController 并单击“添加”按钮。

SecretController

它将在内部包含两个操作,如下面的代码所示。

using System.Web.Mvc;

namespace MVCSecurityDemo.Controllers{
   public class SecretController : Controller{
      // GET: Secret
      public ContentResult Secret(){
         return Content("Secret informations here");
      }
		
      public ContentResult PublicInfo(){
         return Content("Public informations here");
      }
   }
}

当您运行此应用程序时,您可以访问此信息而无需任何身份验证,如下面的屏幕截图所示。

Secret Information Here

因此,只有经过身份验证的用户才能访问 Secret 操作方法,而 PublicInfo 可以被任何人在没有任何身份验证的情况下使用。

要保护此特定操作并阻止未经身份验证的用户到达此处,您可以使用 Authorize 属性。Authorize 属性在没有任何其他参数的情况下将确保知道用户身份并且他们不是匿名用户。

// GET: Secret
[Authorize]
public ContentResult Secret(){
   return Content("Secret informations here");
}

现在再次运行此应用程序并指定相同的 URL https://127.0.0.1:54232/Secret/Secret。MVC 应用程序将检测到您无权访问应用程序的该特定区域,并且它会自动将您重定向到登录页面,在该页面上它将为您提供登录并尝试返回到您被拒绝访问的应用程序区域的机会。

Redirect Automatically Login Page

您可以看到它在返回 URL 中指定,这实际上告诉此页面,如果用户成功登录,则将其重定向回 /secret/secret。

输入您的凭据并单击“登录”按钮。您将看到它直接转到该页面。

Secret Information Here

如果您返回主页并注销,则无法访问秘密页面。系统将再次要求您登录,但如果您转到 /Secret/PublicInfo,即使您未经身份验证,您也可以看到该页面。

Public Information Here

因此,当您不想在控制器内部的每个操作上都放置授权时,在控制器内部几乎所有内容都需要授权。在这种情况下,您始终可以将此过滤器应用于控制器本身,现在此控制器内的每个操作都将要求用户经过身份验证。

using System.Web.Mvc;

namespace MVCSecurityDemo.Controllers{
   [Authorize]
   public class SecretController : Controller{
      // GET: Secret
      public ContentResult Secret(){
         return Content("Secret informations here");
      }
		
      public ContentResult PublicInfo(){
         return Content("Public informations here");
      }
   }
}

但是,如果您确实希望任何操作都公开,您可以使用另一个属性覆盖此授权规则,即 AllowAnonymous。

using System.Web.Mvc;

namespace MVCSecurityDemo.Controllers{
   [Authorize]
   public class SecretController : Controller{
      // GET: Secret
      public ContentResult Secret(){
         return Content("Secret informations here");
      }
		
      [AllowAnonymous]
      public ContentResult PublicInfo(){
         return Content("Public informations here");
      }
   }
}

运行此应用程序,您可以在不登录的情况下访问 /Secret/PublicInfo,但其他操作将需要身份验证。

Public Information Here

它将仅允许匿名用户访问此一个操作。

使用Authorize属性,您还可以指定一些参数,例如允许某些特定用户访问此操作。

using System.Web.Mvc;

namespace MVCSecurityDemo.Controllers{
   [Authorize(Users = "[email protected]")]
   public class SecretController : Controller{
      // GET: Secret
      public ContentResult Secret(){
         return Content("Secret informations here");
      }
		
      [AllowAnonymous]
      public ContentResult PublicInfo(){
         return Content("Public informations here");
      }
   }
}

当您运行此应用程序并访问/secret/secret时,它会要求您登录,因为您不是此控制器的正确用户。

Go to Secret
广告