ASP.NET MVC - 安全


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

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

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

验证

用户认证是指验证用户的身份。这真的很重要。出于显而易见的原因,您可能只需要向经过身份验证的用户展示您的应用程序。

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

新的 MVC 应用程序

单击“确定”继续。

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

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

验证按钮已启用

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

身份验证选项

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

无需验证

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

无需验证

它对任何人开放,每个人都通过每个页面进行连接。您以后可以随时更改该设置,但“无身份验证”选项意味着不会有任何功能来识别访问该网站的用户。

个人用户帐户

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

个人用户帐户

密码也存储在数据库中,但首先进行哈希处理。由于密码经过哈希处理,因此您不必担心数据库中的纯文本密码。

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

这允许用户使用他们的实时帐户或 Twitter 帐户登录您的网站,并且他们可以选择本地用户名,但您不需要存储任何密码。

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

工作和学校账户

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

工作学校账户

您将设置 Office 365 或使用 Azure Active Directory 服务,并且可以对内部应用程序和云应用程序进行单点登录。

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

Windows 身份验证

第四个选项是 Windows 身份验证,它非常适合 Intranet 应用程序。

Windows 身份验证

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

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

基于表单的身份验证

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

匿名用户

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

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

点击注册链接

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

输入邮箱ID 密码

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

点击注册

它将能够显示您的名字。在下面的屏幕截图中,您可以看到您好,muhammad.waqas@outlook.com!被展示。您可以单击它,它是一个指向您可以更改密码的页面的链接。

显示你的名字

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

单击注销按钮

再次点击登录链接,您将进入以下页面。

点击登录链接

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

为了达到这一点,幕后需要进行大量工作。然而,我们想要做的是检查每个功能并了解此 UI 是如何构建的。什么是管理注销和登录过程?这些信息在数据库中存储在哪里?

让我们从一些简单的基础知识开始。首先我们来看看这个用户名是如何显示的。从解决方案资源管理器的 View/Shared 文件夹中打开 _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 的调用。这将检索用户名,在本例中为“muhammad.waqas@outlook.com”

授权

假设我们有某种信息,希望防止未经身份验证的用户访问。因此,让我们创建一个新控制器来显示该信息,但仅在用户登录时显示。

右键单击控制器文件夹并选择添加 → 控制器。

用户已登录

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

输入名称 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 操作方法,并且任何人都可以在没有任何身份验证的情况下使用 PublicInfo。

为了保护此特定操作并防止未经身份验证的用户到达此处,您可以使用授权属性。不带任何其他参数的 Authorize 属性将确保用户的身份已知并且他们不是匿名用户。

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

现在再次运行此应用程序并指定相同的 URL http://localhost:54232/Secret/Secret。MVC 应用程序将检测到您无权访问应用程序的该特定区域,并将自动将您重定向到登录页面,您将有机会登录并尝试返回应用程序的该区域你被拒绝的地方。

自动重定向登录页面

你可以看到它是在返回URL中指定的,它本质上告诉这个页面,如果用户登录成功,则将其重定向回/secret/secret。

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

秘密信息在这里

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

公共信息在这里

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

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,但其他操作将需要身份验证。

公共信息在这里

它将只允许匿名用户执行这一操作。

使用授权属性,您还可以指定一些参数,例如允许某些特定用户执行此操作。

using System.Web.Mvc;

namespace MVCSecurityDemo.Controllers{
   [Authorize(Users = "ali.khan@outlook.com")]
   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 时,它会要求您登录,因为它不是该控制器的正确用户。

前往秘密