在本章中,我们将讨论如何在应用程序中实现安全功能。还将看到asp.net中包含的新成员特性,并可从asp.net mvc中使用。在asp.net的最新版本中,可以通过以下方式管理用户身份 -
在本章中,我们将介绍作为asp.net一部分的新身份组件,并了解如何自定义用户和角色的成员资格。
用户的认证意味着验证用户的身份,这真的很重要。可能需要将您的应用程序仅显示给经过身份验证的用户,原因很明显。
我们来创建一个新的asp.net mvc应用程序项目:mvcsecurity 。点击确定 继续。

当启动一个新的asp.net应用程序时,这个过程中的一个步骤就是为应用程序需要配置身份验证服务。选择mvc模板,将看到现在启用了更改认证按钮。

这是通过出现在“新建项目”对话框中的“更改认证”按钮完成的。默认身份验证是个人用户帐户。
当单击更改按钮时,您将看到一个对话框,其中包含四个选项,如下所示。
第一个选项是不验证,当想建立一个不关心访问者是谁的网站时使用这个选项。
它是开放给任何人和每个人连接为每一个页面。以后可以随时更改,但“不进行身份验证” 选项意味着不会有任何功能来识别访问该网站的用户。

第二个选项是个人用户帐户,这是用户可以访问网站的传统的基于表单的身份验证。 他们可以注册,创建一个登录名,默认情况下,用户名是使用一些新的asp.net身份特性存储在sql server数据库中。

密码也存储在数据库中,但首先被散列。由于密码是散列的,因此不必担心在数据库中的纯文本密码而被别人知道。
此选项通常用于要建立用户身份的internet站点。 除了允许用户使用网站的密码创建本地登录外,还可以启用来自microsoft,google,facebook和twitter等第三方的登录。
这允许用户使用他们的真实帐户或他们的twitter帐户登录到您的网站,但是您不需要存储任何密码。
这将在这个模块中花费一些时间的选项。个人用户帐户 选项。
第三个选择是使用组织帐户,这通常用于使用活动目录联合服务的业务应用程序。
将设置office 365或使用azure active directory服务,并且您有内部应用程序和云应用程序的单一登录。
您还需要提供应用程序id,以便应用程序需要在windows azure管理门户(如果这是基于azure的)上进行注册,并且应用程序id将在所有可能注册的应用程序中唯一标识此应用程序。
第四个选项是windows身份验证 ,适用于intranet应用程序。
用户登录到windows桌面,并可以将浏览器启动到位于同一防火墙内的应用程序。 asp.net可以自动获取用户的身份,即由活动目录建立的身份。 该选项不允许任何匿名访问该站点,但这也是一个可以更改的配置设置。
下面我们来看看基于表单的身份验证 ,即名称为个人用户帐户的身份验证。 此应用程序将用户名和密码,旧密码存储在本地sql server数据库中,创建此项目时,visual studio也将添加nuget包。

现在运行这个应用程序,当您第一次访问这个应用程序,那么是以一个匿名用户来访问的。

因为您还没有登录的账户,所以需要在这个网站上先注册一个用户。
点击注册 链接,会看到下面的视图。

输入您的电子邮件id和密码,例如:maxsu@h3.com和abc@123。
点击注册。 现在,应用程序将使用此账户信息识别您。
它将能够显示用户的名字。 在下面的截图中,可以看到:“你好,maxsu@h3.com!” 。 可以点击它链接到一个页面,可以在这个页面中更改密码。

