AOPにより実現する.NET上の完全なロールベースアクセス制御(RBAC)モデル
52541 ワード
一.背景 .NETプラットフォームには完全なRBACメカニズムがない.NETのセキュリティモデル(コードアクセスセキュリティ:CAS)はRole階層に実装されているだけで、Task階層に細分化されていません.ASP.NET 2.0の多くのセキュリティメカニズム、例えばMembership、Web.Configのセキュリティ構成は、すべてRoleに対してしか設定できません.これらのセキュリティメカニズムを利用して、プログラム/コードハードコーディング(HardCode)ロールを必要とすることが多く、実行中にロールをカスタマイズする機能 を実現できません. Windows 2000/2003に付属のAuthorization Managerは比較的完全なRBACモデルを実現しているが、一般的にはWindowsユーザーのみに適用され、手動でアクセスチェック(AccessCheckメソッド呼び出し) を行う必要がある.権限チェックは汎用的な操作であり、最良の実現方法は側面向けのプログラミング(AOP) である.
二、関連テーマの紹介 RBACモデルの要素:ユーザー、ロール、タスク(またはアクション)(User、Role、Task)の3つのエンティティで、安定性が徐々に向上し、2つの関係があります. Userは、日常管理運転時に確立する である. Roleは、導入/配信確立 です. Taskは開発時確定 である. User<->Roleは、日常的な管理実行時に確立された です. Role<->Taskは、導入/提供時に を確立します.
一般的に、Taskは固定的であり、アプリケーションと密接にバインドされており、ハードコーディングを行っても には関係ない. User/Role部分は比較的容易に実現できる、例えばASP.NET 2.0におけるMembershipの実現 三、具体的な実現
注:本文の中で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を定義する
?
3.権限チェックに使用する2つのクラスを定義します:AzMan、AzHelper
この2つのクラスの機能は、XMLプロファイルからRoleとTaskのマッピング関係を読み込み、RoleにTaskの参照が含まれているかどうかを決定し、現在のRoleがTaskに対する権限を持っているかどうかを決定することです.
注意:ここでは、プロジェクトの実際の状況に応じて、RoleとTaskのマッピング関係がWindowsのAuthorizatiom Managerまたはデータベースに格納されている場合は、以下のクラスを独自の方法で置き換えることができます.
この例では、私のロールとTaskの関係はXMLファイルに格納され、XMLファイルのフォーマットは次のようになります.
AzMan.csロール/タスクマッピング関係のチェックを完了
AzHelper.csアシスタントクラス、他のクラスを支援し、AzManクラスをよりよく呼び出す方法、およびパフォーマンスの考慮に基づいて、Role<-->TaskのXMLプロファイルをキャッシュします.
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を取り出すことができる.コードは以下の通りである.
このコードは、Task定義をXMLファイルに保存します.SQL Server/Authorzatiom Managerに保存する場合は、コードを少し変更すればいいです.
Q:プログラムのロールはどのように実現しますか?
A:ASPならNETアプリは、その中のMemberShip Roleメカニズムを直接利用できるのか、それとも簡単なのか
Q.あるユーザーが操作できない場合、対応するButtonを直接禁止または非表示にしたい場合は、どのようにしますか?
A:これはASPを利用できます.NET 2.0の式機能は、現在のユーザーの役割がTaskを実行できるかどうかを直接チェックし、できない場合は、返されたBool値を利用してButtonなどのコントロールの属性を直接設定します.
1)App_Codeの下で式クラスを定義します.cs
2)Web.Configに上記の式定義を追加し、ページで直接参照できるようにします.
3)次のように、ページコントロールの対応する属性に直接式をバインドします.この操作が実行可能であれば表示する、そうでなければ を隠す.この操作が実行可能であれば有効である、そうでなければ を禁止する.
4)コード内で権限をチェックする場合は、次のような方法を直接呼び出すことができます.
5)どのようにUser<-->Roleのマッピングを創立して、Role<-->Taskのマッピング
前者は比較的簡単で、ASP.NET 2.0にはすでにこの機能があり、もちろんAPIを利用して自分の定義インタフェースを実現することもできます.
Role-Taskのマッピングでは,まず上のコードを用いてプログラムセットからすべてのTaskを取り出し,XMLファイルに保存し,次に構成時に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.タスクを記述する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 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 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 IContributeObjectSink , AOP public IMessageSink GetObjectSink(MarshalByRefObject o, IMessageSink next) { return new SecurityAspect(next); } #endregion
IContextProperty
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));
}
}
}
4.ビジネスロジッククラスの実現
ほとんどの仕事はAOPで実現されているので、ビジネスロジッククラスの実現は簡単で、主に以下のいくつかのステップに分けられます.
例: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を追加するなど)
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
", ta.Name, ta.Description));
}
}
}
}
}
// Task
sXml.Append("\r
");
}
このコードは、Task定義をXMLファイルに保存します.SQL Server/Authorzatiom Managerに保存する場合は、コードを少し変更すればいいです.
Q:プログラムのロールはどのように実現しますか?
A:ASPならNETアプリは、その中のMemberShip Roleメカニズムを直接利用できるのか、それとも簡単なのか
Q.あるユーザーが操作できない場合、対応するButtonを直接禁止または非表示にしたい場合は、どのようにしますか?
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);
}
}
2)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
="<%$ PermissionCheck:AddItem %>"
/>
<
asp:Button
ID
="Button2"
runat
="server"
Text
="AddItem"
Enabled
="<%$ PermissionCheck:AddItem %>"
/>
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を表示してマッピングを行う.
次の図に示します.
ロールとタスクのマッピングユーザーとロールのマッピング