最新の安定バージョンについては、Spring Security 6.3.1 を使用してください!

セキュアなオブジェクトの実装

AOP Alliance(MethodInvocation)セキュリティインターセプター

Spring Security 2.0 の前は、MethodInvocation を固定するには、かなり多くのボイラープレート構成が必要でした。現在、メソッドのセキュリティに推奨されるアプローチは、名前空間の構成を使用することです。このようにして、メソッドセキュリティインフラストラクチャ Bean が自動的に構成されるため、実装クラスについて実際に知る必要はありません。ここでは、関連するクラスの概要を簡単に説明します。

メソッドのセキュリティは、MethodInvocation を保護する MethodSecurityInterceptor を使用して実施されます。構成アプローチによっては、インターセプターは単一の Bean に固有であるか、複数の Bean 間で共有される場合があります。インターセプターは、MethodSecurityMetadataSource インスタンスを使用して、特定のメソッド呼び出しに適用される構成属性を取得します。MapBasedMethodSecurityMetadataSource は、メソッド名(ワイルドカードを使用可能)をキーとする構成属性を格納するために使用され、<intercept-methods> または <protect-point> 要素を使用してアプリケーションコンテキストで属性が定義されるときに内部的に使用されます。他の実装を使用して、アノテーションベースの構成を処理します。

明示的な MethodSecurityInterceptor 設定

もちろん、Spring AOP のプロキシメカニズムの 1 つで使用するために、アプリケーションコンテキストで MethodSecurityInterceptor を直接構成できます。

<bean id="bankManagerSecurity" class=
	"org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="afterInvocationManager" ref="afterInvocationManager"/>
<property name="securityMetadataSource">
	<sec:method-security-metadata-source>
	<sec:protect method="com.mycompany.BankManager.delete*" access="ROLE_SUPERVISOR"/>
	<sec:protect method="com.mycompany.BankManager.getBalance" access="ROLE_TELLER,ROLE_SUPERVISOR"/>
	</sec:method-security-metadata-source>
</property>
</bean>

AspectJ(JoinPoint)セキュリティインターセプター

AspectJ セキュリティインターセプターは、前のセクションで説明した AOP Alliance セキュリティインターセプターと非常によく似ています。実際、このセクションの違いについてのみ説明します。

AspectJ インターセプターの名前は AspectJSecurityInterceptor です。Spring アプリケーションコンテキストに依存してプロキシ経由でセキュリティインターセプターを織り込む AOP Alliance セキュリティインターセプターとは異なり、AspectJSecurityInterceptor は AspectJ コンパイラー経由で織り込まれています。同じアプリケーションで両方の型のセキュリティインターセプターを使用することは珍しくありません。AspectJSecurityInterceptor はドメインオブジェクトインスタンスのセキュリティに使用され、AOP Alliance MethodSecurityInterceptor はサービスレイヤーのセキュリティに使用されます。

まず、AspectJSecurityInterceptor が Spring アプリケーションコンテキストでどのように構成されているかを考えてみましょう。

<bean id="bankManagerSecurity" class=
	"org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="afterInvocationManager" ref="afterInvocationManager"/>
<property name="securityMetadataSource">
	<sec:method-security-metadata-source>
	<sec:protect method="com.mycompany.BankManager.delete*" access="ROLE_SUPERVISOR"/>
	<sec:protect method="com.mycompany.BankManager.getBalance" access="ROLE_TELLER,ROLE_SUPERVISOR"/>
	</sec:method-security-metadata-source>
</property>
</bean>

ご覧のとおり、クラス名を除き、AspectJSecurityInterceptor は AOP Alliance のセキュリティインターセプターとまったく同じです。実際、SecurityMetadataSource は AOP ライブラリ固有のクラスではなく java.lang.reflect.Method で動作するため、2 つのインターセプターは同じ securityMetadataSource を共有できます。もちろん、アクセスの決定は、関連する AOP ライブラリ固有の呼び出し(つまり MethodInvocation または JoinPoint)にアクセスできるため、アクセスの決定(メソッドの引数など)を行う際に追加の条件の範囲を考慮することができます。

次に、AspectJ aspect を定義する必要があります。例:

package org.springframework.security.samples.aspectj;

import org.springframework.security.access.intercept.aspectj.AspectJSecurityInterceptor;
import org.springframework.security.access.intercept.aspectj.AspectJCallback;
import org.springframework.beans.factory.InitializingBean;

public aspect DomainObjectInstanceSecurityAspect implements InitializingBean {

	private AspectJSecurityInterceptor securityInterceptor;

	pointcut domainObjectInstanceExecution(): target(PersistableEntity)
		&& execution(public * *(..)) && !within(DomainObjectInstanceSecurityAspect);

	Object around(): domainObjectInstanceExecution() {
		if (this.securityInterceptor == null) {
			return proceed();
		}

		AspectJCallback callback = new AspectJCallback() {
			public Object proceedWithObject() {
				return proceed();
			}
		};

		return this.securityInterceptor.invoke(thisJoinPoint, callback);
	}

	public AspectJSecurityInterceptor getSecurityInterceptor() {
		return securityInterceptor;
	}

	public void setSecurityInterceptor(AspectJSecurityInterceptor securityInterceptor) {
		this.securityInterceptor = securityInterceptor;
	}

	public void afterPropertiesSet() throws Exception {
		if (this.securityInterceptor == null)
			throw new IllegalArgumentException("securityInterceptor required");
		}
	}
}

上記の例では、セキュリティインターセプターは PersistableEntity のすべてのインスタンスに適用されますが、これは示されていない抽象クラスです(他のクラスまたは pointcut 式を使用できます)。proceed(); ステートメントは around() 本体内でのみ特別な意味を持つため、好奇心の強い人には AspectJCallback が必要です。AspectJSecurityInterceptor は、ターゲットオブジェクトを継続させたいときに、この匿名 AspectJCallback クラスを呼び出します。

アスペクトをロードして AspectJSecurityInterceptor に接続するように Spring を構成する必要があります。これを実現する Bean 宣言を以下に示します。

<bean id="domainObjectInstanceSecurityAspect"
	class="security.samples.aspectj.DomainObjectInstanceSecurityAspect"
	factory-method="aspectOf">
<property name="securityInterceptor" ref="bankManagerSecurity"/>
</bean>

以上です! これで、アプリケーション内の任意の場所から Bean を作成できます。new Person(); などの適切な手段を使用すると、セキュリティインターセプターが適用されます。