最新の安定バージョンについては、Spring Security 6.3.1 を使用してください! |
認可アーキテクチャ
オーソリティー
Authentication
は、すべての Authentication
実装が GrantedAuthority
オブジェクトのリストを格納する方法について説明しています。これらは、プリンシパルに付与された権限を表します。GrantedAuthority
オブジェクトは、AuthenticationManager
によって Authentication
オブジェクトに挿入され、後で認可決定を行うときに AuthorizationManager
のいずれかによって読み取られます。
GrantedAuthority
は、メソッドが 1 つだけのインターフェースです。
String getAuthority();
この方法により、AuthorizationManager
は GrantedAuthority
の正確な String
表現を取得できます。表現を String
として返すことにより、GrantedAuthority
はほとんどの AuthorizationManager
および AccessDecisionManager
で簡単に「読み取る」ことができます。GrantedAuthority
を String
として正確に表すことができない場合、GrantedAuthority
は「複雑」と見なされ、getAuthority()
は null
を返す必要があります。
「複雑な」 GrantedAuthority
の例は、異なる顧客アカウント番号に適用される操作と権限のしきい値のリストを格納する実装です。この複雑な GrantedAuthority
を String
として表現することは非常に難しく、その結果、getAuthority()
メソッドは null
を返すはずです。これは、AuthorizationManager
の内容を理解するために GrantedAuthority
実装を特にサポートする必要があることを AuthorizationManager
に示します。
Spring Security には、GrantedAuthority
の具体的な実装 SimpleGrantedAuthority
が含まれています。これにより、ユーザー指定の String
を GrantedAuthority
に変換できます。セキュリティアーキテクチャに含まれるすべての AuthenticationProvider
は、SimpleGrantedAuthority
を使用して Authentication
オブジェクトに入力します。
呼び出し前の処理
Spring Security は、メソッド呼び出しや Web リクエストなどのセキュアオブジェクトへのアクセスを制御するインターセプターを提供します。呼び出しの続行を許可するかどうかの呼び出し前の決定は、AccessDecisionManager
によって行われます。
AuthorizationManager
AuthorizationManager
は両方の AccessDecisionManager
および AccessDecisionVoter
に取って代わります。
AccessDecisionManager
または AccessDecisionVoter
をカスタマイズするアプリケーションは、AuthorizationManager
の使用に変更することをお勧めします。
AuthorizationManager
は AuthorizationFilter
によって呼び出され、最終的なアクセス制御の決定を行う責任があります。AuthorizationManager
インターフェースには、次の 2 つのメソッドが含まれています。
AuthorizationDecision check(Supplier<Authentication> authentication, Object secureObject);
default AuthorizationDecision verify(Supplier<Authentication> authentication, Object secureObject)
throws AccessDeniedException {
// ...
}
AuthorizationManager
の check
メソッドには、認可の決定を行うために必要なすべての関連情報が渡されます。特に、セキュア Object
を渡すと、実際のセキュアオブジェクト呼び出しに含まれる引数をインスペクションできます。例: 安全なオブジェクトが MethodInvocation
であると仮定しましょう。MethodInvocation
に任意の Customer
引数を照会し、AuthorizationManager
に何らかのセキュリティロジックを実装して、プリンシパルがその顧客での操作を認可されていることを確認するのは簡単です。実装は、アクセスが認可された場合は正の AuthorizationDecision
を返し、アクセスが拒否された場合は負の AuthorizationDecision
を返し、決定を控えた場合は null の AuthorizationDecision
を返すことが期待されます。
verify
は check
を呼び出し、その後、負の AuthorizationDecision
の場合は AccessDeniedException
をスローします。
デリゲートベースの AuthorizationManager 実装
ユーザーは独自の AuthorizationManager
を実装して認証のすべての側面を制御できますが、Spring Security には、個々の AuthorizationManager
と連携できる委譲 AuthorizationManager
が付属しています。
RequestMatcherDelegatingAuthorizationManager
は、リクエストを最も適切なデリゲート AuthorizationManager
と照合します。メソッドのセキュリティには、AuthorizationManagerBeforeMethodInterceptor
および AuthorizationManagerAfterMethodInterceptor
を使用できます。
AuthorizationManager の実装は、関連するクラスを示しています。
このアプローチを使用すると、認可の決定時に AuthorizationManager
実装の構成をポーリングできます。
AuthorityAuthorizationManager
Spring Security で提供される最も一般的な AuthorizationManager
は AuthorityAuthorizationManager
です。現在の Authentication
を検索するために、特定の権限のセットで構成されます。Authentication
に構成済みの権限のいずれかが含まれている場合は、正の AuthorizationDecision
が返されます。それ以外の場合は、負の AuthorizationDecision
を返します。
AuthenticatedAuthorizationManager
別のマネージャーは AuthenticatedAuthorizationManager
です。これを使用して、匿名の完全認証済みユーザーと remember-me 認証済みユーザーを区別できます。多くのサイトでは、remember-me 認証で特定の制限付きアクセスが許可されていますが、フルアクセスを取得するには、ユーザーがログインして ID を確認する必要があります。
カスタム認可マネージャー
もちろん、カスタム AuthorizationManager
を実装することもでき、必要なほぼすべてのアクセス制御ロジックをその中に入れることができます。アプリケーションに固有の場合もあれば(ビジネスロジック関連)、セキュリティ管理ロジックを実装する場合もあります。例: Open PolicyAgent または独自の認証データベースを照会できる実装を作成できます。
Spring Web サイトには、従来の AccessDecisionVoter を使用して、アカウントが一時停止されているユーザーのリアルタイムアクセスを拒否する方法を説明したブログ記事 (英語) があります。代わりに AuthorizationManager を実装することで、同じ結果を達成できます。 |
AccessDecisionManager と AccessDecisionVoters の適応
AuthorizationManager
の前に、Spring Security は AccessDecisionManager
および AccessDecisionVoter
を公開しました。
古いアプリケーションを移行する場合のように、AccessDecisionManager
または AccessDecisionVoter
を呼び出す AuthorizationManager
を導入することが望ましい場合があります。
既存の AccessDecisionManager
を呼び出すには、次のようにします。
Java
@Component
public class AccessDecisionManagerAuthorizationManagerAdapter implements AuthorizationManager {
private final AccessDecisionManager accessDecisionManager;
private final SecurityMetadataSource securityMetadataSource;
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, Object object) {
try {
Collection<ConfigAttributes> attributes = this.securityMetadataSource.getAttributes(object);
this.accessDecisionManager.decide(authentication.get(), object, attributes);
return new AuthorizationDecision(true);
} catch (AccessDeniedException ex) {
return new AuthorizationDecision(false);
}
}
@Override
public void verify(Supplier<Authentication> authentication, Object object) {
Collection<ConfigAttributes> attributes = this.securityMetadataSource.getAttributes(object);
this.accessDecisionManager.decide(authentication.get(), object, attributes);
}
}
そしてそれをあなたの SecurityFilterChain
に接続します。
または、AccessDecisionVoter
のみを呼び出すには、次のようにします。
Java
@Component
public class AccessDecisionVoterAuthorizationManagerAdapter implements AuthorizationManager {
private final AccessDecisionVoter accessDecisionVoter;
private final SecurityMetadataSource securityMetadataSource;
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, Object object) {
Collection<ConfigAttributes> attributes = this.securityMetadataSource.getAttributes(object);
int decision = this.accessDecisionVoter.vote(authentication.get(), object, attributes);
switch (decision) {
case ACCESS_GRANTED:
return new AuthorizationDecision(true);
case ACCESS_DENIED:
return new AuthorizationDecision(false);
}
return null;
}
}
そしてそれをあなたの SecurityFilterChain
に接続します。
階層的なロール
アプリケーションの特定のロールが他のロールを自動的に「含める」ことが一般的な要件です。例: 「管理者」と「ユーザー」のロールの概念を持つアプリケーションでは、管理者が通常のユーザーができることをすべて行えるようにしたい場合があります。これを実現するには、すべての管理ユーザーにも「ユーザー」ロールが割り当てられていることを確認します。または、"user" ロールに "admin" ロールも含める必要があるすべてのアクセス制約を変更できます。アプリケーションにさまざまなロールがある場合、これは非常に複雑になる可能性があります。
ロール階層を使用すると、どのロール(または権限)に他のロールを含めるかを構成できます。Spring Security の RoleVoter
の拡張バージョンである RoleHierarchyVoter
は、RoleHierarchy
で構成されており、そこから、ユーザーに割り当てられているすべての「到達可能な権限」を取得します。一般的な構成は次のようになります。
Java
XML
@Bean
AccessDecisionVoter hierarchyVoter() {
RoleHierarchy hierarchy = new RoleHierarchyImpl();
hierarchy.setHierarchy("ROLE_ADMIN > ROLE_STAFF\n" +
"ROLE_STAFF > ROLE_USER\n" +
"ROLE_USER > ROLE_GUEST");
return new RoleHierarchyVoter(hierarchy);
}
<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
を呼び出すように適合された AuthorizationManager
に対してセキュリティ制約が評価されると、4 つのロールすべてを持っているかのように動作します。>
シンボルは、「含む」という意味と考えることができます。
ロール階層は、アプリケーションのアクセス制御構成データを簡素化したり、ユーザーに割り当てる必要のある権限の数を減らしたりする便利な手段を提供します。より複雑な要件については、アプリケーションに必要な特定のアクセス権とユーザーに割り当てられているロール間の論理マッピングを定義し、ユーザー情報をロードするときに 2 つを変換することができます。
レガシー認証コンポーネント
Spring Security には、いくつかのレガシーコンポーネントが含まれています。それらはまだ削除されていないため、ドキュメントは歴史的な目的で含まれています。推奨される代替は上記のとおりです。 |
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 実装
ユーザーは独自の AccessDecisionManager
を実装して認証のすべての側面を制御できますが、Spring Security には投票に基づくいくつかの AccessDecisionManager
実装が含まれています。投票決定マネージャーは、関連するクラスを示しています。
このアプローチを使用して、一連の 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
からの投票には追加の重み付けが適用される場合がありますが、特定の投票者からの拒否投票には拒否権が付与される場合があります。
RoleVoter
Spring Security で提供される最も一般的に使用される AccessDecisionVoter
は、単純な RoleVoter
です。これは、構成属性を単純なロール名として扱い、ユーザーにそのロールが割り当てられている場合にアクセス権を付与します。
ConfigAttribute
が接頭辞 ROLE_
で始まる場合に投票します。ROLE_
で始まる 1 つ以上の ConfigAttributes
と正確に等しい(getAuthority()
メソッドを介して) String
表現を返す GrantedAuthority
がある場合、アクセスを許可するために投票します。ROLE_
で始まる ConfigAttribute
の正確な一致がない場合、RoleVoter
はアクセスを拒否するために投票します。ConfigAttribute
が ROLE_
で始まっていない場合、投票者は棄権します。
AuthenticatedVoter
暗黙的に確認したもう 1 つの投票者は AuthenticatedVoter
です。これは、匿名ユーザー、完全認証ユーザー、記憶ユーザー認証ユーザーを区別するために使用できます。多くのサイトでは、remember-me 認証で特定の制限付きアクセスが許可されていますが、完全なアクセスのためにログインすることでユーザーに ID の確認を要求します。
属性 IS_AUTHENTICATED_ANONYMOUSLY
を使用して匿名アクセスを許可したとき、この属性は AuthenticatedVoter
によって処理されていました。詳細については、このクラスの Javadoc を参照してください。
カスタム投票者
当然、カスタム AccessDecisionVoter
を実装することもでき、必要なほぼすべてのアクセス制御ロジックを配置できます。アプリケーション固有のもの(ビジネスロジック関連)か、セキュリティ管理ロジックを実装している場合があります。例: Spring の Web サイトには、投票者を使用して、アカウントが停止されたユーザーへのアクセスをリアルタイムで拒否する方法を説明したブログ記事 (英語) があります。
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
構成属性によって実現されます。