ポストシャープ:承認と認証

アスペクト指向プログラミングの最も一般的なアプリケーションの1つは、サービスインフラストラクチャコードの削除です。これは、多くの場合、システム内で別のクラスで繰り返されます。 これは、単一責任の原則(SRP-単一責任原則)の現れです。 多くの場合、承認および認証タスクはコード全体に散らばり、ユーザーのアクセス権をチェックします。 これの結果は、ロジックのこの重要な部分の変更の多大な労力と、その一般的な検証です。 単一責任の原則は、クラスを変更する理由は1つだけである必要があることを示唆しているため、許可と認証に関連するすべてが単に別々のクラスで要求されます。



通常、基本認証方法は、アプリケーション全体で使用されるものではありません。 たとえば、Webアプリケーションでは、認証および認証自体のデータ入力は1ページで行われ、その後、ユーザー情報は有効期限付きのトークンに保存されます。 これにより、ユーザーはトークンの期間中にシステムに自動的にログインできます。 したがって、アプリケーションのすべてのページに浸透する唯一のコードは、ユーザーがまだシステムにいることを確認することです。 もちろん、このような検証にはPostSharpを使用できますが、私の意見では、これは最適な適用方法ではありません。







対照的に、承認はPostSharpを使用する良い例です。 多くの場合、ユーザーロールに基づいてアクションを解決するロジックがアプリケーション全体でひどくぼやけていることがわかります。 このような状況では、PostSharpを使用して「汚れ」を取り除き、意味のあるコードのみを残すことができます。 さらに、ロールベースのセキュリティが広すぎる場合があり、アクセスを制御するためのシンツールが必要になる場合があります。 たとえば、作成者以外の全員にデータ編集を制限します。 それでは、私がコンサルタントとして働いていたものと非常によく似た簡単なアプリケーションを見てみましょう。PostSharpがどのように役立つかを理解します。



テストアプリケーションを使用して、人々が公共サービスに対するいくつかの要求を満たせるようにします。 Webアプリケーションの場合もありますが、簡単にするためにWinFormsアプリケーションを使用します。 すべてのユーザーがリクエストフォームに入力できます。この場合は、それが唯一のテキストフィールドになります。 システム管理者はすべてのリクエストを削除でき、単純なユーザーはリクエストを作成して既存のリクエストのみを表示できます。



ここではプログラムの完全なコードを提供するのではなく、示されたアクションの基本的な機能を提供するサービスクラスに限定します。 彼はストレージとして静的コレクションを使用しますが、このアプリケーションでは、もちろんデータベース、サービスなどが使用されます。

public class GovtFormService : IGovtFormService{ private static readonly IList _govtFormsDatabase = new List(); public GovtFormService() { // build up some initial entries of the static list } public void SubmitForm(GovtForm form) { _govtFormsDatabase.Add(form); } public IEnumerable GetAllForms() { return _govtFormsDatabase; } public GovtForm GetFormById(Guid guid) { return _govtFormsDatabase.FirstOrDefault(form => form.FormId == guid); } }
      
      





ここでは、フォームをサービスに関連付けるだけで、最終的に基本的なアプリケーションを取得するために、サービスからボタンを押す操作にメソッドを割り当てます。 ただし、要件は、ユーザーが自分の要求のみを表示できる必要があることを明確に示しています。 GetFormByIdは現在、チェックを行いません。 複数の条件を追加できますが、どこでも使用できるアスペクトをすぐに作成することをお勧めします。

 [Serializable] public class AuthorizeReturnValueAttribute : OnMethodBoundaryAspect{    [NonSerialized] private IAuth Auth;    public override void RuntimeInitialize(System.Reflection.MethodBase method) {        Auth = new AuthService();    }    public override void OnSuccess(MethodExecutionArgs args) {        var singleForm = args.ReturnValue as GovtForm;        if (singleForm != null) {            if(Auth.CurrentUserHasPermission(singleForm, Permission.Read)) {                MessageBox.Show(                 "You are not authorized to view the details of that form",                 "Authorization Denied!");                args.ReturnValue = null;            }            return;        }    } }
      
      





これを実装する1つの方法は、インターセプトされたメソッドがGovtForm型を返すかどうかを確認することです。 はいの場合、このリクエストフォームがユーザーのものであるかどうかを確認します。 タイプI Authのフィールドは非シリアル化可能としてマークされ、オーバーライドされたRuntimeInitializeメソッドで初期化されることに注意してください。 この場合、ハードコードが使用されましたが、IoCコンテナーをサービスロケーターとして使用できます(依存性注入に関する翻訳を参照)。



アスペクトにさらにコードを追加して、GovtFormコレクションを返すメソッドを操作し、現在のユーザーが利用できるリクエストフォームをフィルタリングできるようにします。

