[转]Asp.Net大型项目实践(11)-基于MVC Action粒度的权限管理【续】【源码在这里】(在线demo,全部源码)...
本文轉(zhuǎn)自:http://www.cnblogs.com/legendxian/archive/2010/01/25/1655551.html
接上篇Asp.Net大型項(xiàng)目實(shí)踐(10)-基于MVC Action粒度的權(quán)限管理(在線demo,全部源碼)
在線Demo:
地址:http://218.60.8.35:1234/
服務(wù)器:網(wǎng)通
端口:不要禁用1234端口應(yīng)該就可以訪問(wèn)
注意:連了數(shù)據(jù)庫(kù)的,時(shí)間倉(cāng)促肯定有漏洞,不要搗亂哈:)
登錄用戶: 1.用戶名:牛頭人戰(zhàn)士 密碼:000000 權(quán)限:有全部菜單頁(yè)面,不能進(jìn)行數(shù)據(jù)庫(kù)的更改操作(不影響錄入體驗(yàn))
?2.用戶名:老虎MM? 密碼:000000? 權(quán)限:少兩個(gè)菜單頁(yè)面,不能進(jìn)行數(shù)據(jù)庫(kù)的更改操作(不影響錄入體驗(yàn))
3.用戶名:admin 密碼不公開(kāi) 權(quán)限:所有權(quán)限
注:以上的實(shí)現(xiàn)都是通過(guò)權(quán)限管理s配置出的哈,沒(méi)有任何硬編碼
權(quán)限判斷的邊界
由于項(xiàng)目是基于MVC的,除去數(shù)據(jù)權(quán)限不說(shuō),功能權(quán)限的判斷邊界做在MVC 的Action上無(wú)疑是最好的選擇,因?yàn)闊o(wú)論是一個(gè)頁(yè)面,還是一個(gè)按鈕,還是一次查詢,都是通過(guò)Action請(qǐng)求實(shí)現(xiàn)的。這樣我們只需要在每個(gè)Action請(qǐng)求執(zhí)行之前進(jìn)行權(quán)限判斷就可以了,也不用折騰RBAC里的資源+操作=權(quán)限 這么麻煩。
?
菜單權(quán)限和功能權(quán)限
其實(shí)在MIS項(xiàng)目中,大多數(shù)的權(quán)限判斷粒度還是頁(yè)面級(jí)的,再加上我們還需要根據(jù)權(quán)限動(dòng)態(tài)生成用戶的菜單,所以我們把權(quán)限分成“菜單權(quán)限”和“功能權(quán)限”
菜單權(quán)限:在用戶登錄驗(yàn)證后,每個(gè)頁(yè)面的請(qǐng)求都必須通過(guò)權(quán)限驗(yàn)證。
功能權(quán)限:默認(rèn)客戶進(jìn)入頁(yè)面后,頁(yè)面的相關(guān)操作默認(rèn)都不判斷,只對(duì)顯示維護(hù)出的功能權(quán)限進(jìn)行權(quán)限判斷。
這樣有幾個(gè)好處:一般情況下權(quán)限的配置簡(jiǎn)單了,因?yàn)橹恍枰渲么至6鹊捻?yè)面權(quán)限即可使用;增加了效率,不必每個(gè)Action執(zhí)行之前都判斷權(quán)限(雖然都做了緩存,但能少判斷一次還是好的);完全不影響細(xì)粒度的權(quán)限判斷,隨時(shí)都可以增加對(duì)任何一個(gè)Action的權(quán)限判定
?
如何取Action功能權(quán)限
我們通過(guò)反射把所有的Action權(quán)限全部取出來(lái),這樣在維護(hù)選取的時(shí)候就比較方便了,也不會(huì)產(chǎn)生錄入錯(cuò)誤,如下圖:
大家用Demo可以體驗(yàn)到我們模糊輸入Action名稱就可以找到我們想要的Action的,因?yàn)槭桥渲眠x取用也不用擔(dān)心什么反射的效率問(wèn)題,其實(shí)大家從demo可以看到速度還是挺快的,在我真實(shí)的項(xiàng)目中Action中有上萬(wàn)個(gè),拉出來(lái)一樣是瞬時(shí)的,所以我覺(jué)得有時(shí)候吧,也別過(guò)于“談反射色變”,呵呵
通過(guò)反射獲取所有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>就是系統(tǒng)中所有Action的集合,大家可看到我們通過(guò)BaseController找到了項(xiàng)目中所有的Controller,再通過(guò)ActionResult找到Controller中所有的Action。
不知道大家注意下拉出的Action有個(gè)描述屬性,這個(gè)屬性是通過(guò)在Action上定義DescriptionAttribute實(shí)現(xiàn)的,這樣通過(guò)反射就能取到中文描述了,例如:為了實(shí)現(xiàn)頁(yè)面的選取方便,我們還要實(shí)現(xiàn)對(duì)IList<ActionPermission>的分頁(yè)和模糊查詢,因?yàn)槭亲兞考?jí)集合,這里我們使用Linq查詢就可以了,代碼如下:
[Description("訪問(wèn)功能權(quán)限管理頁(yè)面")] [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); }
?
把權(quán)限判斷相關(guān)的數(shù)據(jù)都緩存起來(lái)提高效率
我們把當(dāng)前登錄用戶的:用戶信息,擁有菜單權(quán)限,擁有功能權(quán)限 放在Session里
我們把需要托管的所有Action功能權(quán)限放在 Appliction全局應(yīng)用程序變量里
這樣我們所有的權(quán)限相關(guān)判斷都是從緩存中取數(shù)據(jù),不需要頻繁訪問(wèn)數(shù)據(jù)了。
相關(guān)代碼懶得貼了,自己去下載的源碼里翻吧....注意一下緩存相關(guān)都是通過(guò)ICache這個(gè)接口出的,搜一下就能找到
?
如何對(duì)每個(gè)Action進(jìn)行攔截,在它執(zhí)行之前判斷權(quán)限
最土的辦法就是在每個(gè)Action加一段權(quán)限判斷的代碼,哈哈...如果我要這樣做的話,估計(jì)要被大家的磚頭拍死。
看過(guò)本系列Asp.Net大型項(xiàng)目實(shí)踐(7)-用Unity實(shí)現(xiàn)AOP之事務(wù)處理+為啥要用AOP(附源碼)的朋友應(yīng)該就能想到,這是一個(gè)典型的AOP應(yīng)用場(chǎng)景。
由于Asp.net MVC的Filter機(jī)制其實(shí)就是Aop,所以我們直接使用它。熟悉Asp.net MVC的朋友估計(jì)知道里面其實(shí)自帶的有一個(gè)AuthorizeAttribute的ActionFilter,但基本就是個(gè)玩具,本來(lái)我想繼承它重寫(xiě)的,但無(wú)奈里面的filterContext沒(méi)有ActionDescriptor屬性,所以干脆不要它自己寫(xiě)個(gè)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> /// 權(quán)限攔截 /// </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;//忽略對(duì)Login登錄頁(yè)的權(quán)限判定 object[] attrs = filterContext.ActionDescriptor.GetCustomAttributes(typeof(ViewPageAttribute), true); var isViewPage = attrs.Length == 1;//當(dāng)前Action請(qǐng)求是否為具體的功能頁(yè) if (this.AuthorizeCore(filterContext, isViewPage) == false)//根據(jù)驗(yàn)證判斷進(jìn)行處理 { //注:如果未登錄直接在URL輸入功能權(quán)限地址提示不是很友好;如果登錄后輸入未維護(hù)的功能權(quán)限地址,那么也可以訪問(wèn),這個(gè)可能會(huì)有安全問(wèn)題 if (isViewPage == true) { filterContext.Result = new HttpUnauthorizedResult();//直接URL輸入的頁(yè)面地址跳轉(zhuǎn)到登陸頁(yè) } else { filterContext.Result = new ContentResult { Content = @"JsHelper.ShowError('抱歉,你不具有當(dāng)前操作的權(quán)限!')" };//功能權(quán)限彈出提示框 } } } //權(quán)限判斷業(yè)務(wù)邏輯 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();//獲取當(dāng)前用戶信息 var controllerName = filterContext.RouteData.Values["controller"].ToString(); var actionName = filterContext.RouteData.Values["action"].ToString(); if (isViewPage && (controllerName.ToLower() != "main" && actionName.ToLower() != "masterpage"))//如果當(dāng)前Action請(qǐng)求為具體的功能頁(yè)并且不是MasterPage頁(yè) { if (user.MenuPermission.Count(m => m.ControllerName == controllerName && m.ActionName ==
轉(zhuǎn)載于:https://www.cnblogs.com/freeliver54/p/6385001.html
總結(jié)
以上是生活随笔為你收集整理的[转]Asp.Net大型项目实践(11)-基于MVC Action粒度的权限管理【续】【源码在这里】(在线demo,全部源码)...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 微信小程序自定义变量使用,静态变量
- 下一篇: Centos7安装MySQL(多图)