在本章中,我们将讨论如何在应用程序中实现安全功能,我们还将研究ASP.NET附带的新成员资格功能,这些新成员资格功能可从ASP.NET MVC使用。
认证方式
用户身份验证是指验证用户身份,这真的很重要。出于明显的原因,您可能只需要向经过身份验证的用户展示您的应用程序。
让我们创建一个新的ASP.Net MVC应用程序。

单击确定继续。
当您启动新的ASP.NET应用程序时,该过程中的步骤之一是为应用程序需求配置身份验证服务。
选择MVC模板,您将看到“Change Authentication”按钮现已启用。

这是通过出现在“New Project”对话框中的“Change Authentication”按钮完成的。默认身份验证为“User Accounts”。
认证方式选项
单击"Change"按钮时,将看到一个包含四个选项的对话框,如下所示。
不认证
第一个选项是“No Authentication”,当您要构建一个不关心访问者是谁的网站时,将使用此选项。

它向任何人开放,每个人都像每个页面一样连接。您以后可以随时更改它,但是“No Authentication”选项表示将没有任何功能来识别访问该网站的用户。
个人用户帐号
第二个选项是“Individual User Accounts”,这是传统的基于表单的身份验证,用户可以在其中访问网站,他们可以注册,创建登录名,并且默认情况下,他们的用户名使用一些新的ASP.NET身份功能存储在SQL Server数据库中,我们将介绍这些功能。

密码也存储在数据库中,密码是散列的,因此您不必担心数据库中存在纯文本密码。
工作和学校账户
第三种选择是使用组织帐户,通常用于要使用活动目录联合服务的业务应用程序。

您将设置Office 365或使用Azure Active Directory服务,并且对内部应用程序和云应用程序进行一次登录。
您还需要提供一个应用程序ID,以便如果您的应用程序是基于Azure的,则需要在Windows Azure管理门户中进行注册,并且该应用程序ID将在所有可能注册的应用程序中唯一标识该应用程序。
Windows验证
第四个选项是Windows身份验证,它对于Intranet应用程序非常有效。

用户登录Windows桌面,可以启动浏览器浏览位于同一防火墙内的应用程序, ASP.NET可以自动获取用户身份,该身份由活动目录创建,此选项不允许对站点的任何匿名访问,但是同样,这是可以更改的配置设置。
让我们看一下基于表单的身份验证,该身份称为“Individual User Accounts”,此应用程序将用户名和密码,旧密码存储在本地SQL Server数据库中,并且在创建此项目时,Visual Studio还将添加NuGet包。

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

您还没有可以登录的帐户,因此需要在此站点上注册。
单击注册链接,您将看到以下视图。

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

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

它将能够显示您的姓名,您可以单击该链接,这是指向可以更改密码的页面的链接。

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

再次单击"Login"链接,您将转到下一页。

