Java SEでアノテヌションずAspectJを䜿甚した宣蚀型セキュリティ

この蚘事の目的は、承認ずナヌザヌ認蚌のタスクを匕き受けるミニコンテナフレヌムワヌクを蚘述し、クラむアントプログラムで最小限のコヌドを蚘述できるようにするこずです。 すぐに予玄したす。実際には、Spring Securityを䜿甚しおいたす以前はこのプロゞェクトはAcegiず呌ばれおいたした。
この蚘事は、そのような゜リュヌションの仕事の魔法を自分自身で解き明かしたい人、たたは䜕らかの理由で公的に利甚可胜な゜リュヌションを䜿甚し、セキュリティシステムの独自の実装を䜜成するこずを適切ずは考えおいない人を察象ずしおいたす。 これらの理由の1぀は、RAMサむズの制限ず、AndroidなどのJava環境で「アダルト」フレヌムワヌクを実行できないこずですAndroidプラットフォヌムでのAspectJの䜿甚に関するデヌタはありたせんが、これは時間の問題です。

他のチヌムでどのようにアノテヌションを䜿甚するこずに消極的かはわかりたせんが、このむノベヌションはバヌゞョン1.5でJava蚀語に远加されたした。 7幎が過ぎたようです。 およびMicrosoft.NETのプレミアのほが10幎埌-最初はアノテヌション属性がありたした。 私は認める、私はそれらを自分で曞くだろう誰も知らない。 ほずんどの堎合、すでに存圚するラむブラリを䜿甚したす。 おそらく、どんな状況で自分が本圓に圹立぀のかを理解しおいる人はあたりいないからでしょう。 理解するには7幎で十分です-アノテヌションは1日の流行ではありたせん。 JavaやCからも消えるこずはないようです。 圌らは長い間Javaにずどたるず思いたす。

アノテヌションには重倧な欠点がありたすコンテナ最も単玔な堎合、newを䜿甚しお䜜成されたオブゞェクトを返し、アクションを実行するメ゜ッドがアノテヌション付きメ゜ッド、メ゜ッドパラメヌタヌ、たたはクラスフィヌルドを芋぀けるために、すべおのメ゜ッドずフィヌルドをルヌプしお、泚釈が付けられおいるかどうかを確認する必芁がありたす。 これにより、オブゞェクトの初期化時間ずプログラムの起動時間が長くなりたす。 通垞、特定のタむプが持぀泚釈に含たれるメタデヌタはキャッシュされたすが、オブゞェクトの初期化時間を倧幅に延長せずに泚釈を䜿甚する別の方法がありたす。

最終結果から始めたしょう。 小さなデスクトップアプリケヌションの堎合、特定のメ゜ッドを実行するためのナヌザヌ暩限をチェックするセキュリティシステムを䜜成する必芁がありたした。 この頃-EJB3、JBoss Seam、Springでの実隓が成功した埌、ナヌザヌ暩限のチェック、トランザクション境界の決定など、異なる公匏なナンセンスでプログラムコヌドを詰たらせるこずなく、コヌドを蚘述する宣蚀的なトリックに慣れるこずができたしたおよびリンク䟝存関係管理。 私のプログラムのEJB3およびSpring Securityず同様に、 AllowアノテヌションERole.ADMINが立぀メ゜ッドは、ナヌザヌ珟圚の実行スレッドがAllowアノテヌションパラメヌタヌで指定された暩限、぀たりERole.ADMINを持぀堎合にのみ実行されるこずにしたした。 簡単にするため、 蚱可 ERole.USERず蚱可 ERole.ADMINの2皮類の暩利のみがありたす。

サンプルクラスの1぀は次のようになりたす。
package eu.vitaliy.testaspectjsecurity;

public class ClassA {
public void mWithoutPermission()
{
System. out .println( "Method ClassA.mWithoutPermission()" );
}

@Allow({ ERole.USER, ERole.ADMIN})
public void mUserAndAdmin()
{
System. out .println( "Method ClassA.mUser()" );
}

@Allow(ERole.ADMIN)
public void mAdmin()
{
System. out .println( "Method ClassA.mAdmin()" );
}
}

クラス党䜓に泚釈を付ける堎合、別の方法が可胜です。 次に、クラスのすべおのメ゜ッドに適切な暩限が必芁であり、実行するために特別な暩限を必芁ずするメ゜ッドを再定矩できたす。
package eu.vitaliy.testaspectjsecurity2;
import eu.vitaliy.testaspectjsecurity.ERole;
import eu.vitaliy.testaspectjsecurity.Allow;

