RBAC的资料
利用?AOP?實現?.NET?上完整的基于角色的訪問控制(RBAC)模型?
作者:admin 日期:2006-11-30
一. 背景
二、相關主題介紹
- User 是日常管理運行時建立
- Role 是部署/交付建立
- Task 是開發時確定
- User<->Role 是日常管理運行時建立
- Role<->Task 是部署/交付時建立
三、具體實現
注:本文中實現 AOP 的思路主要來自于如下文章:Aspect oriented Programming using .NET - AOP in C# (http://www.developerfusion.co.uk/show/5307/3/) ,這是我看到的、在.NET 上實現 AOP最簡捷/方便的方法,它不便提供了原理介紹,也提供了 Visual Studio 2005 的 Sample Project ,其中有 Security Check 和 Logging 的 AOP 功能。它的優點在于,在實現 AOP 的同時,不需要再去建立接口(這是很多人的做法),直接在原有類上進行少量改動,即可實現完整的 AOP 功能。
1. 定義描述“Task”(任務)的 Attribute
using System; namespace BusinessLogic.Security ...{ /**//// /// 用于定義系統中的操作 /// [AttributeUsage(AttributeTargets.All,AllowMultiple=false,Inherited=true)] public sealed class Task : Attribute ...{ private string _name,_description; public string Name ...{ get ...{ return _name; } set ...{ _name = value; }} public string Description ...{ get ...{ return _description; } set ...{ _description = value; }} public Task(string name,string description) ...{_name = name;_description = description;} public Task() ...{}} }2. 編寫權限檢查的 AOP 類 SecurityAspect,完成權限檢查的功能
using System; using System.Diagnostics; using System.Reflection; using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Contexts; using System.Runtime.Remoting.Activation; namespace BusinessLogic.Security ...{ //消息接收器 internal class SecurityAspect : IMessageSink ...{ //內部變量 private IMessageSink m_next; //構造方法 internal SecurityAspect(IMessageSink next) ...{ m_next = next; } IMessageSink 實現#region IMessageSink 實現 public IMessageSink NextSink ...{ get ...{ return m_next; } } //同步處理消息 public IMessage SyncProcessMessage(IMessage msg) ...{ Preprocess(msg); IMessage returnMethod = m_next.SyncProcessMessage(msg); return returnMethod; } //異步處理消息(不實現) public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink) ...{ throw new InvalidOperationException(); } #endregion 自定義的 AOP 方法#region 自定義的 AOP 方法 private void Preprocess(IMessage msg) ...{ //只處理方法調用 if (!(msg is IMethodMessage)) return; //獲取方法中定義的 Task 屬性,交給權限檢查類去檢查 IMethodMessage call = msg as IMethodMessage; MethodBase mb = call.MethodBase; object[] attrObj = mb.GetCustomAttributes(typeof(Task), false); if (attrObj != null) ...{ Task attr = (Task)attrObj[0]; if(!string.IsNullOrEmpty(attr.Name)) AzHelper.PermissionCheck(attr.Name); } // Type type = Type.GetType(call.TypeName); } #endregion } public class PermissionCheckProperty : IContextProperty, IContributeObjectSink ...{ IContributeObjectSink 實現,將 AOP 類加入消息處理鏈#region IContributeObjectSink 實現,將 AOP 類加入消息處理鏈 public IMessageSink GetObjectSink(MarshalByRefObject o, IMessageSink next) ...{ return new SecurityAspect(next); } #endregion IContextProperty 實現#region IContextProperty 實現 public string Name ...{ get ...{ return "PermissionCheckProperty"; } } public void Freeze(Context newContext) ...{ } public bool IsNewContextOK(Context newCtx) ...{ return true; } #endregion } //特性定義,用于 Consumer [AttributeUsage(AttributeTargets.Class)] public class PermissionCheckAttribute : ContextAttribute ...{ public PermissionCheckAttribute() : base("PermissionCheck") ...{ } public override void GetPropertiesForNewContext(IConstructionCallMessage ccm) ...{ ccm.ContextProperties.Add(new PermissionCheckProperty()); } } }?
3. 定義用于權限檢查的兩個類:AzMan、AzHelper
這兩個類的功能是從 XML 配置文件中讀入 Role 和 Task 的映射關系,以確定 Role 中是否包含 Task 的引用,從而確定當前 Role 是否具有對此 Task 的權限。
注:這里可根據項目的實際情況,如果你的 Role 和 Task 的映射關系是存放在 Windows 的授權管理器(Authorizatiom Manager)或數據庫中,你可以使用自已
的方法來替換下列類。
在本例中,我的 Role 和 Task 的關系是存放在 XML 文件中,XML文件的格式如下所示:
<?xml version="1.0" encoding="utf-8"?> <ACL> <Tasks> <Task Name="AddItem" Description="增加" /> <Task Name="ModifyItem" Description="修改" /> <Task Name="RemoveItem" Description="刪除" /> <Task Name="ListItem" Description="獲取列表" /> </Tasks> <Roles> <Role Name="Manager"> <Task Name="AddItem" /> <Task Name="ModifyItem" /> <Task Name="RemoveItem" /> <Task Name="ListItem" /> </Role> </Roles> </ACL>AzMan.cs 完成角色/任務映射關系的檢查
AzHelper.cs 助手類,協助其他類,更好地調用 AzMan 類的方法,以及基于性能考慮,對Role<-->Task的XML配置文件進行緩存:
using System; using System.Collections.Generic; using System.Text; using System.Xml; using System.Web; using System.Web.Security; using System.Diagnostics; using System.Reflection; using System.Web.Caching; namespace BusinessLogic.Security ...{ public class AzHelper ...{ /**//// /// 檢查當前用戶是否具有執行當前任務的權限,如果有權限,則不做任何處理 /// 如果不具有權限,則引發異常 /// public static void PermissionCheck(string taskName) ...{ if (HttpContext.Current != null) ...{ XmlDocument aclDoc = (XmlDocument)HttpContext.Current.Cache["ACLDoc"]; if (aclDoc == null) ...{ CacheXml(); aclDoc = (XmlDocument)HttpContext.Current.Cache["ACLDoc"]; } string[] roles = Roles.GetRolesForUser(); if (!AzMan.AccessCheck(taskName, roles, aclDoc)) throw new UnauthorizedAccessException("訪問被拒絕,當前用戶不具有操作此功能的權限!"); } } /**//// /// 檢查當前用戶是否具有執行指定任務的權限 /// /// 任務名稱 /// True/False 是否允許執行 public static bool IsPermissible(string taskName) ...{ if (HttpContext.Current != null) ...{ XmlDocument aclDoc = (XmlDocument)HttpContext.Current.Cache["ACLDoc"]; if (aclDoc == null) ...{ CacheXml(); aclDoc = (XmlDocument)HttpContext.Current.Cache["ACLDoc"]; } string[] roles = Roles.GetRolesForUser(); aclDoc.Load(HttpContext.Current.Server.MapPath("~/App_Data/ACL.xml")); return AzMan.AccessCheck(taskName, roles, aclDoc); } else return true; } /**//// /// 緩存 XML 文件 /// private static void CacheXml() ...{ string fileName = HttpContext.Current.Server.MapPath("~/App_Data/ACL.xml"); XmlDocument aclDoc = new XmlDocument(); aclDoc.Load(fileName); HttpContext.Current.Cache.Insert("ACLDoc", aclDoc, new CacheDependency(fileName)); } } }
4. 業務邏輯類的實現
由于大多數工作都在 AOP 中實現了,所以業務邏輯類的實現較為簡單,主要分為以下幾個步驟:
- 在類的層次定義要求 AOP 方式權限檢查的 Attribute: [PermissionCheck()]
- 使類繼承自 ContextBoundObject 對象
- 在方法層次上利用 Task Attribute 來定義其對應的操作(注:多個方法可以定義為同一個 Task)
例如:ItemManager.cs
這樣就可以了,CLR 會在運行時檢查類的 PermissionCheck?Attribute,然后尋找方法上的 Task ,取出當前用戶對應的 Role ,再去進行匹配檢查,如果不能執行此操作,會拋出 UnauthorizedAccessException 的異常,在外部進行處理即可(如在 ASP.NET 中增加 ErrorPage 等)
5. 其他相關功能的實現
Q:如果我寫程序時,在各個業務邏輯類定義了大量的 Task ,如果統一提取出來?
A:利用反射可取出程序集中定義的所有 Task ,代碼如下:
List<string> dic = new List<string>(); StringBuilder sXml = new StringBuilder(""); string curDir = this.GetCurrentPath(); Assembly ass = Assembly.LoadFile(curDir + "\\AppFramework.BusinessLogic.dll"); foreach (Type t in ass.GetTypes()) ...{ MethodInfo[] mis = t.GetMethods(); foreach (MethodInfo mi in mis) ...{ object[] attrs = mi.GetCustomAttributes(false); if (attrs.Length > 0) ...{ foreach (object attr in attrs) ...{ if (attr.GetType().ToString().IndexOf("Task") >= 0) ...{ Task ta = (Task)attr; //檢查重復的 Task if (dic.IndexOf(ta.Name) < 0) ...{ dic.Add(ta.Name); sXml.Append(string.Format("\r\n ", ta.Name, ta.Description)); } } } } } //這就是所有的 Task 定義 sXml.Append("\r\n"); }此段代碼是將 Task 定義保存到 XML 文件中,如果你想保存到 SQL Server/Authorzatiom Manager 中,對代碼稍加修改即可。
Q:程序中的 Role 如何實現?
A:如果是 ASP.NET 應用程序,可以直接利用其中的 MemberShip Role 機制,還是比較簡單的
Q:如果我想在界面上預先實現一些控制,如某用戶不能進行某項操作,則直接將其對應的 Button 禁止或隱藏(Disable/Invisible)掉,如何做?
A:這可以利用 ASP.NET 2.0 中的表達式功能,直接檢查當前用戶的角色是否可以執行 Task ,如果不行,則利用返回的 Bool 值直接設置 Button 等控件的屬性,做法如下:
1)在 App_Code 下定義表達式類 PermissionCheckExpressionBuilder.cs
2)在 Web.Config 中加入上述表達式定義,以便可以直接在頁面上引用
3)直接在頁面控件的相應屬性上綁定表達式,如:
- 如果能執行此操作則顯示,否則則隱藏
<asp:Button ID="Button1" runat="server" Text="AddItem" Visible="" /> - 如果能執行此操作則啟用,否則則禁止
<asp:Button ID="Button2" runat="server" Text="AddItem" Enabled="" />
4)如果想在代碼中自行檢查權限,可以直接調用相應方法,如:
protected void Button1_Click(object sender, EventArgs e) ...{ AzHelper.PermissionCheck("AddItem"); //..其他操作 }5)如何建立 User<-->Role 的映射,Role<-->Task的映射
前者較為簡單,ASP.NET 2.0 中就已經具有此功能,當然你也可以利用其 API 來實現自己的定義界面。
對于 Role-Task 的映射來說,首先利用上面的代碼從程序集中取出所有 Task ,保存在 XML 文件中,然后在進行配置時,可以顯示 Role 和 Task ,來進行映射。
如下圖所示:
角色與任務的映射
用戶與角色的映射
總結
- 上一篇: 网络广告策划过程
- 下一篇: 8成收入来自卖车!为什么特斯拉能狂赚12