也可以注销,关闭,重新启动,一个星期后回来,应该可以使用之前使用的凭据登录。现在点击注销 按钮,它将显示以下页面。再次点击登录链接进入下一页。可以使用相同的凭据重新登录。
很多工作都在幕后进行到现在。 但是,我们想要做的是检查每个功能,看看这个ui是如何构建的。 什么是管理注销和登录过程? 这些信息在数据库中排序?
让我们从一些简单的基础开始。 首先,我们将看到这个用户名是如何显示的。 从解决方案资源管理器中的view/shared文件夹中打开_layout.cshtml。
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@viewbag.title - 我的 asp.net 应用程序</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" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
@html.actionlink("应用程序名称", "index", "home", new { area = "" }, new { @class = "navbar-brand" })
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>@html.actionlink("主页", "index", "home")</li>
<li>@html.actionlink("关于", "about", "home")</li>
<li>@html.actionlink("联系方式", "contact", "home")</li>
</ul>
@html.partial("_loginpartial")
</div>
</div>
</div>
<div class="container body-content">
@renderbody()
<hr />
<footer>
<p>© @datetime.now.year - 我的 asp.net 应用程序</p>
</footer>
</div>
@scripts.render("~/bundles/jquery")
@scripts.render("~/bundles/bootstrap")
@rendersection("scripts", required: false)
</body>
</html>
有一个共同的导航栏,应用程序名称,菜单,并有一个局部视图呈现为_loginpartial。 这实际上是显示用户名或注册和登录名的视图。 所以_loginpartial.cshtml也在shared文件夹中。其代码如下所示 -
@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("你好," + user.identity.getusername() + "!", "index", "manage", routevalues: null, htmlattributes: new { title = "manage" })
</li>
<li><a href="javascript:document.getelementbyid('logoutform').submit()">注销</a></li>
</ul>
}
}
else
{
<ul class="nav navbar-nav navbar-right">
<li>@html.actionlink("注册", "register", "account", routevalues: null, htmlattributes: new { id = "registerlink" })</li>
<li>@html.actionlink("登录", "login", "account", routevalues: null, htmlattributes: new { id = "loginlink" })</li>
</ul>
}
正如可以看到上面,有if/else语句。 如果请求没有被认证,这个视图将显示注册和登录链接。用户可以点击链接登录或注册。所有这些都是由帐户控制器完成的。
现在,我们想看看如何获取用户名,这是在request.isauthenticated。 可以看到对user.identity.getusername的调用。这将检索用户名,在这种情况下是"maxsu@h3.com"
假设想要防止未经验证的用户的信息。 因此,让我们创建一个新的控制器来显示这些信息,但只有当用户登录时才能操作。
右键单击controllers 文件夹,然后选择:添加 -> 控制器 。选择一个mvc 5控制器 - 空 控制器,然后点击“添加”。输入名称secretcontroller,然后单击“添加” 按钮。
它将会有两个动作,如下面的代码所示。参考以下代码 -
using system;
using system.collections.generic;
using system.linq;
using system.web;
using system.web.mvc;
namespace mvcsecurity.controllers
{
public class secretcontroller : controller
{
// get: secret
public actionresult index()
{
return view();
}
// get: secret
public contentresult secret()
{
return content("secret informations here");
}
public contentresult publicinfo()
{
return content("public informations here");
}
}
}
当运行这个应用程序时,可以在没有任何验证的情况下访问这个信息(url:http://localhost:57742/secret/secret),如下面的截图所示。

假设只有经过身份验证的用户才能够使用secret动作方法,并且任何人都可以使用publicinfo,而不需要任何身份验证。
为了保护这个特定的操作并保证未经身份验证的用户到达此处,可以使用authorize属性。 没有任何其他参数的授权属性将确保用户的身份是已知的,他们不是一个匿名用户。
// get: secret
[authorize]
public contentresult secret(){
return content("secret informations here");
}
现在再次运行这个应用程序,并指定相同的url:http://localhost:57742/secret/secret。 mvc应用程序将检测到您无权访问应用程序的特定区域,并且会自动重定向到登录页面,在那里登录并尝试返回访问受限的应用程序的url。

可以看到它在返回url中指定,它告诉此页面,如果用户成功登录,则将其重定向到/secret/secret。
输入您的用户名和密码,然后点击“登录”按钮。会看到它直接进入该页面。
当不想在每一个动作上进行授权的时候,当想要在一个控制器里,几乎所有的事情都需要授权。 在这种情况下,总是可以将此过滤器应用于控制器本身,现在,此控制器内部的每个操作都将要求用户进行身份验证。
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");
}
}
}
使用authorize属性,也可以指定一些参数,比如允许一些特定的用户进入这个动作。
using system.web.mvc;
namespace mvcsecuritydemo.controllers{
[authorize(users = "maxsu@h3.com")]
public class secretcontroller : controller{
// get: secret
public contentresult secret(){
return content("secret informations here");
}
[allowanonymous]
public contentresult publicinfo(){
return content("public informations here");
}
}
}
当运行此应用程序并转到url:/secret/secret时,如果它不是此控制器要求的用户(maxsu@h3.com),它会要求您登录。