Spring Security の高度な認証機能は、その人気の理由の 1 つです。Spring Security が提供するメカニズムやプロバイダーを使用するか、あるいはコンテナーや他の Spring 以外のセキュリティ認証機関と統合するかに関係なく、アプリケーション内で認可サービスを一貫した簡単な方法で使用することができます。
このパートでは、パート I で紹介されたさまざまな AbstractSecurityInterceptor
の実装について説明します。次に、ドメインアクセス制御リストを使用して認可を微調整する方法について説明します。
技術概要で見たように、すべての Authentication
実装は GrantedAuthority
オブジェクトのリストを保存します。これらは、プリンシパルに付与された権限を表します。 GrantedAuthority
オブジェクトは AuthenticationManager
によって Authentication
オブジェクトに挿入され、あとで AccessDecisionManager
によって認可の決定を行うときに読み取られます。
GrantedAuthority
は、メソッドが 1 つだけのインターフェースです。
String getAuthority();
この方法により、 AccessDecisionManager
は GrantedAuthority
の正確な String
表現を取得できます。表現を String
として返すことにより、ほとんどの AccessDecisionManager
が GrantedAuthority
を簡単に「読み取る」ことができます。 GrantedAuthority
を String
として正確に表すことができない場合、 GrantedAuthority
は「複雑」と見なされ、 getAuthority()
は null
を返す必要があります。
「複雑な」 GrantedAuthority
の例は、異なる顧客アカウント番号に適用される操作と権限のしきい値のリストを格納する実装です。この複雑な GrantedAuthority
を String
として表現することは非常に難しく、その結果、 getAuthority()
メソッドは null
を返すはずです。これは、 AccessDecisionManager
の内容を理解するために GrantedAuthority
実装を特にサポートする必要があることを AccessDecisionManager
に示します。
Spring Security には、 GrantedAuthority
の具体的な実装 SimpleGrantedAuthority
が含まれています。これにより、ユーザー指定の String
を GrantedAuthority
に変換できます。セキュリティアーキテクチャに含まれるすべての AuthenticationProvider
は、 SimpleGrantedAuthority
を使用して Authentication
オブジェクトに入力します。
技術概要の章でも見たように、Spring Security はメソッド呼び出しや Web リクエストなどの安全なオブジェクトへのアクセスを制御するインターセプターを提供します。呼び出しの続行を許可するかどうかの呼び出し前の決定は、 AccessDecisionManager
によって行われます。
AccessDecisionManager
は AbstractSecurityInterceptor
によって呼び出され、最終的なアクセス制御の決定を行います。 AccessDecisionManager
インターフェースには 3 つのメソッドが含まれています。
void decide(Authentication authentication, Object secureObject, Collection<ConfigAttribute> attrs) throws AccessDeniedException; boolean supports(ConfigAttribute attribute); boolean supports(Class clazz);
AccessDecisionManager
の decide
メソッドには、認可決定を行うために必要なすべての関連情報が渡されます。特に、セキュア Object
を渡すと、実際のセキュアオブジェクト呼び出しに含まれる引数をインスペクションできます。例: セキュアなオブジェクトが MethodInvocation
であると仮定しましょう。 Customer
引数について MethodInvocation
を照会し、 AccessDecisionManager
に何らかのセキュリティロジックを実装して、プリンシパルがその顧客での操作を認可されていることを確認するのは簡単です。アクセスが拒否された場合、実装は AccessDeniedException
をスローすることが期待されています。
supports(ConfigAttribute)
メソッドは、 AccessDecisionManager
が渡された ConfigAttribute
を処理できるかどうかを判別するために、起動時に AbstractSecurityInterceptor
によって呼び出されます。 supports(Class)
メソッドは、セキュリティインターセプターの実装によって呼び出され、構成された AccessDecisionManager
がセキュリティインターセプターが提示する型のセキュアオブジェクトをサポートするようにします。
ユーザーは独自の AccessDecisionManager
を実装して認証のすべての側面を制御できますが、Spring Security には投票に基づくいくつかの AccessDecisionManager
実装が含まれています。図 11.1: “ 投票決定マネージャー ” は、関連するクラスを示しています。
このアプローチを使用して、一連の AccessDecisionVoter
実装が認可決定でポーリングされます。 AccessDecisionManager
は、投票の評価に基づいて AccessDeniedException
をスローするかどうかを決定します。
AccessDecisionVoter
インターフェースには 3 つのメソッドがあります。
int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attrs); boolean supports(ConfigAttribute attribute); boolean supports(Class clazz);
具体的な実装は int
を返し、可能な値は AccessDecisionVoter
静的フィールド ACCESS_ABSTAIN
、 ACCESS_DENIED
、 ACCESS_GRANTED
に反映されます。認可の決定について意見がない場合、投票の実装は ACCESS_ABSTAIN
を返します。意見がある場合は、 ACCESS_DENIED
または ACCESS_GRANTED
のいずれかを返す必要があります。
Spring Security には投票を集計する 3 つの具体的な AccessDecisionManager
があります。 ConsensusBased
実装は、非棄権投票のコンセンサスに基づいてアクセスを許可または拒否します。投票が等しい場合、またはすべての投票が棄権された場合の動作を制御するプロパティが提供されます。 AffirmativeBased
実装は、1 つ以上の ACCESS_GRANTED
投票が受信された場合にアクセスを許可します(つまり、少なくとも 1 つの許可投票があった場合、拒否投票は無視されます)。 ConsensusBased
実装のように、すべての投票者が棄権した場合の動作を制御するパラメーターがあります。 UnanimousBased
プロバイダーは、棄権を無視して、アクセスを許可するために満場一致の ACCESS_GRANTED
投票を期待しています。 ACCESS_DENIED
投票がある場合、アクセスを拒否します。他の実装と同様に、すべての投票者が棄権した場合の動作を制御するパラメーターがあります。
投票を異なる方法で集計するカスタム AccessDecisionManager
を実装することができます。例: 特定の AccessDecisionVoter
からの投票には追加の重み付けが適用される場合がありますが、特定の投票者からの拒否投票には拒否権が付与される場合があります。
Spring Security で提供される最も一般的に使用される AccessDecisionVoter
は、単純な RoleVoter
です。これは、構成属性を単純なロール名として扱い、ユーザーにそのロールが割り当てられている場合にアクセス権を付与します。
ConfigAttribute
が接頭辞 ROLE_
で始まる場合に投票します。 ROLE_
で始まる 1 つ以上の ConfigAttributes
と正確に等しい( getAuthority()
メソッドを介して) String
表現を返す GrantedAuthority
がある場合、アクセスを許可するために投票します。 ROLE_
で始まる ConfigAttribute
の正確な一致がない場合、 RoleVoter
はアクセスを拒否するために投票します。 ConfigAttribute
が ROLE_
で始まっていない場合、投票者は棄権します。
暗黙的に確認したもう 1 つの投票者は AuthenticatedVoter
です。これは、匿名ユーザー、完全認証ユーザー、記憶ユーザー認証ユーザーを区別するために使用できます。多くのサイトでは、remember-me 認証で特定の制限付きアクセスが許可されていますが、完全なアクセスのためにログインすることでユーザーに ID の確認を要求します。
属性 IS_AUTHENTICATED_ANONYMOUSLY
を使用して匿名アクセスを許可したとき、この属性は AuthenticatedVoter
によって処理されていました。詳細については、このクラスの Javadoc を参照してください。
当然、カスタム AccessDecisionVoter
を実装することもでき、必要なほぼすべてのアクセス制御ロジックを配置できます。アプリケーション固有のもの(ビジネスロジック関連)か、セキュリティ管理ロジックを実装している場合があります。例: Spring の Web サイトには、投票者を使用して、アカウントが停止されたユーザーへのアクセスをリアルタイムで拒否する方法を説明したブログ記事 (英語) があります。
AccessDecisionManager
は、セキュアオブジェクトの呼び出しを続行する前に AbstractSecurityInterceptor
によって呼び出されますが、一部のアプリケーションでは、セキュアオブジェクトの呼び出しによって実際に返されるオブジェクトを変更する方法が必要です。これを実現するために独自の AOP 懸念事項を簡単に実装できますが、Spring Security は ACL 機能と統合するいくつかの具体的な実装を持つ便利なフックを提供します。
図 11.2: “ 呼び出し実装後 ” は、Spring Security の AfterInvocationManager
とその具体的な実装を示しています。
Spring Security の他の多くの部分と同様に、 AfterInvocationManager
には、 AfterInvocationProvider
のリストをポーリングする単一の具象実装 AfterInvocationProviderManager
があります。各 AfterInvocationProvider
は、戻りオブジェクトを変更するか、 AccessDeniedException
をスローできます。実際、前のプロバイダーの結果がリスト内の次のプロバイダーに渡されるため、複数のプロバイダーがオブジェクトを変更できます。
AfterInvocationManager
を使用している場合は、 MethodSecurityInterceptor
の AccessDecisionManager
が操作を許可できるようにする構成属性が引き続き必要です。一般的な Spring Security に含まれる AccessDecisionManager
実装を使用している場合、特定のセキュアなメソッド呼び出しに定義された構成属性がないと、各 AccessDecisionVoter
は投票を控えます。次に、 AccessDecisionManager
プロパティ「allowIfAllAbstainDecisions」が false
の場合、 AccessDeniedException
がスローされます。この潜在的な課題を回避するには、(i)「allowIfAllAbstainDecisions」を true
に設定するか(これは一般的に推奨されません)、または(ii) AccessDecisionVoter
がアクセスを許可するために投票する構成属性が少なくとも 1 つあることを確認します。この後者の(推奨)アプローチは、通常 ROLE_USER
または ROLE_AUTHENTICATED
構成属性によって実現されます。
アプリケーションの特定のロールが他のロールを自動的に「含める」ことが一般的な要件です。例: 「管理者」と「ユーザー」のロールの概念を持つアプリケーションでは、管理者が通常のユーザーができることをすべて行えるようにしたい場合があります。これを実現するには、すべての管理ユーザーにも「ユーザー」ロールが割り当てられていることを確認します。または、「user」ロールに「admin」ロールも含める必要があるすべてのアクセス制約を変更できます。アプリケーションにさまざまなロールがある場合、これは非常に複雑になる可能性があります。
ロール階層を使用すると、他のロール(または権限)を含めるロールを構成できます。Spring Security の RoleVoter の拡張バージョンである RoleHierarchyVoter
は RoleHierarchy
で構成され、そこからユーザーが割り当てられているすべての「到達可能な権限」を取得します。典型的な構成は次のようになります。
<bean id="roleVoter" class="org.springframework.security.access.vote.RoleHierarchyVoter"> <constructor-arg ref="roleHierarchy" /> </bean> <bean id="roleHierarchy" class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl"> <property name="hierarchy"> <value> ROLE_ADMIN > ROLE_STAFF ROLE_STAFF > ROLE_USER ROLE_USER > ROLE_GUEST </value> </property> </bean>
ここでは、階層 ROLE_ADMIN ⇒ ROLE_STAFF ⇒ ROLE_USER ⇒ ROLE_GUEST
に 4 つのロールがあります。 ROLE_ADMIN
で認証されたユーザーは、上記の RoleHierarchyVoter
で構成された AccessDecisionManager
に対してセキュリティ制約が評価された場合、4 つのロールすべてを持っているかのように動作します。 >
シンボルは、「含む」という意味と考えることができます。
ロール階層は、アプリケーションのアクセス制御構成データを簡素化したり、ユーザーに割り当てる必要のある権限の数を減らしたりする便利な手段を提供します。より複雑な要件については、アプリケーションに必要な特定のアクセス権とユーザーに割り当てられているロール間の論理マッピングを定義し、ユーザー情報をロードするときに 2 つを変換することができます。
Spring Security 2.0 以前は、 MethodInvocation
を固定するには、非常に多くのボイラープレート構成が必要でした。メソッドセキュリティの推奨されるアプローチは、名前空間の構成を使用 すること です。このようにして、メソッドセキュリティインフラストラクチャ Bean が自動的に設定されるため、実装クラスについて知る必要はありません。ここで関係するクラスの概要を簡単に説明します。
メソッドのセキュリティは、 MethodInvocation
を保護する MethodSecurityInterceptor
を使用して実施されます。構成アプローチによっては、インターセプターは単一の Bean に固有であるか、複数の Bean 間で共有される場合があります。インターセプターは、 MethodSecurityMetadataSource
インスタンスを使用して、特定のメソッド呼び出しに適用される構成属性を取得します。 MapBasedMethodSecurityMetadataSource
は、メソッド名(ワイルドカードを使用可能)をキーとする構成属性を格納するために使用され、 <intercept-methods>
または <protect-point>
要素を使用してアプリケーションコンテキストで属性が定義されるときに内部的に使用されます。他の実装を使用して、アノテーションベースの構成を処理します。
もちろん、Spring AOP のプロキシメカニズムの 1 つで使用するために、アプリケーションコンテキストで MethodSecurityIterceptor
を直接構成できます。
<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 セキュリティインターセプターは、前のセクションで説明した 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>
以上です! これで、適切と思われる手段( new Person();
など)を使用して、アプリケーション内のどこからでも Bean を作成でき、セキュリティインターセプターが適用されます。
Spring Security 3.0 は、以前に見られた構成属性およびアクセス決定投票者の単純な使用に加えて、Spring EL 式を認可メカニズムとして使用する機能を導入しました。式ベースのアクセス制御は同じアーキテクチャに基づいて構築されていますが、複雑なブールロジックを単一の式にカプセル化できます。
Spring Security は、式のサポートに Spring EL を使用します。トピックをより深く理解することに興味がある場合は、それがどのように機能するかを確認してください。式は、評価コンテキストの一部として「ルートオブジェクト」で評価されます。Spring Security は、組み込み式と現在のプリンシパルなどの値へのアクセスを提供するために、Web およびメソッドセキュリティの特定のクラスをルートオブジェクトとして使用します。
式ルートオブジェクトの基本クラスは SecurityExpressionRoot
です。これにより、Web セキュリティとメソッドセキュリティの両方で使用できる一般的な式が提供されます。
テーブル 11.1: 一般的な組み込み式
式 | 説明 |
---|---|
| 現在のプリンシパルに指定されたロールがある場合、 例: デフォルトでは、指定されたロールが「ROLE_」で始まらない場合は追加されます。これは、 |
| 現在のプリンシパルが提供されたロールのいずれかを持っている場合(文字列のコンマ区切りリストとして与えられた場合) 例: デフォルトでは、指定されたロールが「ROLE_」で始まらない場合は追加されます。これは、 |
| 現在のプリンシパルが指定された権限を持っている場合、 例: |
| 現在のプリンシパルに提供された権限のいずれかがある場合、 例: |
| 現在のユーザーを表すプリンシパルオブジェクトへの直接アクセスを許可する |
|
|
| 常に |
| 常に |
| 現在のプリンシパルが匿名ユーザーの場合、 |
| 現在のプリンシパルが remember-me ユーザーである場合、 |
| ユーザーが匿名でない場合、 |
| ユーザーが匿名ユーザーでも覚えのないユーザーでもない場合は、 |
| ユーザーが指定された許可に対して提供されたターゲットにアクセスできる場合、 |
| ユーザーが指定された許可に対して提供されたターゲットにアクセスできる場合、 |
式を使用して個々の URL を保護するには、最初に <http>
要素の use-expressions
属性を true
に設定する必要があります。Spring Security は、 <intercept-url>
要素の access
属性に Spring EL 式が含まれることを期待します。式はブール値に評価され、アクセスを許可するかどうかを定義する必要があります。例:
<http> <intercept-url pattern="/admin*" access="hasRole('admin') and hasIpAddress('192.168.1.0/24')"/> ... </http>
ここで、アプリケーションの「admin」領域(URL パターンで定義)は、付与された権限「admin」を持ち、IP アドレスがローカルサブネットと一致するユーザーのみが使用できるように定義しました。前のセクションでビルトイン hasRole
式を見てきました。式 hasIpAddress
は、Web セキュリティに固有の追加の組み込み式です。これは、 WebSecurityExpressionRoot
クラスによって定義され、そのインスタンスは、Web アクセス式を評価するときに式ルートオブジェクトとして使用されます。このオブジェクトは、 request
という名前で HttpServletRequest
オブジェクトを直接公開しているため、式でリクエストを直接呼び出すことができます。式が使用されている場合、 WebExpressionVoter
がネームスペースで使用される AccessDecisionManager
に追加されます。そのため、ネームスペースを使用しておらず、式を使用する場合は、これらのいずれかを構成に追加する必要があります。
使用可能な式を継承したい場合は、公開する Spring Bean を簡単に参照できます。例: 次のメソッドシグネチャーを含む webSecurity
という名前の Bean があると仮定します。
public class WebSecurity { public boolean check(Authentication authentication, HttpServletRequest request) { ... } }
以下を使用してメソッドを参照できます。
<http> <intercept-url pattern="/user/**" access="@webSecurity.check(authentication,request)"/> ... </http>
または Java 構成
http .authorizeRequests() .antMatchers("/user/**").access("@webSecurity.check(authentication,request)") ...
URL 内のパス変数を参照できると便利な場合があります。例: /user/{userId}
形式の URL パスから id でユーザーを検索する RESTful アプリケーションを検討します。
パターンに配置することで、パス変数を簡単に参照できます。例: 次のメソッドシグネチャーを含む webSecurity
という名前の Bean がある場合:
public class WebSecurity { public boolean checkUserId(Authentication authentication, int id) { ... } }
以下を使用してメソッドを参照できます。
<http> <intercept-url pattern="/user/{userId}/**" access="@webSecurity.checkUserId(authentication,#userId)"/> ... </http>
または Java 構成
http .authorizeRequests(authorizeRequests -> authorizeRequests .antMatchers("/user/{userId}/**").access("@webSecurity.checkUserId(authentication,#userId)") ... );
どちらの構成でも、一致する URL はパス変数を渡して(そして変換して)checkUserId メソッドに渡します。例: URL が /user/123/resource
の場合、渡される ID は 123
になります。
メソッドのセキュリティは、単純な許可または拒否ルールよりも少し複雑です。Spring Security 3.0 は、式の使用を包括的にサポートできるようにするために、いくつかの新しいアノテーションを導入しました。
式属性をサポートする 4 つのアノテーションがあり、呼び出し前および呼び出し後の認可チェックを可能にし、送信されたコレクション引数または戻り値のフィルタリングもサポートします。それらは @PreAuthorize
、 @PreFilter
、 @PostAuthorize
、 @PostFilter
です。それらの使用は、 global-method-security
名前空間要素を介して有効になります。
<global-method-security pre-post-annotations="enabled"/>
最も明らかに有用なアノテーションは、メソッドを実際に呼び出すことができるかどうかを決定する @PreAuthorize
です。たとえば (「連絡先」サンプルアプリケーションから)
@PreAuthorize("hasRole('USER')") public void create(Contact contact);
つまり、アクセスはロール「ROLE_USER」を持つユーザーにのみ許可されます。明らかに、従来の構成と必要なロールの単純な構成属性を使用して、同じことを簡単に実現できます。しかし、どうですか:
@PreAuthorize("hasPermission(#contact, 'admin')") public void deletePermission(Contact contact, Sid recipient, Permission permission);
ここでは、式の一部としてメソッド引数を実際に使用して、現在のユーザーに特定の連絡先の「管理者」権限があるかどうかを判断しています。以下に示すように 、ビルトイン hasPermission()
式は、アプリケーションコンテキストを介して Spring Security ACL モジュールにリンクされ ます 。式の変数として名前でメソッドの引数にアクセスできます。
Spring Security がメソッドの引数を解決できる方法はいくつかあります。Spring Security は、 DefaultSecurityParameterNameDiscoverer
を使用してパラメーター名を検出します。デフォルトでは、メソッド全体に対して次のオプションが試行されます。
Spring Security の @P
アノテーションがメソッドへの単一の引数に存在する場合、値が使用されます。これは、パラメーター名に関する情報を含まない JDK 8 より前の JDK でコンパイルされたインターフェースに役立ちます。例:
import org.springframework.security.access.method.P; ... @PreAuthorize("#c.name == authentication.name") public void doSomething(@P("c") Contact contact);
バックグラウンドでは、 AnnotationParameterNameDiscoverer
を使用してこの使用を実装し、指定されたアノテーションの value 属性をサポートするようにカスタマイズできます。
Spring Data の @Param
アノテーションがメソッドの少なくとも 1 つのパラメーターに存在する場合、値が使用されます。これは、パラメーター名に関する情報を含まない JDK 8 より前の JDK でコンパイルされたインターフェースに役立ちます。例:
import org.springframework.data.repository.query.Param; ... @PreAuthorize("#n == authentication.name") Contact findContactByName(@Param("n") String name);
バックグラウンドでは、 AnnotationParameterNameDiscoverer
を使用してこの使用を実装し、指定されたアノテーションの value 属性をサポートするようにカスタマイズできます。
式内で任意の Spring-EL 機能を使用できるため、引数のプロパティにアクセスすることもできます。例: 連絡先のユーザー名と一致するユーザー名を持つユーザーのみにアクセスを許可する特定の方法が必要な場合は、次のように記述できます。
@PreAuthorize("#contact.name == authentication.name") public void doSomething(Contact contact);
ここでは、セキュリティコンテキストに格納されている Authentication
である別の組み込み式 authentication
にアクセスしています。式 principal
を使用して、その「プリンシパル」プロパティに直接アクセスすることもできます。多くの場合、値は UserDetails
インスタンスであるため、 principal.username
や principal.enabled
などの式を使用できます。
通常、Less では、メソッドが呼び出された後にアクセス制御チェックを実行することができます。これは、 @PostAuthorize
アノテーションを使用して実現できます。メソッドからの戻り値にアクセスするには、式で組み込み名 returnObject
を使用します。
すでにご存じかもしれませんが、Spring Security はコレクションと配列のフィルタリングをサポートしており、式を使用してこれを実現できるようになりました。これは、メソッドの戻り値で最も一般的に実行されます。例:
@PreAuthorize("hasRole('USER')") @PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')") public List<Contact> getAll();
@PostFilter
アノテーションを使用する場合、Spring Security は返されたコレクションを反復処理し、指定された式が false である要素を削除します。 filterObject
という名前は、コレクション内の現在のオブジェクトを指します。 @PreFilter
を使用して、メソッド呼び出しの前にフィルタリングすることもできますが、これはあまり一般的な要件ではありません。構文はまったく同じですが、コレクション型である引数が複数ある場合は、このアノテーションの filterTarget
プロパティを使用して名前で 1 つ選択する必要があります。
フィルタリングは、データ取得クエリをチューニングするための代替ではないことに注意してください。大規模なコレクションをフィルタリングし、多くのエントリを削除する場合、これは非効率的である可能性があります。
メソッドのセキュリティに固有の組み込み式がいくつかありますが、これはすでに上記で使用されているものです。 filterTarget
と returnValue
の値は非常に単純ですが、 hasPermission()
式を使用することで、より詳細に見ることができます。
hasPermission()
式は PermissionEvaluator
のインスタンスに委譲されます。式システムと Spring Security の ACL システムを橋渡しすることを目的としており、抽象的な認可に基づいて、ドメインオブジェクトの認可制約を指定できます。ACL モジュールへの明示的な依存関係はないため、必要に応じて別の実装に交換できます。インターフェースには 2 つのメソッドがあります。
boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission); boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission);
最初の引数( Authentication
オブジェクト)が指定されていないことを除いて、使用可能な式のバージョンに直接マップします。1 つ目は、アクセスが制御されているドメインオブジェクトがすでにロードされている状況で使用されます。次に、現在のユーザーがそのオブジェクトに対して与えられた許可を持っている場合、式は true を返します。2 番目のバージョンは、オブジェクトがロードされていないが、その識別子がわかっている場合に使用されます。ドメインオブジェクトの抽象的な「型」指定子も必要です。これにより、正しい ACL 権限をロードできます。これは伝統的にオブジェクトの Java クラスでしたが、パーミッションのロードメソッドと一貫している限り、そうである必要はありません。
hasPermission()
式を使用するには、アプリケーションコンテキストで PermissionEvaluator
を明示的に構成する必要があります。これは次のようになります。
<security:global-method-security pre-post-annotations="enabled"> <security:expression-handler ref="expressionHandler"/> </security:global-method-security> <bean id="expressionHandler" class= "org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler"> <property name="permissionEvaluator" ref="myPermissionEvaluator"/> </bean>
myPermissionEvaluator
は、 PermissionEvaluator
を実装する Bean です。通常、これは AclPermissionEvaluator
と呼ばれる ACL モジュールからの実装になります。詳細については、「連絡先」サンプルアプリケーションの構成を参照してください。
メソッドのセキュリティのためにメタアノテーションを使用して、コードを読みやすくすることができます。これは、コードベース全体で同じ複雑な式を繰り返している場合に特に便利です。例: 次のことを考慮してください。
@PreAuthorize("#contact.name == authentication.name")
これをどこでも繰り返す代わりに、代わりに使用できるメタアノテーションを作成できます。
@Retention(RetentionPolicy.RUNTIME) @PreAuthorize("#contact.name == authentication.name") public @interface ContactPermission {}
メタアノテーションは、Spring Security メソッドのセキュリティアノテーションのいずれにも使用できます。仕様への準拠を維持するため、JSR-250 アノテーションはメタアノテーションをサポートしていません。
この例では、ユーザーの認証のみが必要であり、アプリケーションのすべての URL で認証されています。 http.authorizeRequests()
メソッドに複数の子を追加することにより、URL のカスタム要件を指定できます。例:
protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests(authorizeRequests ->authorizeRequests .antMatchers("/resources/**", "/signup", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.anyRequest().authenticated()
) .formLogin(withDefaults()); }
| |
すべてのユーザーがアクセスできる複数の URL パターンを指定しました。具体的には、URL が「/resources/」で始まる、「/signup」に等しい、または「/about」に等しい場合、どのユーザーもリクエストにアクセスできます。 | |
「/admin/」で始まる URL は、ロール「ROLE_ADMIN」を持つユーザーに制限されます。 | |
「/db/」で始まる URL では、ユーザーに「ROLE_ADMIN」と「ROLE_DBA」の両方が必要です。 | |
まだ一致していない URL には、ユーザーの認証のみが必要です |
バージョン 2.0 以降、Spring Security はサービスレイヤーメソッドにセキュリティを追加するためのサポートを大幅に改善しました。JSR-250 アノテーションセキュリティおよびフレームワークの元の @Secured
アノテーションのサポートを提供します。3.0 から、新しい式ベースのアノテーションを利用することもできます。 intercept-methods
要素を使用して Bean 宣言を装飾し、単一の Bean にセキュリティを適用するか、AspectJ スタイルのポイントカットを使用してサービスレイヤー全体で複数の Bean を保護できます。
@Configuration
インスタンスで @EnableGlobalMethodSecurity
アノテーションを使用して、アノテーションベースのセキュリティを有効にできます。例: 以下は、Spring Security の @Secured
アノテーションを有効にします。
@EnableGlobalMethodSecurity(securedEnabled = true) public class MethodSecurityConfig { // ... }
(クラスまたはインターフェース上の)メソッドにアノテーションを追加すると、それに応じてそのメソッドへのアクセスが制限されます。Spring Security のネイティブアノテーションサポートは、メソッドの属性セットを定義します。これらは、AccessDecisionManager に渡され、実際の決定が行われます。
public interface BankService { @Secured("IS_AUTHENTICATED_ANONYMOUSLY") public Account readAccount(Long id); @Secured("IS_AUTHENTICATED_ANONYMOUSLY") public Account[] findAccounts(); @Secured("ROLE_TELLER") public Account post(Account account, double amount); }
JSR-250 アノテーションのサポートは、次を使用して有効にできます。
@EnableGlobalMethodSecurity(jsr250Enabled = true) public class MethodSecurityConfig { // ... }
これらは標準ベースであり、単純なロールベースの制約を適用できますが、Spring Security のネイティブアノテーションはありません。新しい式ベースの構文を使用するには、次を使用します
@EnableGlobalMethodSecurity(prePostEnabled = true) public class MethodSecurityConfig { // ... }
同等の Java コードは
public interface BankService { @PreAuthorize("isAnonymous()") public Account readAccount(Long id); @PreAuthorize("isAnonymous()") public Account[] findAccounts(); @PreAuthorize("hasAuthority('ROLE_TELLER')") public Account post(Account account, double amount); }
@EnableGlobalMethodSecurity
アノテーションで許可されるよりも複雑な操作を実行する必要がある場合があります。これらのインスタンスでは、 GlobalMethodSecurityConfiguration
を継承して、 @EnableGlobalMethodSecurity
アノテーションがサブクラスに存在することを確認できます。例: カスタム MethodSecurityExpressionHandler
を提供する場合、次の構成を使用できます。
@EnableGlobalMethodSecurity(prePostEnabled = true) public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration { @Override protected MethodSecurityExpressionHandler createExpressionHandler() { // ... create and return custom MethodSecurityExpressionHandler ... return expressionHandler; } }
オーバーライドできるメソッドの詳細については、 GlobalMethodSecurityConfiguration
Javadoc を参照してください。
この要素は、(要素に適切な属性を設定することにより)アプリケーションでアノテーションベースのセキュリティを有効にし、アプリケーションコンテキスト全体に適用されるセキュリティポイントカット宣言をグループ化するために使用されます。 <global-method-security>
要素を 1 つだけ宣言する必要があります。次の宣言により、Spring Security の @Secured
のサポートが有効になります。
<global-method-security secured-annotations="enabled" />
(クラスまたはインターフェース上の)メソッドにアノテーションを追加すると、それに応じてそのメソッドへのアクセスが制限されます。Spring Security のネイティブアノテーションサポートは、メソッドの属性セットを定義します。これらは、実際の決定を行うために AccessDecisionManager
に渡されます。
public interface BankService { @Secured("IS_AUTHENTICATED_ANONYMOUSLY") public Account readAccount(Long id); @Secured("IS_AUTHENTICATED_ANONYMOUSLY") public Account[] findAccounts(); @Secured("ROLE_TELLER") public Account post(Account account, double amount); }
JSR-250 アノテーションのサポートは、次を使用して有効にできます。
<global-method-security jsr250-annotations="enabled" />
これらは標準ベースであり、単純なロールベースの制約を適用できますが、Spring Security のネイティブアノテーションはありません。新しい式ベースの構文を使用するには、次を使用します
<global-method-security pre-post-annotations="enabled" />
同等の Java コードは
public interface BankService { @PreAuthorize("isAnonymous()") public Account readAccount(Long id); @PreAuthorize("isAnonymous()") public Account[] findAccounts(); @PreAuthorize("hasAuthority('ROLE_TELLER')") public Account post(Account account, double amount); }
式ベースのアノテーションは、ユーザーの権限リストに対するロール名のチェックにとどまらない単純なルールを定義する必要がある場合に適しています。
![]() | メモ |
---|---|
=== アノテーション付きメソッドは、Spring Bean として定義されているインスタンスに対してのみ保護されます(メソッドセキュリティが有効になっている同じアプリケーションコンテキスト内)。Spring によって作成されていないインスタンスを保護する場合(たとえば、 |
![]() | メモ |
---|---|
=== 同じアプリケーションで複数のタイプのアノテーションを有効にできますが、他の方法では動作が適切に定義されないため、インターフェースまたはクラスには 1 つのタイプのみを使用する必要があります。特定のメソッドに適用される 2 つのアノテーションが見つかった場合、そのうちの 1 つだけが適用されます。=== |
protect-pointcut
の使用は、単純な宣言のみで多くの Bean にセキュリティを適用できるため、特に強力です。次の例を考えてみましょう。
<global-method-security> <protect-pointcut expression="execution(* com.mycompany.*Service.*(..))" access="ROLE_USER"/> </global-method-security>
これにより、クラスが com.mycompany
パッケージにあり、クラス名が「Service」で終わるアプリケーションコンテキストで宣言された Bean のすべてのメソッドが保護されます。 ROLE_USER
ロールを持つユーザーのみがこれらのメソッドを呼び出すことができます。URL マッチングと同様に、最初のマッチング表現が使用されるため、ポイントカットのリストで最も具体的な一致が最初に来る必要があります。セキュリティアノテーションはポイントカットよりも優先されます。
複雑なアプリケーションでは、多くの場合、単純に Web リクエストまたはメソッド呼び出しレベルではなく、アクセス認可を定義する必要があります。代わりに、セキュリティの決定には、誰( Authentication
)、どこ( MethodInvocation
)および何( SomeDomainObject
)の両方を含める必要があります。言い換えると、認可の決定では、メソッド呼び出しの実際のドメインオブジェクトインスタンスサブジェクトも考慮する必要があります。
ペットクリニック用のアプリケーションを設計しているとします。Spring ベースのアプリケーションのユーザーには、ペットクリニックのスタッフとペットクリニックの顧客の 2 つのメイングループがあります。スタッフはすべてのデータにアクセスできますが、顧客は自分の顧客レコードのみを見ることができます。もう少し面白くするために、顧客は、「子犬幼稚園」のメンターやローカル「ポニークラブ」の社長など、他のユーザーに顧客レコードの閲覧を許可できます。Spring Security を基盤として使用すると、使用できるアプローチがいくつかあります。
Customer
ドメインオブジェクトインスタンス内のコレクションを参照して、アクセスできるユーザーを判断できます。 SecurityContextHolder.getContext().getAuthentication()
を使用すると、 Authentication
オブジェクトにアクセスできます。 AccessDecisionVoter
を記述して、 Authentication
オブジェクトに格納されている GrantedAuthority[]
からセキュリティを強化します。これは、 AuthenticationManager
が、プリンシパルがアクセスできる Customer
ドメインオブジェクトインスタンスのそれぞれを表すカスタム GrantedAuthority[]
を Authentication
に取り込む必要があることを意味します。 AccessDecisionVoter
を記述してセキュリティを強化し、ターゲット Customer
ドメインオブジェクトを直接開きます。これは、投票者が Customer
オブジェクトを取得できる DAO にアクセスする必要があることを意味します。次に、 Customer
オブジェクトの承認済みユーザーのコレクションにアクセスし、適切な決定を下します。 これらのアプローチはいずれも完全に正当です。ただし、最初のステップでは、認可チェックをビジネスコードに結合します。これに関する主な問題には、単体テストの難易度の向上と、 Customer
認証ロジックを他の場所で再利用することがより困難になるという事実が含まれます。 Authentication
オブジェクトから GrantedAuthority[]
を取得することもできますが、多数の Customer
に拡張することはできません。ユーザーが 5,000 Customer
にアクセスできる場合(この場合はほとんどありませんが、大規模なポニークラブで人気の獣医だと想像してください! )、 Authentication
オブジェクトの構築に必要なメモリ量と時間は望ましくありません。 Customer
を外部コードから直接開く最後のメソッドは、おそらく 3 つのうちの最良のメソッドです。関心事の分離を実現し、メモリまたは CPU サイクルを誤用しませんが、 AccessDecisionVoter
と最終的なビジネスメソッド自体の両方が Customer
オブジェクトの取得を担当する DAO への呼び出しを実行するという点で、依然として非効率的です。メソッド呼び出しごとに 2 回アクセスすることは明らかに望ましくありません。さらに、リストされているすべてのアプローチで、独自のアクセス制御リスト(ACL)の永続性とビジネスロジックをゼロから作成する必要があります。
幸いなことに、別の選択肢があります。これについては以下で説明します。
Spring Security の ACL サービスは spring-security-acl-xxx.jar
で提供されます。Spring Security のドメインオブジェクトインスタンスのセキュリティ機能を使用するには、この JAR をクラスパスに追加する必要があります。
Spring Security のドメインオブジェクトインスタンスのセキュリティ機能は、アクセス制御リスト(ACL)の概念に基づいています。システム内のすべてのドメインオブジェクトインスタンスには独自の ACL があり、ACL はそのドメインオブジェクトを操作できる人と操作できない人の詳細を記録します。これを念頭に置いて、Spring Security は、アプリケーションに 3 つの主要な ACL 関連機能を提供します。
最初の箇条書きで示されるように、Spring Security ACL モジュールの主な機能の 1 つは、ACL を取得する高性能なメソッドを提供することです。この ACL リポジトリ機能は非常に重要です。システム内のすべてのドメインオブジェクトインスタンスには複数のアクセス制御エントリがあり、各 ACL はツリー状の構造の他の ACL から継承する可能性があるためです(これは Spring Security ですぐにサポートされます。および非常に一般的に使用されています)。Spring Security の ACL 機能は、プラグ可能なキャッシュ、デッドロックを最小限に抑えるデータベース更新、ORM フレームワークからの独立性(JDBC を直接使用)、適切なカプセル化、透過的なデータベース更新とともに、ACL の高性能検索を提供するように注意深く設計されています。
データベースが ACL モジュールの操作の中心であることを前提に、実装でデフォルトで使用される 4 つのメインテーブルを見てみましょう。表は、典型的な Spring Security ACL デプロイのサイズの順に以下に示されており、最も多くの行を持つ表が最後にリストされています。
GrantedAuthority
のどちらを参照しているかを示すフラグです。一意のプリンシパルまたは GrantedAuthority
ごとに 1 つの行があります。許可を受け取るという文脈で使用される場合、SID は一般に「受信者」と呼ばれます。 最後の段落で記述されていたように、ACL システムは整数ビットマスキングを使用します。心配不要です。ACL システムを使用するためのビットシフトの細かい点を意識する必要はありませんが、オンまたはオフに切り替えることができる 32 ビットがあると言えば十分です。これらの各ビットは許可を表し、デフォルトでは許可は読み取り(ビット 0)、書き込み(ビット 1)、作成(ビット 2)、削除(ビット 3)および管理(ビット 4)です。他のアクセス許可を使用する場合は、独自の Permission
インスタンスを簡単に実装できます。ACL フレームワークの残りの部分は、拡張機能の知識がなくても動作します。
システム内のドメインオブジェクトの数は、整数ビットマスキングを使用することを選択したという事実とはまったく関係がないことを理解することが重要です。許可に 32 ビットを使用できますが、数十億のドメインオブジェクトインスタンス(ACL_OBJECT_IDENTITY およびおそらく ACL_ENTRY の数十億行を意味する)を持つことができます。これは、潜在的なドメインオブジェクトごとにビットが必要であると人々が誤って信じていることがあるためです。
ACL システムの機能と、テーブル構造での表示の基本的な概要を説明したため、次に主要なインターフェースを見てみましょう。主なインターフェースは次のとおりです。
Acl
: すべてのドメインオブジェクトには Acl
オブジェクトが 1 つだけあり、 Acl
オブジェクトは内部で AccessControlEntry
を保持し、 Acl
の所有者を知っています。Acl はドメインオブジェクトを直接参照せず、代わりに ObjectIdentity
を参照します。 Acl
は ACL_OBJECT_IDENTITY テーブルに保存されます。 AccessControlEntry
: Acl
は、複数の AccessControlEntry
を保持します。これらは、フレームワークでは ACE と略されることがよくあります。各 ACE は、 Permission
、 Sid
、 Acl
の特定のタプルを参照します。ACE は、許可または非許可であり、監査設定を含むこともできます。ACE は ACL_ENTRY テーブルに保存されます。 Permission
: 許可は、特定の不変のビットマスクを表し、ビットマスキングと情報の出力に便利な機能を提供します。上記の基本的なアクセス許可(ビット 0 〜 4)は、 BasePermission
クラスに含まれています。 Sid
: ACL モジュールは、プリンシパルと GrantedAuthority[]
を参照する必要があります。間接レベルは、「セキュリティ ID」の略語である Sid
インターフェースによって提供されます。一般的なクラスには、 PrincipalSid
( Authentication
オブジェクト内のプリンシパルを表す)および GrantedAuthoritySid
が含まれます。セキュリティ ID 情報は ACL_SID テーブルに保存されます。 ObjectIdentity
: 各ドメインオブジェクトは、 ObjectIdentity
によって ACL モジュール内で内部的に表されます。デフォルトの実装は ObjectIdentityImpl
と呼ばれます。 AclService
: 指定された ObjectIdentity
に適用可能な Acl
を取得します。含まれる実装( JdbcAclService
)では、検索操作は LookupStrategy
に委譲されます。 LookupStrategy
は、バッチ検索 (BasicLookupStrategy
を使用して ACL 情報を取得するための高度に最適化された戦略を提供し、マテリアライズドビュー、階層クエリ、同様のパフォーマンス中心の非 ANSI SQL 機能を活用するカスタム実装をサポートします。 MutableAclService
: 永続化のために、変更された Acl
を提示できます。希望しない場合、このインターフェースを使用することは必須ではありません。すぐに使える AclService および関連するデータベースクラスはすべて ANSI SQL を使用することに注意してください。これはすべての主要なデータベースで機能するはずです。執筆時点では、システムは Hypersonic SQL、PostgreSQL、Microsoft SQL Server、Oracle を使用して正常にテストされていました。
Spring Security には、ACL モジュールを示す 2 つのサンプルが付属しています。1 つ目は Contacts サンプルで、もう 1 つは Document Management System(DMS)サンプルです。例としてこれらを確認することをお勧めします。
Spring Security の ACL 機能の使用を開始するには、ACL 情報をどこかに保存する必要があります。これには、Spring を使用した DataSource
のインスタンス化が必要です。次に、 DataSource
は JdbcMutableAclService
および BasicLookupStrategy
インスタンスに注入されます。後者は高性能 ACL 検索機能を提供し、前者はミューテーター機能を提供します。構成例については、Spring Security に同梱されているサンプルのいずれかを参照してください。また、最後のセクションにリストされている 4 つの ACL 固有のテーブルをデータベースに追加する必要があります(適切な SQL ステートメントについては ACL サンプルを参照してください)。
必要なスキーマを作成して JdbcMutableAclService
をインスタンス化したら、次にドメインモデルが Spring Security ACL パッケージとの相互運用性をサポートしていることを確認する必要があります。 ObjectIdentityImpl
が使用できる多くのメソッドを提供するため、 ObjectIdentityImpl
で十分であることを願っています。ほとんどの人は、 public Serializable getId()
メソッドを含むドメインオブジェクトを持っています。戻り値の型が長い場合、または long と互換性がある場合(int など)、 ObjectIdentity
の課題をさらに考慮する必要はありません。ACL モジュールの多くの部分は、長い識別子に依存しています。long(または int、byte など)を使用していない場合、多くのクラスを再実装する必要がある可能性が非常に高くなります。Spring Security の ACL モジュールでは、最も一般的な識別子データ型であるすべてのデータベースシーケンスとすでに互換性があり、すべての一般的な使用シナリオに対応するのに十分な長さがあるため、長い識別子をサポートするつもりはありません
次のコードは、 Acl
を作成する方法、または既存の Acl
を変更する方法を示しています。
// Prepare the information we'd like in our access control entry (ACE) ObjectIdentity oi = new ObjectIdentityImpl(Foo.class, new Long(44)); Sid sid = new PrincipalSid("Samantha"); Permission p = BasePermission.ADMINISTRATION; // Create or update the relevant ACL MutableAcl acl = null; try { acl = (MutableAcl) aclService.readAclById(oi); } catch (NotFoundException nfe) { acl = aclService.createAcl(oi); } // Now grant some permissions via an access control entry (ACE) acl.insertAce(acl.getEntries().length, p, sid, true); aclService.updateAcl(acl);
上記の例では、ID 番号 44 の「Foo」ドメインオブジェクトに関連付けられた ACL を取得しています。次に、ACE を追加して、「Samantha」というプリンシパルがオブジェクトを「管理」できるようにします。コードの断片は、insertAce メソッドを除き、比較的自明です。insertAce メソッドの最初の引数は、Acl のどの位置に新しいエントリを挿入するかを決定します。上記の例では、新しい ACE を既存の ACE の最後に配置しています。最後の引数は、ACE が許可するか拒否するかを示すブール値です。ほとんどの場合、許可されます(true)が、拒否している場合(false)は、アクセス許可が事実上ブロックされています。
Spring Security は、DAO またはリポジトリ操作の一部として ACL を自動的に作成、更新、または削除するための特別な統合を提供しません。代わりに、個々のドメインオブジェクトに対して上記のようなコードを記述する必要があります。サービスレイヤーで AOP を使用して、ACL 情報をサービスレイヤーの操作に自動的に統合することを検討する価値があります。これは過去に非常に効果的なアプローチであることがわかりました。
上記の手法を使用していくつかの ACL 情報をデータベースに保存したら、次のステップは、認可決定ロジックの一部として実際に ACL 情報を使用することです。ここには多くの選択肢があります。メソッド呼び出しの前後にそれぞれ起動する独自の AccessDecisionVoter
または AfterInvocationProvider
を作成できます。このようなクラスは、 AclService
を使用して関連する ACL を取得し、 Acl.isGranted(Permission[] permission, Sid[] sids, boolean administrativeMode)
を呼び出して認可を認可するか拒否するかを決定します。または、 AclEntryVoter
、 AclEntryAfterInvocationProvider
、または AclEntryAfterInvocationCollectionFilteringProvider
クラスを使用できます。これらのクラスはすべて、実行時に ACL 情報を評価するための宣言ベースのアプローチを提供するため、コードを記述する必要がありません。これらのクラスの使用方法については、サンプルアプリケーションを参照してください。