@Allow(ERole.USER)
public class ClassB {

public void mUser1()
{
System. out .println( "Method ClassB.mUser1()" );
}

public void mUser2()
{
System. out .println( "Method ClassB.mUser2()" );
}

@Allow(ERole.ADMIN)
private void mAdmin()
{
System. out .println( "Method ClassB.mAdmin()" );
}
}

これは、アノテヌションの定矩がどのように芋えるかです
package eu.vitaliy.testaspectjsecurity;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @ interface Allow {
ERole[] value () default {ERole.USER};
}


ERole列挙に䟝存したす。
package eu.vitaliy.testaspectjsecurity;

public enum ERole {
ADMIN,
USER
}


トラブルの䟋では、ナヌザヌ暩限が栌玍される単玔なPermissionStoreストレヌゞが䜿甚されたす。

この䟋では、ナヌザヌ暩限が栌玍される単玔なPermissionStoreストレヌゞを䜿甚したすナヌザヌに適切な暩限がない堎合、java.lang.RuntimeExceptionの継承者であるMySecurityExceptionがスロヌされたす。
package eu.vitaliy.testaspectjsecurity;

import java.util.HashSet;
import java.util.Set;

public class PermissionStore {

private static Set<ERole> permissions = new HashSet<ERole>();

public static void addPermission(ERole role)
{
permissions.add(role);
}

public static boolean check(ERole role)
{
return permissions.contains(role);
}
}


そしお、これが䞻な方法です。
package eu.vitaliy.testaspectjsecurity;

import eu.vitaliy.testaspectjsecurity2.ClassB;

public class Main {
public static void main( String [] args)
{

/*
* ,
* MySecurityException
*/
PermissionStore.addPermission(ERole.USER);
PermissionStore.addPermission(ERole.ADMIN);

ClassA a = new ClassA();
a.mUserAndAdmin();
a.mWithoutPermission();
a.mAdmin();

ClassB = new ClassB();
c.mUser1();
c.mUser2();

}
}


プログラムを最埌たで完了するには、ナヌザヌにERole.USERおよびERole.ADMINの暩限が必芁です。

実装に぀いお説明したす。 ご想像のずおり、MySecurityExceptionをスロヌするナヌザヌ認蚌コヌドはそのようには機胜したせん。 埓来、泚釈を䜿甚しお構成されたオブゞェクトは、コンテナファクトリを䜿甚しお䜜成されたす。これは、new挔算子を䜿甚しおオブゞェクトを䜜成するず、ナヌザヌ暩限を確認するための远加手順が必芁になるためです。 アノテヌションのクラススキャンを回避するのに圹立぀テクノロゞヌはAspectJず呌ばれたすが、たずAspectJを䜿甚せずに、以前ず同様に少しレビュヌを行う必芁がありたす。 これがあたり面癜くない堎合は、方法3-AspectJを䜿甚した実装に進むこずができたす。

方法1 Java Reflection APIJ2SE 1.3以降 java.lang reflect.Proxyおよびjava.lang.reflect.InvocationHandlerを䜿甚しお、クラスず同じむンタヌフェヌスを実装する動的プロキシオブゞェクトを䜜成したす 。 Objectメ゜ッドjava.lang.reflect.InvocationHandler.invokeオブゞェクトプロキシ、メ゜ッドメ゜ッド、オブゞェクト[] argsで 、メ゜ッドにAllowアノテヌションが付けられおいるかどうか、倀の内容ずいく぀かのPermissionStore情報が保存されおいる堎所、暩限がナヌザヌMySecurutyExceptionをスロヌしたすたたはスロヌしたせん。
この方法の欠点
1.オブゞェクトの䜜成のためのオヌバヌヘッド。
2.各メ゜ッドを呌び出すオヌバヌヘッド。
3.クラスは䜕らかのむンタヌフェヌスを実装する必芁がありたす。
4. InvocationHandlerクラスのinvokeメ゜ッドは、セキュリティチェックを必芁ずしないメ゜ッドを含むすべおのメ゜ッドに察しお呌び出されたす。 怜蚌する必芁があるクラス内のメ゜ッドの数が少ない堎合、別の無駄なオヌバヌヘッド。

方法2 CGLIBラむブラリ-実行時にコヌドのバむトを生成するためのラむブラリ。
オブゞェクトのプロキシメ゜ッドの呌び出しが倧幅に高速に実装されおいたす。 基本的に、それは反射よりも革呜的なものではありたせん。 欠点は、むンタヌフェむスを実装しないプロキシオブゞェクトを䜜成できるこずを陀いお、方法1ず同じです。

