ASP.NET MVC 不像 ASP.NET WEB FORMS 那样提供了不少自动保护机制来保护页面不受恶意用户的攻击,更明确的说,后者是致力于使应用程序免受攻击:程序员
ASP.NET MVC 对标记和程序的运行提供了更多控制,这意味着程序员要承担更多的责任。web
之因此应用程序存在安全隐患,主要是由于开发人员缺少足够的信息或理解。另外,人无完人,不免有疏忽的时候,鉴于此,下面是本章的关键总结:数据库
黑客、解密高手、垃圾邮件发送者、病毒、恶意软件,它们都想进入计算机并查看或破坏里面的数据!浏览器
保护应用程序的第一步,也是最简单的一步,就是要求登陆系统的用户访问那些由应用程序指定的 URL。咱们能够经过控制器上或控制器内部特定操做上的 Authorize 操做过滤器来实现。安全
Authorize 特性是 ASP.NET MVC 自带的默认受权过滤器,可限制用户对操做方法的访问,若该特性运用于控制器,则会应用于控制器内部全部操做方法。服务器
有时会对用户身份验证和用户受权之间的区别感到困惑,这两个词也比较类似!cookie
受权特性不带任何参数,只要求用户以某种角色身份登陆网站,换句话说,禁止匿名访问!app
如今根据一个很是简单的购物应用需求,建立音乐商店的应用程序。程序中的 StoreController 控制器仅包含 2 个操做,Index 和 Buy:框架
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using MvcMusicStore.Models;
namespace MvcMusicStore.Controllers
{
public class StoreManagerController : Controller
{
public ActionResult Index()
{
var albums = GetAlbums();
return View(albums);
}
[Authorize]
public ActionResult Buy(int id)
{
var album = GetAlbums().Single(e => e.AlbumId == id);
return View(album);
}
private static List<Album> GetAlbums()
{
var albums = new List<Album> {
new Album {AlbumId = 1, Title = "The Fall of Math", Price = 8.99M},
new Album {AlbumId = 2, Title = "The Blue Notebooks", Price = 8.99M},
new Album {AlbumId = 3, Title = "Lost in Translation", Price = 9.99M},
new Album {AlbumId = 4, Title = "Permutation", Price = 10.99M}
};
return albums;
}
}
}
若是如今访问 Store 控制器的 Buy 操做时,就会要求登陆。ide
下面内容很是重要,请慢读、理解、记忆:
<location path="Admin" allowOverride="false" />
<system.web>
<authorization>
<allow roles="Administrator"/>
<deny users="?"/>
</authorization>
</system.web>
这种方式在 MVC 框架中没法正常工做,缘由有两个。首先,请求再也不映射到屋里目录;其次,可能存在多种查找同一控制器的方式。
实现安全性的最好方法是,安全性检查尽量的接近要保护的对象。可能有高于堆栈的检查,但最终都要确保实际资源的安全。这样不管用户如何得到资源,该方式都会对其进行安全性检查。因而,也就没必要依赖路由和 URL 受权来确保控制器安全了。
Authorize 特性就起这个做用:
上面例子在后台是如何操做的呢?原来,在 ASP.NET MVC 的 InternetApplication 模板包含一个基本的 AccountController,它支持 ASP.NET Membership 和 OAuth 验证的帐户管理。
Authorize 特性是一个过滤器,它能先于控制器操做执行。首先会执行它在 OnAuthorization 方法中的主要操做,这是一个在接口 IauthorizationFilter 中定义的标准方法,查看源码就会发现,基本的安全机制正在核实 ASP.NET 上下文中存储的基自己份验证信息:
return HttpContext.User.Identity.IsAuthenticated;
若是用户验证失败,就会返回一个 HttpUnauthorizedResult 操做结果,产生一个 HTTP 401(未受权)的状态码。这个状态码被 FormsAuthenticationModule 的 Onleave 方法截获,并转而重定向到配置中的登陆页面。
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>
MVC 框架 InternetApplication 模板提供的这种方式,在简单的应用场合中能够轻松添加受权,而不须要编写任何额外代码及配置。
有时可能会但愿受权级别是控制器,而不是在内部每个操做上添加 Authorize 特性。此时,能够添加 Authorize 特性至 Controller 上。
大部分网站,基本上整个应用程序都是须要受权的。这种情形下,默认受权要求和匿名访问少数网页就变得极其简单。把 AuthorizeAttribute 配置为全局过滤器,而使用 AllowAnonymous 特性标注容许匿名访问的控制器或方法。
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new AuthorizeAttribute());
filters.Add(new HandleErrorAttribute());
}
}
这样就限制了对整个应用程序全部操做的访问,但也别忘了标注容许匿名访问的控制器或操做(若是有的话)。
[Authorize(Roles = "Administrator")]
public class StoreManagerController : Controller
这样就限定了受权访问的用户角色只能是管理员角色。顾名思义(Roles 是复数),传递的角色列表能够是逗号间隔的字符串;也能够受权给一组用户 Users = "xx,xxx";也能够二者同时使用。
固然,应当使用角色而非用户组。另外,当建立角色组时,可考虑使用基于特权的角色分组,像 CanEditAlbums 这样的角色组远比 SuperAdmin、CEOOffice更为精细,更为便于管理。
ASP.NET MVC 的好处之一就是它运行在成熟且功能齐全的 ASP.NET 核心之上。而 ASP.NET MVC 中的身份验证和受权创建在 System.Web.Security 命名空间中的 Role 类和 Membership 类之上。这样作是有好处的:
运用本地数据库维护用户信息也有一些严重的负面影响:
OAuth 和 OpenID 是开放的受权标准。这些协议容许用户使用他们已有的帐户登陆咱们的网站,这些帐户来自于他们信任的网站(提供器)。过去,配置网站以支持 OAuth 和 OpenID 是很是难以实现的。缘由有如下两点:首先是协议复杂,而后是顶级提供器对这两种协议的实现方式不同。
MVC 4 经过在 Internet 模板中内置支持 OAuth 和 OpenID 极大化的简化了这一点。这种支持包括了一个更新的 AccountController、便于注册和帐户管理的视图以及构建在流行库 DotNetOpenAuth 之上的工具类!
新的登陆页面会出现两个登陆的选项,以下图:
须要显式的启用外部网站,以便利用它们的帐户登陆咱们的网站。可喜的是,这个操做很是简单,能够在 AuthConfig.cs 中配置受权提供程序。默认文件中的全部验证提供器都会注释掉,以下:
public static void RegisterAuth()
{
// 若要容许此站点的用户使用他们在其余站点(例如 Microsoft、Facebook 和 Twitter)上拥有的账户登陆,
// 必须更新此站点。有关详细信息,请访问 http://go.microsoft.com/fwlink/?LinkID=252166
//OAuthWebSecurity.RegisterMicrosoftClient(
// clientId: "",
// clientSecret: "");
//OAuthWebSecurity.RegisterTwitterClient(
// consumerKey: "",
// consumerSecret: "");
//OAuthWebSecurity.RegisterFacebookClient(
// appId: "",
// appSecret: "");
//OAuthWebSecurity.RegisterGoogleClient();
}
使用OAuth提供器的网站(如Facebook、Twitter等)要求咱们把网站注册为一个应用程序,这样它们就会提供咱们一个客户端 id 和一个口令,咱们利用 OAuth 提供器就能够进行验证。利用 OpenID(如 Google 和 Yahoo)的网站不须要注册应用程序,咱们也不须要客户端 id 和口令。
因为不用注册,不用填写参数,所以配置 OpenID 提供器是很是简单的,Google、Yahoo 等都有现成的实现,而 myOpenID 须要建立注册一个自定义的客户端(经过 using 语句引入一些必要的命名空间,位于 DotNetOpenAuth 下):
using Microsoft.Web.WebPages.OAuth;
using DotNetOpenAuth.AspNet.Clients;
using DotNetOpenAuth.OpenId.RelyingParty;
namespace OAuthMVC
{
public static class AuthConfig
{
public static void RegisterAuth()
{
// 配置 Google 提供器
OAuthWebSecurity.RegisterGoogleClient();
// 配置 Yahoo 提供器
OAuthWebSecurity.RegisterYahooClient();
// 配置 myOpenID 提供器,先建立 OpenIdClient,再进行注册
var MyOpenIdClient = new OpenIdClient("myopenid", WellKnownProviders.MyOpenId);
OAuthWebSecurity.RegisterClient(MyOpenIdClient, "MyOpenID", null);
}
}
}
运行程序,测试登陆的效果,如图:
Google 被和谐,点击 Yahoo 能够导航到登陆界面:
输入帐户密码以后,Yahoo 询问是否赞成登陆咱们的网站,选择 Agree:
验证成功,并受权后,浏览器返回咱们的站点,此时能够完成一些咱们站点的注册步骤,并在单击注册后,会被做为一个已认定的用户重定向到主页:
注册成功:
点击用户名能够管理本身的帐户,添加一个本地帐户和密码或者绑定额外的外部登陆提供器:
须要在第三方网站将本身的站点注册为一个 App,以后使用得到的 AppID 和密钥就能够注册了,例以下面的 Facebook 站点:
public static void RegisterAuth()
{
OAuthWebSecurity.RegisterFacebookClient(appId: "123456789", appSecret: "abcdefg");
}
尽管 OAuth 和 OpenID 简化了安全性编码,但也给应用程序引入了其余潜在的攻击媒介,若是一个提供器网站被破坏,或者网站之间的安全通讯遭到破坏,攻击者可能会破坏咱们的网站登陆、或者捕获用户信息。所以要作到如下几点:
1. 可信的外部登陆提供器,使用知名的提供器,只支持咱们信任的站点
2. 要求 SSL 登陆,外部提供器到网站的回调中包含拥有用户信息的安全令牌,当令牌在互联网传递时,使用 HTTPS 传输是很重要的,这样能够防止信息被拦截。
[RequireHttps]
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}