 var formCollection = args.ReturnValue as IEnumerable; if (formCollection != null) {    args.ReturnValue = formCollection            .Where(f => Auth.CurrentUserHasPermission(f, Permission.Read));    return; }
      
      





GovtFormクラスは、UserNameプロパティを実装することを除いて目立たない。 提案されたアプローチに従って処理する各ビジネスオブジェクトにISeifiableインターフェイスを追加できます。 その後、CurrentUserHasPermissionメソッドを書き換えて、ISeifiable型の引数を受け入れることができます。 その後は、単一のビジネスオブジェクトまたはそのコレクションを返すすべてのサービスおよびリポジトリにAuthorizedRecordsOnly属性を追加するだけで、ユーザーにはリクエストフォームのみが表示されます。 このアプローチでは、UIまたはサービスのコードを大幅に変更する必要はありません。



すでにいくつかの有用な側面に精通しているので、今度はより包括的な例を検討します。 特定のメソッドについて、キャッシュの側面承認の側面の両方があると想像してください。 キャッシングは承認後に行われるべきであると想定するのは論理的です。そうでなければ、キャッシュされたデータへのアクセスはアクセス権なしで取得できます。 それでは、キャッシュする前にどのように認可の側面を機能させるのでしょうか?



方法は次のとおりです。

  1. ProvideAspectRoleAttribute属性を関心のある側面に適用します。 ロールは文字列にすることも、PostSharpスペースのStandardRoles列挙を使用することもできます。
  2. AspectRoleDependencyAttribute属性を対象のアスペクトに適用して、依存関係のタイプとそれらが依存するロールを示します。


上記の問題を解決するには、次のようなアスペクトに属性を適用する必要があります。

 [Serializable] [AspectRoleDependency(AspectDependencyAction.Order,    AspectDependencyPosition.Before, StandardRoles.Caching)] public class AuthorizeReturnValueAttribute : OnMethodBoundaryAspect{ } [Serializable] [ProvideAspectRole(StandardRoles.Caching)] public class CachingAttribute : OnMethodBoundaryAspect {    public override void OnEntry(MethodExecutionArgs args) {        // do caching stuff    }    public override void OnSuccess(MethodExecutionArgs args) {        // do caching stuff    } }
      
      





このコードを使用して、キャッシュアスペクトがキャッシングロールで作成されていることをPostSharpに伝え、キャッシングロールで他のアスペクトが有効になる前に承認アスペクトを適用する必要があることを明示的に述べました。



次に、両方の側面が適用された後のGetAllFormsメソッドのコードの様子を示します(Reflectorの精神でプログラムを使用して取得)。

 public IEnumerable GetAllForms(){    MethodExecutionArgs CS$0$2__aspectArgs = new MethodExecutionArgs(null, null);    <>z__Aspects.a1.OnEntry(CS$0$2__aspectArgs);    IEnumerable CS$1$1__returnValue = _govtFormsDatabase;    <>z__Aspects.a1.OnSuccess(CS$0$2__aspectArgs);    CS$0$2__aspectArgs.ReturnValue = CS$1$1__returnValue;    <>z__Aspects.a0.OnSuccess(CS$0$2__aspectArgs);    return (IEnumerable) CS$0$2__aspectArgs.ReturnValue; }
      
      





比較のために、コード、すべてが正しい場合、AspectDependencyPositionをBeforeに変更します。

 public IEnumerable GetAllForms(){    <>z__Aspects.a1.OnEntry(null);    MethodExecutionArgs CS$0$2__aspectArgs = new MethodExecutionArgs(null, null);    IEnumerable CS$1$1__returnValue = _govtFormsDatabase;    CS$0$2__aspectArgs.ReturnValue = CS$1$1__returnValue;    <>z__Aspects.a0.OnSuccess(CS$0$2__aspectArgs);    CS$1$1__returnValue = (IEnumerable) CS$0$2__aspectArgs.ReturnValue;    <>z__Aspects.a1.OnSuccess(null);    return CS$1$1__returnValue; }
      
      





a1(キャッシュ)とa0(許可)が入れ替わっていることに注意してください。



PostSharpは、アスペクトの依存関係をカスタマイズするための広範なオプションを提供します。 依存関係を解決するための5つのアクション(通勤、競合、順序、要求、なし)を自由に選択できます。 さらに、StandardRoles列挙で十分でない場合、無限の数のロールを作成できます。 幸いなことに、これらの機能が必要になることはあまりありませんが、時間が来たときに使用する機能について知っておくと便利です。 最良の部分はリラックスできることです。チームの最新メンバーであっても、承認が必要なUIのデータは、コードにアスペクトを適用する順序は関係ないので、データを提供しません。



マシュー ・D Grovesソフトウェアエンジニア   Telligent 彼のブログは mgroves です。 com



PostSharpロギング と監査キャッシュ遅延依存ロード



All Articles