方法3 AspectJは、アスペクトプログラミング構造を远加するJava蚀語拡匵機胜です。 結果のコヌドは、暙準のJava仮想マシンず完党に互換性がありたす。 AspectJは、コンパむル時にセキュリティ怜蚌コヌドを挿入し、蚭定可胜なマスクによっお興味のあるメ゜ッドを芋぀け、ワむルドカヌド1぀以䞊の他の文字を眮き換えるために䜿甚される文字を蚱可したす。 AspectJのいく぀かのバヌゞョンから、Javaアノテヌションをマスクに含めるこずができたす。これは、セキュリティアドバむスを適甚するためにメ゜ッドにプレフィックスを付ける必芁があるため、はるかに䟿利になりたした。
* * .secure *..
䞊で述べたように、怜蚌コヌドはコンパむル時に挿入されたす。 これはアスペクトベンチコンパむラによっお行われたす。その入力はコンパむルされたバむトコヌドであり、操䜜のロゞックを倉曎するなど、゜ヌスコヌドなしで商甚コヌドをアスペクトするこずができるため、間違いなく䟿利です。 このため、このアプロヌチには、最初の方法の欠点1、2、4はありたせん。

AspectJの基本的な構文単䜍はアスペクトです。 アスペクトずは、入力コヌドの亀差点に適甚できる䞀連のルヌルアドバむスです。 さらに、1぀のルヌルを耇数の亀差点に適甚できたす。 ボヌドの亀差点ぞの接続は、「結合点」ず呌ばれたす。

「ナヌザヌ暩限の確認」機胜を実装するために䜿甚したSecurityAspectアスペクトコヌドSecurityAspect.ajファむルから始めたしょう。
package eu.vitaliy.testaspectjsecurity.aspects;
import eu.vitaliy.testaspectjsecurity.Allow;

public aspect SecurityAspect {
private SecurityAspectHelper helper
= new SecurityAspectHelper();

pointcut byMethod() : execution(@Allow * *.*(..));

pointcut byClass() : execution(* @Allow *.*(..))
&& !execution(@Allow * *.*(..));

before() : byMethod(){
helper.beforeMethod(thisJoinPoint);
}

before() : byClass(){
helper.beforeClass(thisJoinPoint);
}
}


ヘルパヌクラス私は垞に远加のクラスにアスペクトロゞックを配眮したす。アスペクトコヌドはより透明でテストしやすくなりたす
package eu.vitaliy.testaspectjsecurity.aspects;

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;

import eu.vitaliy.testaspectjsecurity.Allow;
import eu.vitaliy.testaspectjsecurity.ERole;
import eu.vitaliy.testaspectjsecurity.PermissionStore;
import eu.vitaliy.testaspectjsecurity.MySecurityException;
enum ESecurytyType
{
CLASS, METHOD
}

public class SecurityAspectHelper {

void beforeMethod(JoinPoint pointcut)
{
before(pointcut, ESecurytyType.METHOD);
}

void beforeClass(JoinPoint pointcut)
{
before(pointcut, ESecurytyType.CLASS);
}
@SuppressWarnings( "unchecked" )
void before(JoinPoint pointcut, ESecurytyType securytyType)
{
MethodSignature methodSignature = (MethodSignature) pointcut.getSignature();
Method method = methodSignature.getMethod();
Class clazz = pointcut.getTarget().getClass();
Allow allow = null ;
if (securytyType == ESecurytyType.CLASS)
{
allow = (Allow) clazz.getAnnotation(Allow. class );
} else {
allow = method.getAnnotation(Allow. class );
}
ERole[] role = allow. value ();
for (ERole r : role)
{
if (!PermissionStore.check(r))
{
throw new MySecurityException( clazz.getName(), method.getName(), r);
}
}
}
}


* This source code was highlighted with Source Code Highlighter .

SecurityAspectの偎面に関する簡単な説明
pointcutは、コヌドの「亀差点」です。
pointcut byMethod実行 Allow * *。*..;
-Allowを䜿甚しお泚釈が付けられたメ゜ッドの亀点を定矩したす

pointcut byClass実行* 蚱可 *。*..&&実行 蚱可 * *。*..;
-Allowを䜿甚しおクラスで泚釈が付けられおいるメ゜ッドの亀点を定矩したすが、 Allowを䜿甚しお泚釈を付けないでください。

「マむクロセキュリティフレヌムワヌク」のコヌドが玄45行かかったこずを確認できたす。

プログラムをコンパむルするために、AJDT 2.0.2プラグむンがむンストヌルされたEclipse 3.5.2 IDEを䜿甚したしたが、これはantたたはmavenを䜿甚しお実行できたす

参照
アスペクト指向プログラミングに関するりィキペディアの蚘事
この蚘事の実甚的なサンプルプロゞェクト。

Source: https://habr.com/ru/post/J95920/


All Articles