[转]Asp.Net大型项目实践(11)-基于MVC Action粒度的权限管理【续】【源码在这里】(在线demo,全部源码)...
本文轉自:http://www.cnblogs.com/legendxian/archive/2010/01/25/1655551.html
接上篇Asp.Net大型項目實踐(10)-基于MVC Action粒度的權限管理(在線demo,全部源碼)
在線Demo:
地址:http://218.60.8.35:1234/
服務器:網通
端口:不要禁用1234端口應該就可以訪問
注意:連了數據庫的,時間倉促肯定有漏洞,不要搗亂哈:)
登錄用戶: 1.用戶名:牛頭人戰士 密碼:000000 權限:有全部菜單頁面,不能進行數據庫的更改操作(不影響錄入體驗)
?2.用戶名:老虎MM? 密碼:000000? 權限:少兩個菜單頁面,不能進行數據庫的更改操作(不影響錄入體驗)
3.用戶名:admin 密碼不公開 權限:所有權限
注:以上的實現都是通過權限管理s配置出的哈,沒有任何硬編碼
權限判斷的邊界
由于項目是基于MVC的,除去數據權限不說,功能權限的判斷邊界做在MVC 的Action上無疑是最好的選擇,因為無論是一個頁面,還是一個按鈕,還是一次查詢,都是通過Action請求實現的。這樣我們只需要在每個Action請求執行之前進行權限判斷就可以了,也不用折騰RBAC里的資源+操作=權限 這么麻煩。
?
菜單權限和功能權限
其實在MIS項目中,大多數的權限判斷粒度還是頁面級的,再加上我們還需要根據權限動態生成用戶的菜單,所以我們把權限分成“菜單權限”和“功能權限”
菜單權限:在用戶登錄驗證后,每個頁面的請求都必須通過權限驗證。
功能權限:默認客戶進入頁面后,頁面的相關操作默認都不判斷,只對顯示維護出的功能權限進行權限判斷。
這樣有幾個好處:一般情況下權限的配置簡單了,因為只需要配置粗粒度的頁面權限即可使用;增加了效率,不必每個Action執行之前都判斷權限(雖然都做了緩存,但能少判斷一次還是好的);完全不影響細粒度的權限判斷,隨時都可以增加對任何一個Action的權限判定
?
如何取Action功能權限
我們通過反射把所有的Action權限全部取出來,這樣在維護選取的時候就比較方便了,也不會產生錄入錯誤,如下圖:
大家用Demo可以體驗到我們模糊輸入Action名稱就可以找到我們想要的Action的,因為是配置選取用也不用擔心什么反射的效率問題,其實大家從demo可以看到速度還是挺快的,在我真實的項目中Action中有上萬個,拉出來一樣是瞬時的,所以我覺得有時候吧,也別過于“談反射色變”,呵呵
通過反射獲取所有Action的代碼如下:
代碼 public IList<ActionPermission> GetAllActionByAssembly() { var result = new List<ActionPermission>();var types = Assembly.Load("Demo.HIS.MVC").GetTypes();
foreach (var type in types) { if (type.BaseType.Name == "BaseController")//如果是Controller { var members = type.GetMethods(); foreach (var member in members) { if (member.ReturnType.Name == "ActionResult")//如果是Action {
var ap = new ActionPermission();
ap.ActionName = member.Name; ap.ControllerName = member.DeclaringType.Name.Substring(0, member.DeclaringType.Name.Length - 10); // 去掉“Controller”后綴 object[] attrs = member.GetCustomAttributes(typeof(System.ComponentModel.DescriptionAttribute), true); if (attrs.Length > 0) ap.Description = (attrs[0] as System.ComponentModel.DescriptionAttribute).Description;
result.Add(ap); }
} } } return result; }
返回的IList<ActionPermission>就是系統中所有Action的集合,大家可看到我們通過BaseController找到了項目中所有的Controller,再通過ActionResult找到Controller中所有的Action。
不知道大家注意下拉出的Action有個描述屬性,這個屬性是通過在Action上定義DescriptionAttribute實現的,這樣通過反射就能取到中文描述了,例如:為了實現頁面的選取方便,我們還要實現對IList<ActionPermission>的分頁和模糊查詢,因為是變量級集合,這里我們使用Linq查詢就可以了,代碼如下:
[Description("訪問功能權限管理頁面")] [ViewPage] public ActionResult ActionPermission() { return View(); }?
代碼 public IList<ActionPermission> QueryActionPlist(string query, int start, int limit, out long total) { IList<ActionPermission> allActions = GetAllActionByAssembly();total = (from a in allActions where a.ActionName.ToLower().Contains(query.ToLower()) select a).Count();
var result = (from a in allActions where a.ActionName.ToLower().Contains(query.ToLower()) select a).Skip(start).Take(limit);
return new List<ActionPermission>(result); }
?
把權限判斷相關的數據都緩存起來提高效率
我們把當前登錄用戶的:用戶信息,擁有菜單權限,擁有功能權限 放在Session里
我們把需要托管的所有Action功能權限放在 Appliction全局應用程序變量里
這樣我們所有的權限相關判斷都是從緩存中取數據,不需要頻繁訪問數據了。
相關代碼懶得貼了,自己去下載的源碼里翻吧....注意一下緩存相關都是通過ICache這個接口出的,搜一下就能找到
?
如何對每個Action進行攔截,在它執行之前判斷權限
最土的辦法就是在每個Action加一段權限判斷的代碼,哈哈...如果我要這樣做的話,估計要被大家的磚頭拍死。
看過本系列Asp.Net大型項目實踐(7)-用Unity實現AOP之事務處理+為啥要用AOP(附源碼)的朋友應該就能想到,這是一個典型的AOP應用場景。
由于Asp.net MVC的Filter機制其實就是Aop,所以我們直接使用它。熟悉Asp.net MVC的朋友估計知道里面其實自帶的有一個AuthorizeAttribute的ActionFilter,但基本就是個玩具,本來我想繼承它重寫的,但無奈里面的filterContext沒有ActionDescriptor屬性,所以干脆不要它自己寫個ActionFilter,代碼如下:
代碼 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web.Mvc; using System.Web; using System.Security.Principal; using Demo.HIS.Infrastructure.Facade.Authority;namespace Demo.HIS.MVC.CommonSupport.Filter { /// <summary> /// 權限攔截 /// </summary> [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] public class AuthorizeFilterAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { if (filterContext == null) { throw new ArgumentNullException("filterContext"); } var path = filterContext.HttpContext.Request.Path.ToLower(); if (path == "/" || path == "/Main/Login".ToLower() || path == "/Main/UserLogin".ToLower()) return;//忽略對Login登錄頁的權限判定 object[] attrs = filterContext.ActionDescriptor.GetCustomAttributes(typeof(ViewPageAttribute), true); var isViewPage = attrs.Length == 1;//當前Action請求是否為具體的功能頁 if (this.AuthorizeCore(filterContext, isViewPage) == false)//根據驗證判斷進行處理 { //注:如果未登錄直接在URL輸入功能權限地址提示不是很友好;如果登錄后輸入未維護的功能權限地址,那么也可以訪問,這個可能會有安全問題 if (isViewPage == true) { filterContext.Result = new HttpUnauthorizedResult();//直接URL輸入的頁面地址跳轉到登陸頁 } else { filterContext.Result = new ContentResult { Content = @"JsHelper.ShowError('抱歉,你不具有當前操作的權限!')" };//功能權限彈出提示框 } } } //權限判斷業務邏輯 protected virtual bool AuthorizeCore(ActionExecutingContext filterContext, bool isViewPage) { if (filterContext.HttpContext == null) { throw new ArgumentNullException("httpContext"); }
if (!filterContext.HttpContext.User.Identity.IsAuthenticated) { return false;//判定用戶是否登錄 } var user = new CurrentUser();//獲取當前用戶信息 var controllerName = filterContext.RouteData.Values["controller"].ToString(); var actionName = filterContext.RouteData.Values["action"].ToString(); if (isViewPage && (controllerName.ToLower() != "main" && actionName.ToLower() != "masterpage"))//如果當前Action請求為具體的功能頁并且不是MasterPage頁 { if (user.MenuPermission.Count(m => m.ControllerName == controllerName && m.ActionName ==
轉載于:https://www.cnblogs.com/freeliver54/p/6385001.html
總結
以上是生活随笔為你收集整理的[转]Asp.Net大型项目实践(11)-基于MVC Action粒度的权限管理【续】【源码在这里】(在线demo,全部源码)...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信小程序自定义变量使用,静态变量
- 下一篇: Centos7安装MySQL(多图)