为了达到这一点,很多工作在幕后进行,但是,我们要做的是检查每个功能并查看如何构建此UI,如何管理注销和登录过程?该信息在数据库中的何处排序?
让我们从几个简单的基础开始。首先,我们将看到如何显示该用户名。从Solution Explorer中的"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">
</span><span class="tag"><div</span><span class="pln"> </span><span class="atn">class</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="atv">"navbar-header"</span><span class="tag">></span><span class="pln">
</span><span class="tag"><button</span><span class="pln"> </span><span class="atn">type</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="atv">"button"</span><span class="pln"> </span><span class="atn">class</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="atv">"navbar-toggle"</span><span class="pln"> </span><span class="atn">datatoggle</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="atv">"collapse"</span><span class="pln">
</span><span class="atn">data-target</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="atv">".navbar-collapse"</span><span class="tag">></span><span class="pln">
</span><span class="tag"><span</span><span class="pln"> </span><span class="atn">class</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="atv">"icon-bar"</span><span class="tag">></span></span><span class="pln">
</span><span class="tag"><span</span><span class="pln"> </span><span class="atn">class</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="atv">"icon-bar"</span><span class="tag">></span></span><span class="pln">
</span><span class="tag"><span</span><span class="pln"> </span><span class="atn">class</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="atv">"icon-bar"</span><span class="tag">></span></span><span class="pln">
</span><span class="tag"></button></span><span class="pln">
@Html.ActionLink("Application name", "Index", "Home", new
{ area="" }, new { @class="navbar-brand" })
</span><span class="tag"></div></span><span class="pln">
</span><span class="tag"><div</span><span class="pln"> </span><span class="atn">class</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="atv">"navbar-collapse collapse"</span><span class="tag">></span><span class="pln">
</span><span class="tag"><ul</span><span class="pln"> </span><span class="atn">class</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="atv">"nav navbar-nav"</span><span class="tag">></span><span class="pln">
</span><span class="tag"><li></span><span class="pln">@Html.ActionLink("Home", "Index", "Home")</span><span class="tag"></li></span><span class="pln">
</span><span class="tag"><li></span><span class="pln">@Html.ActionLink("About", "About", "Home")</span><span class="tag"></li></span><span class="pln">
</span><span class="tag"><li></span><span class="pln">@Html.ActionLink("Contact", "Contact", "Home")</span><span class="tag"></li></span><span class="pln">
</span><span class="tag"></ul></span><span class="pln">
@Html.Partial("_LoginPartial")
</span><span class="tag"></div></span><span class="pln">
</span><span class="tag"></div></span><span class="pln">
</span><span class="tag"></div></span><span class="pln">
</span><span class="tag"><div</span><span class="pln"> </span><span class="atn">class</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="atv">"container body-content"</span><span class="tag">></span><span class="pln">
@RenderBody()
</span><span class="tag"><hr</span><span class="pln"> </span><span class="tag">/></span><span class="pln">
</span><span class="tag"><footer></span><span class="pln">
</span><span class="tag"><p></span><span class="pln">© @DateTime.Now.Year - My ASP.NET Application</span><span class="tag"></p></span><span class="pln">
</span><span class="tag"></footer></span><span class="pln">
</span><span class="tag"></div></span><span class="pln">
@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>
</span><span class="str"><li></span><span class="pln">
</span><span class="pun"><</span><span class="pln">a href </span><span class="pun">=</span><span class="pln"> </span><span class="str">"javascript:document.getElementById(logoutForm).submit()"</span><span class="pun">></span><span class="typ">Logoff</span><span class="pun"></</span><span class="pln">a</span><span class="pun">></span><span class="pln">
</span><span class="pun"></</span><span class="pln">li</span><span class="pun">></span><span class="pln">
</span><span class="pun"></</span><span class="pln">ul</span><span class="pun">></span><span class="pln">
</span><span class="pun">}</span><span class="pln">
}else{
<ul class = "nav navbar-nav navbar-right">
<li>@Html.ActionLink("Register", "Register", "Account", routeValues:
null, htmlAttributes: new { id = "registerLink" })</li>
</span><span class="str"><li></span><span class="lit">@Html</span><span class="pun">.</span><span class="typ">ActionLink</span><span class="pun">(</span><span class="str">"Log in"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Login"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Account"</span><span class="pun">,</span><span class="pln"> routeValues</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln">
htmlAttributes</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> id </span><span class="pun">=</span><span class="pln"> </span><span class="str">"loginLink"</span><span class="pln"> </span><span class="pun">})</</span><span class="pln">li</span><span class="pun">></span><span class="pln">
</ul>
}
如您在上面看到的,有if/else语句。如果我们不知道用户是谁,因为请求未通过身份验证,此视图将显示注册和登录链接,用户可以单击链接登录或注册,所有这一切都是由AccountController完成的。
现在,我们想看看如何获取用户名,它位于Request.IsAuthenticated中,您可以看到对User.Identity.GetUserName的调用。
授权
假设我们有一些信息要保护,以防未经身份验证的用户使用,因此,让我们创建一个新的控制器来显示该信息,但仅当用户登录时才显示。
右键单击控制器文件夹,然后选择Add→Controller。

