這一節我們來跑通整個系統,驗證的流程,通過AOP切入方式,在訪問方法之前,執行一個驗證機制來判斷是否有操作權限(如:增刪改等)
原理:通過MVC自帶篩選器,在篩選器分解路由的Action和controller來驗證是否有權限。

首先我們要理解一下篩選器
篩選器的由來及用途
有時,您需要在調用操作方法之前或運行操作方法之后執行邏輯。
為了對此提供支持,ASP.NET MVC 提供了篩選器。 篩選器是自定義類,可提供用於向控制器操作方法添加操作前行為和操作后行為的聲明性和編程性手段。
ASP.NET MVC 支持以下類型的操作篩選器:
授權篩選器。 這些篩選器用於實現 IAuthorizationFilter 和做出關於是否執行操作方法(如執行身份驗證或驗證請求的屬性)的安全決策。 AuthorizeAttribute 類和 RequireHttpsAttribute 類是授權篩選器的示例。 授權篩選器在任何其他篩選器之前運行。
操作篩選器。 這些篩選器用於實現 IActionFilter 以及包裝操作方法執行。 IActionFilter 接口聲明兩個方法:OnActionExecuting 和 OnActionExecuted。 OnActionExecuting 在操作方法之前運行。 OnActionExecuted 在操作方法之后運行,可以執行其他處理,如向操作方法提供額外數據、檢查返回值或取消執行操作方法。
結果篩選器。 這些篩選器用於實現 IResultFilter 以及包裝 ActionResult 對象的執行。 IResultFilter 聲明兩個方法:OnResultExecuting 和 OnResultExecuted。 OnResultExecuting 在執行 ActionResult 對象之前運行。 OnResultExecuted 在結果之后運行,可以對結果執行其他處理,如修改 HTTP 響應。 OutputCacheAttribute 類是結果篩選器的一個示例。
異常篩選器。 這些篩選器用於實現 IExceptionFilter,並在 ASP.NET MVC 管道執行期間引發了未處理的異常時執行。 異常篩選器可用於執行諸如日志記錄或顯示錯誤頁之類的任務。 HandleErrorAttribute 類是異常篩選器的一個示例。
創建自定義操作篩選器
框架將先調用操作篩選器的 OnActionExecuting 方法,然后再調用以操作篩選器特性標記的任意操作方法。 同樣,該框架將在操作方法完成后調用 OnActionExecuted 方法。
調用 OnResultExecuting 方法后,要立即調用您的操作返回的 ActionResult 實例。 執行結果后,緊接着就要調用 OnResultExecuted 方法。 這些方法對於執行日志記錄、緩存輸出結果之類的操作非常有用。
以上都是理論問題了,說到底我們就是要OnActionExecuting利用這個方法
當一個Action被執行時調用OnActionExecuting判斷是否有權限操作。
由於OnActionExecuting涉及到其他用戶和權限的訪問我們需要添加SysUser和SysRight的BLL和DAL層了
我們還需要一個存儲過程[P_Sys_GetRightOperate]用於取模塊的當前用戶操作權限,這里為什么用存儲過程呢,快點唄,反正這快不用怎么維護了
存儲過程如下:
USE db GO /****** Object: StoredProcedure [dbo].[P_Sys_GetRightOperate] Script Date: 12/01/2013 12:25:48 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- ============================================= -- Author: <Author,,Name> -- Create date: <Create Date,,> -- Description: <Description,,> -- ============================================= CREATE PROCEDURE [dbo].[P_Sys_GetRightOperate] @userId varchar(50),@url varchar(200) AS --取模塊的當前用戶操作權限 select distinct KeyCode,IsValid from SysRightOperate where RightId in( select a.id from SysRight a, SysModule b where RoleId in( select SysRoleId from SysRoleSysUser where SysUserId =@userId) and a.ModuleId = b.Id and b.Url =@url) and IsValid=1 GO
創建好了把存儲過程更新到EF中去,EF5.0將自動創建一個復雜的類型,大家可以打開來看下
創建一個權限的類permModel,我們將獲取到的權限保存到這個類中去,這個類最終是一個一個的session轉換而來的。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace App.Models.Sys { public class permModel { public string KeyCode { get; set; }//操作碼 public bool IsValid { get; set; }//是否驗證 } }
SysUser的BLL層和SysRight的DAL層了,如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using App.Models; using App.Models.Sys; namespace App.IDAL { public interface ISysRightRepository { List<permModel> GetPermission(string accountid, string controller); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using App.IDAL; using App.Models; using App.Models.Sys; namespace App.DAL { public class SysRightRepository : ISysRightRepository,IDisposable { /// <summary> /// 取角色模塊的操作權限,用於權限控制 /// </summary> /// <param name="accountid">acount Id</param> /// <param name="controller">url</param> /// <returns></returns> public List<permModel> GetPermission(string accountid, string controller) { using (DBContainer db = new DBContainer()) { List<permModel> rights = (from r in db.P_Sys_GetRightOperate(accountid, controller) select new permModel { KeyCode = r.KeyCode, IsValid = r.IsValid }).ToList(); return rights; } } public void Dispose() { } } }
GetPermission將通過存儲過程訪問取得數據
using System; using System.Collections.Generic; using System.Linq; using System.Text; using App.Models.Sys; using App.Common; using App.Models; namespace App.IBLL { public interface ISysUserBLL { List<permModel> GetPermission(string accountid, string controller); } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using App.BLL.Core; using App.IBLL; using Microsoft.Practices.Unity; using App.IDAL; using App.Models.Sys; using App.Common; using App.Models; using System.Transactions; namespace App.BLL { public class SysUserBLL : BaseBLL, ISysUserBLL { [Dependency] public ISysRightRepository sysRightRepository { get; set; } public List<permModel> GetPermission(string accountid, string controller) { return sysRightRepository.GetPermission(accountid,controller); } } }
可以把SysRightRepository變成SysUserRepository層,我這樣做是為了區分一下而已,SysRight代表權限,SysUser是用戶,根據不同的用戶獲取他的權限
我們創建一個篩選器在App.Admin下的Core創建SupportFilter.cs
添加如下代碼:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; using App.Models.Sys; using App.BLL; using App.DAL; namespace App.Admin { public class SupportFilterAttribute : ActionFilterAttribute { public string ActionName { get; set; } private string Area; public override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); } /// <summary> /// Action加上[SupportFilter]在執行actin之前執行以下代碼,通過[SupportFilter(ActionName="Index")]指定參數 /// </summary> /// <param name="filterContext">頁面傳過來的上下文</param> public override void OnActionExecuting(ActionExecutingContext filterContext) { //讀取請求上下文中的Controller,Action,Id var routes = new RouteCollection(); RouteConfig.RegisterRoutes(routes); RouteData routeData = routes.GetRouteData(filterContext.HttpContext); //取出區域的控制器Action,id string ctlName = filterContext.Controller.ToString(); string[] routeInfo = ctlName.Split('.'); string controller = null; string action = null; string id = null; int iAreas = Array.IndexOf(routeInfo, "Areas"); if (iAreas > 0) { //取區域及控制器 Area = routeInfo[iAreas + 1]; } int ctlIndex = Array.IndexOf(routeInfo, "Controllers"); ctlIndex++; controller = routeInfo[ctlIndex].Replace("Controller", "").ToLower(); string url = HttpContext.Current.Request.Url.ToString().ToLower(); string[] urlArray = url.Split('/'); int urlCtlIndex = Array.IndexOf(urlArray, controller); urlCtlIndex++; if (urlArray.Count() > urlCtlIndex) { action = urlArray[urlCtlIndex]; } urlCtlIndex++; if (urlArray.Count() > urlCtlIndex) { id = urlArray[urlCtlIndex]; } //url action = string.IsNullOrEmpty(action) ? "Index" : action; int actionIndex = action.IndexOf("?", 0); if (actionIndex > 1) { action = action.Substring(0, actionIndex); } id = string.IsNullOrEmpty(id) ? "" : id; //URL路徑 string filePath = HttpContext.Current.Request.FilePath; AccountModel account = filterContext.HttpContext.Session["Account"] as AccountModel; if (ValiddatePermission(account, controller, action, filePath)) { return; } else { filterContext.Result = new EmptyResult(); return; } } public bool ValiddatePermission(AccountModel account, string controller, string action, string filePath) { bool bResult = false; string actionName = string.IsNullOrEmpty(ActionName) ? action : ActionName; if (account != null) { List<permModel> perm = null; //測試當前controller是否已賦權限值,如果沒有從 //如果存在區域,Seesion保存(區域+控制器) if (!string.IsNullOrEmpty(Area)) { controller = Area + "/" + controller; } perm = (List<permModel>)HttpContext.Current.Session[filePath]; if (perm == null) { using (SysUserBLL userBLL = new SysUserBLL() { sysRightRepository = new SysRightRepository() }) { perm = userBLL.GetPermission(account.Id, controller);//獲取當前用戶的權限列表 HttpContext.Current.Session[filePath] = perm;//獲取的勸降放入會話由Controller調用 } } //當用戶訪問index時,只要權限>0就可以訪問 if (actionName.ToLower() == "index") { if (perm.Count > 0) { return true; } } //查詢當前Action 是否有操作權限,大於0表示有,否則沒有 int count = perm.Where(a => a.KeyCode.ToLower() == actionName.ToLower()).Count(); if (count > 0) { bResult = true; } else { bResult = false; HttpContext.Current.Response.Write("你沒有操作權限,請聯系管理員!"); } } return bResult; } public override void OnResultExecuted(ResultExecutedContext filterContext) { base.OnResultExecuted(filterContext); } public override void OnResultExecuting(ResultExecutingContext filterContext) { base.OnResultExecuting(filterContext); } } }
這是一個篩選器。所有邏輯代碼都在這里了
打開SysSampleController
修改Create方法變為以下
[SupportFilter] public ActionResult Create() { return View(); }
回到篩選器public string ActionName { get; set; },其中ActionName是自定義Action的名稱,比如在Create中直接[SupportFilter]那么ActionName取得就是Create,這將和你的數據庫操作碼進行對應的,那么我的方法是CreateAttr,那么要使用Create這個操作碼,怎么辦
那么就是
[SupportFilter(ActionName = "Create")] public ActionResult CreateAttr()
那么類似的寫法
[SupportFilter(ActionName = "Index")] public JsonResult GetList()
Index無需填寫操作碼將自動創建操作碼,如果你擁有一個操作碼那么index將被授權,這個是我們與系統之間的一個約定(你可以去掉這個約定,修改代碼即可)
假如你擁有增刪改權限卻沒有訪問列表的權限,那不是...
OnActionExecuting負責分解,交給ValiddatePermission去生成權限
如果寫在Areas區域的也是兼容的,已經做了處理。
如果你越權操作那么將執行 HttpContext.Current.Response.Write("你沒有操作權限,請聯系管理員!");
目前位置我們已經跑通了整個系統了,接下來就是自動化的用戶角色之間的授權和模塊的制作了,能跑通,其他都是很簡單了,對吧
這一章比較復雜,需要對AOP編程,MVC的篩選器,和路由進行了解,才能讀的比較順。
如果你沒有讀懂,那么代碼敲一遍,那么你也就差不多知道了
代碼進行了大量的注釋,還不懂那么留言。
目前為止,我們一個基於按鈕級別的權限系統已經全部跑通,現在,可以創建一些沒有權限的Action來驗證了
我創建:(很明顯我們數據庫沒有這個test的 action的權限),所以你別想越權操作了
[SupportFilter] public ActionResult Test() { return View(); }
最后預覽

我們預覽一個有權限的

感謝大家,花了你寶貴的時間閱讀這一節。
