RBACの資料
75767 ワード
AOPにより実現する.NET上の完全なロールベースアクセス制御(RBAC)モデル
作者:admin日付:2006-11-30
一.背景
二、関連テーマの紹介
注:本文の中で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.タスクを記述する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.権限チェックに使用する2つのクラスを定義します:AzMan、AzHelper
この2つのクラスの機能は、XMLプロファイルからRoleとTaskのマッピング関係を読み込み、RoleにTaskの参照が含まれているかどうかを決定し、現在のRoleがTaskに対する権限を持っているかどうかを決定することです.
注意:ここでは、プロジェクトの実際の状況に応じて、RoleとTaskのマッピング関係がWindowsのAuthorizatiom Managerまたはデータベースに格納されている場合は、以下のクラスを独自の方法で置き換えることができます.
この例では、私のロールと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 /
using
System;
using
System.Collections.Generic;
using
System.Text;
using
System.Xml;
namespace
BusinessLogic.Security
...
{ public class AzMan ...{ public static bool AccessCheck(string taskName, string[] roles, XmlDocument aclDoc) ...{ XmlNode rootNode = aclDoc.DocumentElement; XmlNodeList roleNodes,taskNodes; bool IsPermissiable = false; for (int i = 0; i < roles.Length; i++) ...{ roleNodes = rootNode.SelectNodes("Roles/Role[@Name='" + roles[i] + "']"); if (roleNodes != null) ...{ taskNodes = roleNodes.Item(0).SelectNodes("Task[@Name='" + taskName + "']"); if (taskNodes.Count != 0) ...{ IsPermissiable = true; break; } } } return IsPermissiable; } } }
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)); } } }
.ビジネスロジッククラスの は、 くの がAOPで されているため、ビジネスロジッククラスの は で、 に のステップに けられます.クラスの AOP の チェックを するAttribute:[permissionCheck()] クラスをContextBoundObjectオブジェクト から するメソッド でTask Attributeを して するアクションを する( : のメソッドは じTaskとして できる) .
:ItemManager.cs
namespace
BusinessLogic
...
{ [PermissionCheck()] public class ItemManager : ContextBoundObject ...{ [Task("AddItem"," ")] public void AddItem(Item item) ...{ //... } } }
これでいいです.CLRは にクラスのPermissionCheckをチェックしますか?Attributeは、 にメソッド のTaskを し、 のユーザに するRoleを り してマッチングチェックを い、それが できない はUnauthorizedAccessExceptionの を げ し、 で すればよい( えばASP.NETにErrorPageを するなど).その の の 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
", ta.Name, ta.Description)); } } } } } // Task sXml.Append("\r
"); }
このコードはTask をXMLファイルに します.SQL Server/Authorzatiom Managerに したい は、コードを し すればいいです.Q:プログラムのロールはどのように しますか?A:ASPならNETアプリは、その のMemberShip Roleメカニズムを することができますか、それとも なQ:もし がインタフェースで にいくつかの を したいならば、あるユーザーがある を うことができないならば、 それに するButtonを したり したり(Disable/Invisible)しますか?A:これはASPを できます.NET 2.0の では、 のユーザーの がTaskを できるかどうかを チェックし、できない は、 されたBool を してButtonなどのコントロールの を し、 のようにします:1)App_Codeの で クラスを します.cs
[ExpressionEditor(
typeof
(PermissionCheckExpressionBuilderEditor))] [ExpressionPrefix(
"
PermissionCheck
"
)]
public
class
PermissionCheckExpressionBuilder : ExpressionBuilder
...
{ public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context) ...{ string taskName = entry.Expression; return new CodePrimitiveExpression(AzHelper.IsPermissible(taskName)); } }
public
class
PermissionCheckExpressionBuilderEditor : System.Web.UI.Design.ExpressionEditor
...
{ public override object EvaluateExpression(string expression, object parseTimeData, Type propertyType, IServiceProvider serviceProvider) ...{ //return expression + ":" + parseTimeData + ":" + propertyType + ":" + serviceProvider; string taskName = expression; return AzHelper.IsPermissible(taskName); } }
)Web.Configに の を えることで、
<
configuration
xmlns
="http://schemas.microsoft.com/.NetConfiguration/v2.0"
>
<
expressionBuilders
>
<
add
expressionPrefix
="PermissionCheck"
type
="PermissionCheckExpressionBuilder"
/>
expressionBuilders> configuration> 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"); //.. }
)User<-->Roleのマッピングをどのように するか、Role<-->Taskのマッピングの は で、ASP.NET 2.0にはすでにこの があり、もちろんAPIを して の インタフェースを することもできます.Role-Taskのマッピングでは,まず のコードを いてプログラムセットからすべてのTaskを り し,XMLファイルに し, に にRoleとTaskを してマッピングを う. の に すように、ロールとタスクのマッピングユーザーとロールのマッピング
( http://www.850816.com/trackback.asp?tbID=44 )