受權過濾器用於實現IAuthorizationFilter接口和作出關因而否執行操做方法(如執行身份驗證或驗證請求的屬性)的安全策略。AuthorizeAttribute類繼承了IAuthorizationFilter接口,是受權過濾器的示例。受權過濾器在任何其餘過濾器以前運行。html
若是要自定義受權過濾器,只須要定義一個類繼承自AuthorizeAttribute類,而後重寫AuthorizeAttribute類裏面的方法便可。git
下面根據一個具體的案例來說解如何使用自定義過濾器github
User實體類代碼以下:數據庫
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace MVCCustomerFilterDemo.Models { public class User { public int Id { get; set; } public string UserName { get; set; } public int RoleId { get; set; } } }
Role實體類代碼以下:緩存
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace MVCCustomerFilterDemo.Models { public class Role { public int Id { get; set; } public string RoleName { get; set; } public string Description { get; set; } } }
RoleWithControllerAction實體類代碼以下:安全
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace MVCCustomerFilterDemo.Models { public class RoleWithControllerAction { public int Id { get; set; } public string ControllerName { get; set; } public string ActionName { get; set; } public string RoleIds { get; set; } } }
用於展現登陸視圖的登陸用戶實體類LogOnViewModel代碼以下:cookie
using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Web; namespace MVCCustomerFilterDemo.Models { // <summary> /// 用戶登陸類 /// </summary> public class LogOnViewModel { /// <summary> /// 用戶名 /// </summary> [DisplayName("用戶名")] public string UserName { get; set; } /// <summary> /// 密碼 /// </summary> [DisplayName("密碼")] public string Password { get; set; } /// <summary> /// 記住我 /// </summary> [DisplayName("記住我")] public bool RememberMe { get; set; } } }
在程序中模擬數據庫中的數據,實際使用中要去數據庫查詢,代碼以下:ide
using MVCCustomerFilterDemo.Models; using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace MVCCustomerFilterDemo.DataBase { /// <summary> /// 測試數據(實際項目中,這些數據應該從數據庫拿) /// </summary> public class SampleData { public static List<User> users; public static List<Role> roles; public static List<RoleWithControllerAction> roleWithControllerAndAction; static SampleData() { // 初始化用戶 users = new List<User>() { new User(){ Id=1, UserName="jxl", RoleId=1}, new User(){ Id=2, UserName ="senior1", RoleId=2}, new User(){ Id=3, UserName ="senior2", RoleId=2}, new User(){ Id=5, UserName="junior1", RoleId=3}, new User(){ Id=6, UserName="junior2", RoleId=3}, new User(){ Id=6, UserName="junior3", RoleId=3} }; // 初始化角色 roles = new List<Role>() { new Role() { Id=1, RoleName="管理員", Description="管理員角色"}, new Role() { Id=2, RoleName="高級會員", Description="高級會員角色"}, new Role() { Id=3, RoleName="初級會員", Description="初級會員角色"} }; // 初始化角色控制器和Action對應類 roleWithControllerAndAction = new List<RoleWithControllerAction>() { new RoleWithControllerAction(){ Id=1, ControllerName="AuthFilters", ActionName="AdminUser", RoleIds="1"}, new RoleWithControllerAction(){ Id=2, ControllerName="AuthFilters", ActionName="SeniorUser",RoleIds="1,2"}, new RoleWithControllerAction(){ Id=3, ControllerName="AuthFilters", ActionName="JuniorUser",RoleIds="1,2,3"}, new RoleWithControllerAction(){ Id=3, ControllerName="AuthFilters", ActionName="Welcome",RoleIds="1,2"}, new RoleWithControllerAction(){ Id=4, ControllerName="ActionFilters", ActionName="Index", RoleIds="2,3"}, new RoleWithControllerAction(){ Id=4, ControllerName="ActionPremisFilters", ActionName="Index", RoleIds="2,3"} }; } } }
新建一個UserAuthorize類,繼承自AuthorizeAttribute類,而後F12轉到定義查看AuthorizeAttribute代碼,代碼以下:測試
#region 程序集 System.Web.Mvc, Version=5.2.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 // E:\Practice\過濾器\自定義權限過濾器\MVCCustomerFilterDemo\packages\Microsoft.AspNet.Mvc.5.2.4\lib\net45\System.Web.Mvc.dll #endregion namespace System.Web.Mvc { // // 摘要: // 指定對控制器或操做方法的訪問只限於知足受權要求的用戶。 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter { // // 摘要: // 初始化 System.Web.Mvc.AuthorizeAttribute 類的新實例。 public AuthorizeAttribute(); // // 摘要: // 獲取或設置有權訪問控制器或操做方法的用戶角色。 // // 返回結果: // 有權訪問控制器或操做方法的用戶角色。 public string Roles { get; set; } // // 摘要: // 獲取此特性的惟一標識符。 // // 返回結果: // 此特性的惟一標識符。 public override object TypeId { get; } // // 摘要: // 獲取或設置有權訪問控制器或操做方法的用戶。 // // 返回結果: // 有權訪問控制器或操做方法的用戶。 public string Users { get; set; } // // 摘要: // 在過程請求受權時調用。 // // 參數: // filterContext: // 篩選器上下文,它封裝有關使用 System.Web.Mvc.AuthorizeAttribute 的信息。 // // 異常: // T:System.ArgumentNullException: // filterContext 參數爲 null。 public virtual void OnAuthorization(AuthorizationContext filterContext); // // 摘要: // 重寫時,提供一個入口點用於進行自定義受權檢查。 // // 參數: // httpContext: // HTTP 上下文,它封裝有關單個 HTTP 請求的全部 HTTP 特定的信息。 // // 返回結果: // 若是用戶已通過受權,則爲 true;不然爲 false。 // // 異常: // T:System.ArgumentNullException: // httpContext 參數爲 null。 protected virtual bool AuthorizeCore(HttpContextBase httpContext); // // 摘要: // 處理未能受權的 HTTP 請求。 // // 參數: // filterContext: // 封裝有關使用 System.Web.Mvc.AuthorizeAttribute 的信息。filterContext 對象包括控制器、HTTP 上下文、請求上下文、操做結果和路由數據。 protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext); // // 摘要: // 在緩存模塊請求受權時調用。 // // 參數: // httpContext: // HTTP 上下文,它封裝有關單個 HTTP 請求的全部 HTTP 特定的信息。 // // 返回結果: // 對驗證狀態的引用。 // // 異常: // T:System.ArgumentNullException: // httpContext 參數爲 null。 protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext); } }
從AuthorizeAttribute的源代碼中能夠看出:裏面定義了Users和Roles兩個屬性,只須要給這兩個屬性賦值,就能夠控制用戶或角色訪問了。要實現自定義的驗證只須要重寫OnAuthorization和AuthorizeCore方法。因此,UserAuthorize類代碼以下:this
using MVCCustomerFilterDemo.DataBase; using MVCCustomerFilterDemo.Models; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace MVCCustomerFilterDemo.Extensions { public class UserAuthorize : AuthorizeAttribute { /// <summary> /// 受權失敗時呈現的視圖 /// </summary> public string AuthorizationFailView { get; set; } /// <summary> /// 請求受權時執行 /// </summary> public override void OnAuthorization(AuthorizationContext filterContext) { // 判斷是否已經驗證用戶 if (!filterContext.HttpContext.User.Identity.IsAuthenticated) { // 若是沒有驗證則跳轉到LogOn頁面 filterContext.HttpContext.Response.Redirect("/Account/LogOn"); } //得到url請求裏的controller和action: string strControllerName = filterContext.RouteData.Values["controller"].ToString().ToLower(); string strActionName = filterContext.RouteData.Values["action"].ToString().ToLower(); //根據請求過來的controller和action去查詢能夠被哪些角色操做: Models.RoleWithControllerAction roleWithControllerAction = SampleData.roleWithControllerAndAction.Find(r => r.ControllerName.ToLower() == strControllerName && r.ActionName.ToLower() == strActionName); if (roleWithControllerAction != null) { //有權限操做當前控制器和Action的角色id this.Roles = roleWithControllerAction.RoleIds; } base.OnAuthorization(filterContext); } /// <summary> /// 自定義受權檢查(返回False則受權失敗) /// </summary> protected override bool AuthorizeCore(HttpContextBase httpContext) { if (httpContext.User.Identity.IsAuthenticated) { //當前登陸用戶的用戶名 string userName = httpContext.User.Identity.Name; //當前登陸用戶對象 User user = SampleData.users.Find(u => u.UserName == userName); if (user != null) { //當前登陸用戶的角色 Role role = SampleData.roles.Find(r => r.Id == user.RoleId); foreach (string roleid in Roles.Split(',')) { if (role.Id.ToString() == roleid) return true; } return false; } else return false; } else { //進入HandleUnauthorizedRequest return false; } } /// <summary> /// 處理受權失敗的HTTP請求 /// </summary> protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { filterContext.Result = new ViewResult { ViewName = AuthorizationFailView }; } } }
Account控制器裏面的LogOn方法用來顯示登錄界面,控制器代碼以下:
using MVCCustomerFilterDemo.Models; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Security; namespace MVCCustomerFilterDemo.Controllers { public class AccountController : Controller { // GET: Account public ActionResult Index() { return View(); } /// <summary> /// 顯示登陸視圖 /// </summary> /// <returns></returns> public ActionResult LogOn() { LogOnViewModel model = new LogOnViewModel(); return View(model); } /// <summary> /// 處理用戶點擊登陸提交回發的表單 /// </summary> /// <param name="model"></param> /// <returns></returns> [HttpPost] public ActionResult LogOn(LogOnViewModel model) { //只要輸入的用戶名和密碼同樣就過 if (model.UserName.Trim() == model.Password.Trim()) { // 判斷是否勾選了記住我 if (model.RememberMe) { //2880分鐘有效期的cookie FormsAuthentication.SetAuthCookie(model.UserName, true); } else { //會話cookie FormsAuthentication.SetAuthCookie(model.UserName, false); } // 跳轉到AuthFilters控制器的Welcome方法 return RedirectToAction("Welcome", "AuthFilters"); } else { return View(model); } } /// <summary> /// 註銷 /// </summary> /// <returns></returns> public ActionResult LogOut() { Session.Abandon(); FormsAuthentication.SignOut(); return RedirectToAction("LogOn"); } } }
LogOn方法對應的視圖頁面代碼以下:
@model MVCCustomerFilterDemo.Models.LogOnViewModel @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>LogOn</title> </head> <body> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-horizontal"> <h4>登陸</h4> <hr /> @Html.ValidationSummary(true) <div class="form-group"> @Html.LabelFor(model => model.UserName, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.UserName) @Html.ValidationMessageFor(model => model.UserName) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Password, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Password) @Html.ValidationMessageFor(model => model.Password) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.RememberMe, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.RememberMe) @Html.ValidationMessageFor(model => model.RememberMe) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="登陸" class="btn btn-default" /> </div> </div> </div> } </body> </html>
修改配置文件,定義權限驗證失敗時跳轉的頁面,代碼以下:
<!--配置登陸頁面--> <authentication mode="Forms"> <forms loginUrl="~/Account/LogOn" timeout="2880" /> </authentication>
添加AuthFilters控制器,代碼以下:
using MVCCustomerFilterDemo.Extensions; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace MVCCustomerFilterDemo.Controllers { public class AuthFiltersController : Controller { // GET: AuthFilters public ActionResult Index() { return View(); } /// <summary> /// 使用自定義的受權驗證,登陸成功就能夠訪問 /// </summary> /// <returns></returns> [Authorize] public ActionResult Welcome() { return View(); }
[UserAuthorize(AuthorizationFailView = "Error")] public ActionResult AdminUser() { ViewBag.Message = "管理員頁面"; return View("Welcome"); } /// <summary> /// 會員頁面(管理員、會員均可訪問) /// </summary> /// <returns></returns> [Authorize] [UserAuthorize(AuthorizationFailView = "Error")] public ActionResult SeniorUser() { ViewBag.Message = "高級會員頁面"; return View("Welcome"); } /// <summary> /// 遊客頁面(管理員、會員、遊客均可訪問) /// </summary> /// <returns></returns> [Authorize] [UserAuthorize(AuthorizationFailView = "Error")] public ActionResult JuniorUser() { ViewBag.Message = "初級會員頁面"; return View("Welcome"); } } }
Welcome這個Action使用了默認的受權驗證,只要登陸成功就能訪問。
URL地址欄裏面輸入:http://localhost:****/AuthFilters/Welcome,會跳轉到登陸頁面,如圖所示:
而後輸入相同的用戶名和密碼,點擊登陸,會顯示Welcome對應的頁面:
在看一下SampleData中,角色爲1,2的也能夠訪問Welcome方法,用角色1訪問Welcome:
點擊登陸:
從上面的截圖中看出:senior1登陸成功了,senior1是角色2,證實角色一、2能夠訪問Welcome方法。在使用junior2登陸名訪問Welcome方法:
因爲junior2的角色是3,而角色3沒有訪問Welcome方法的權限,因此會跳轉到Error頁面:
Welcome這個Action使用了默認的受權驗證,只要登錄成功就能夠訪問。其餘幾個Action上都標註了自定義的UserAuthorize,並無標註Users="....",Roles=".....",由於這樣在Action上寫死用戶或者角色控制權限顯然是不可行的,用戶和角色的對應以及不一樣的角色能夠操做的Action應該是從數據庫裏取出來的。爲了演示就在SampleData類裏初始化了一些用戶和角色信息,根據SampleData類的定義,很明顯jxl擁有1號管理員角色,能夠訪問AuthFilters這個控制器下的全部Action;senior一、senior2擁有2號高級會員的角色,能夠訪問AuthFilters這個控制器下除了AdminUser以外的Action等等。
再次登錄下,就發現擁有高級會員角色的用戶senior1是不能夠訪問AdminUser這個Action,會被帶到AuthorizationFailView屬性指定的Error視圖。
GitHub代碼地址:git@github.com:JiangXiaoLiang1988/MVCCustomerFilterDemo.git