选择一个 MVC 5 Controller-Empty 控制器,然后单击"Add"。
输入名称SecretController,然后单击"Add"按钮。

它将在内部具有两个Action,如以下代码所示。
using System.Web.Mvc;
namespace MVCSecurityDemo.Controllers{
public class SecretController : Controller{
//GET: Secret
public ContentResult Secret(){
return Content("Secret informations here");
}
</span><span class="kwd">public</span><span class="pln"> </span><span class="typ">ContentResult</span><span class="pln"> </span><span class="typ">PublicInfo</span><span class="pun">(){</span><span class="pln">
</span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Content</span><span class="pun">(</span><span class="str">"Public informations here"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
}
}
运行此应用程序时,无需进行任何身份验证即可访问此信息,如以下屏幕截图所示。

因此,只有经过身份验证的用户才能访问Secret操作方法,并且任何未经任何身份验证的人都可以使用PublicInfo。
为了保护此特定操作并防止未经身份验证的用户到达此处,可以使用Authorize属性,没有任何其他参数的Authorize属性将确保用户的身份是已知的,并且他们不是匿名用户。
//GET: Secret [Authorize] public ContentResult Secret(){ return Content("Secret informations here"); }
现在再次运行该应用程序,并指定相同的URL http://localhost:54232/Secret/Secret , MVC应用程序将检测到您无权访问该应用程序的特定区域,并将自动将您重定向到登录页面,在该页面上您将有机会登录并尝试回到该应用程序的该区域。

您可以看到它是在返回URL中指定的,它实际上告诉此页面,如果用户成功登录,请将其重定向回/secret/secret。
输入您的凭据,然后单击"Login"按钮。您将看到它直接转到该页面。

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

因此,当您不想在每个Action上都放置授权时,当您位于控制器中时,几乎所有事情都需要授权,在这种情况下,您始终可以将此过滤器应用于控制器本身,现在此控制器内部的每个操作都将要求对用户进行身份验证。
using System.Web.Mvc;
namespace MVCSecurityDemo.Controllers{
[Authorize]
public class SecretController : Controller{
//GET: Secret
public ContentResult Secret(){
return Content("Secret informations here");
}
</span><span class="kwd">public</span><span class="pln"> </span><span class="typ">ContentResult</span><span class="pln"> </span><span class="typ">PublicInfo</span><span class="pun">(){</span><span class="pln">
</span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Content</span><span class="pun">(</span><span class="str">"Public informations here"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
}
}
但是,如果您真的希望打开任何操作,则可以使用另一个属性AllowAnonymous覆盖此授权规则。
using System.Web.Mvc;
namespace MVCSecurityDemo.Controllers{
[Authorize]
public class SecretController : Controller{
//GET: Secret
public ContentResult Secret(){
return Content("Secret informations here");
}
</span><span class="pun">[</span><span class="typ">AllowAnonymous</span><span class="pun">]</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="typ">ContentResult</span><span class="pln"> </span><span class="typ">PublicInfo</span><span class="pun">(){</span><span class="pln">
</span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Content</span><span class="pun">(</span><span class="str">"Public informations here"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
}
}
运行此应用程序,您可以登录登录访问/Secret/PublicInfo,但其他操作将需要身份验证。

它将仅允许匿名用户执行此操作。
使用Authorize属性,您还可以指定一些参数,如允许某些特定用户执行此操作。
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");
}
</span><span class="pun">[</span><span class="typ">AllowAnonymous</span><span class="pun">]</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="typ">ContentResult</span><span class="pln"> </span><span class="typ">PublicInfo</span><span class="pun">(){</span><span class="pln">
</span><span class="kwd">return</span><span class="pln"> </span><span class="typ">Content</span><span class="pun">(</span><span class="str">"Public informations here"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span><span class="pln">
}
}
当您运行此应用程序并转到/secret/secret时,它将要求您登录,因为它不是此控制器的正确用户。