認可アーキテクチャ
このセクションでは、認可に適用される Spring Security アーキテクチャーについて説明します。
オーソリティー
Authentication
は、すべての Authentication
実装が GrantedAuthority
オブジェクトのリストを格納する方法について説明しています。これらは、プリンシパルに付与された権限を表します。GrantedAuthority
オブジェクトは、AuthenticationManager
によって Authentication
オブジェクトに挿入され、後で認可決定を行うときに AccessDecisionManager
インスタンスによって読み取られます。
GrantedAuthority
インターフェースには 1 つの方法しかありません。
String getAuthority();
このメソッドは、GrantedAuthority
の正確な String
表現を取得するために AuthorizationManager
インスタンスによって使用されます。表現を String
として返すことにより、ほとんどの AuthorizationManager
実装で GrantedAuthority
を簡単に「読み取る」ことができます。GrantedAuthority
を String
として正確に表現できない場合、GrantedAuthority
は「複雑」とみなされ、getAuthority()
は null
を返さなければなりません。
複雑な GrantedAuthority
の例としては、さまざまな顧客アカウント番号に適用される操作と権限のしきい値のリストを保存する実装が挙げられます。この複雑な GrantedAuthority
を String
として表現するのは非常に困難です。その結果、getAuthority()
メソッドは null
を返す必要があります。これは、AuthorizationManager
に対して、その内容を理解するために特定の GrantedAuthority
実装をサポートする必要があることを示します。
Spring Security には、1 つの具体的な GrantedAuthority
実装が含まれています: SimpleGrantedAuthority
。この実装により、ユーザー指定の String
を GrantedAuthority
に変換できます。セキュリティアーキテクチャに含まれるすべての AuthenticationProvider
インスタンスは、SimpleGrantedAuthority
を使用して Authentication
オブジェクトにデータを入力します。
GrantedAuthorityDefaults
を使用してこれをカスタマイズできます。GrantedAuthorityDefaults
は、ロールベースの認可ルールに使用するプレフィックスをカスタマイズできるようにするために存在します。
次のように、GrantedAuthorityDefaults
Bean を公開することにより、異なるプレフィックスを使用するように認可ルールを構成できます。
Java
Kotlin
XML
@Bean
static GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults("MYPREFIX_");
}
companion object {
@Bean
fun grantedAuthorityDefaults() : GrantedAuthorityDefaults {
return GrantedAuthorityDefaults("MYPREFIX_");
}
}
<bean id="grantedAuthorityDefaults" class="org.springframework.security.config.core.GrantedAuthorityDefaults">
<constructor-arg value="MYPREFIX_"/>
</bean>
|
呼び出し処理
Spring Security は、メソッド呼び出しや Web リクエストなどの安全なオブジェクトへのアクセスを制御するインターセプターを提供します。呼び出しの続行が許可されるかどうかに関する呼び出し前の決定は、AuthorizationManager
インスタンスによって行われます。また、指定された値を返すかどうかの呼び出し後の決定は、AuthorizationManager
インスタンスによって行われます。
AuthorizationManager
AuthorizationManager
は両方の AccessDecisionManager
および AccessDecisionVoter
に取って代わります。
AccessDecisionManager
または AccessDecisionVoter
をカスタマイズするアプリケーションは、AuthorizationManager
の使用に変更することをお勧めします。
AuthorizationManager
は、Spring Security のリクエストベース、メソッドベース、メッセージベースの認可コンポーネントによって呼び出され、最終的なアクセス制御の決定を行います。AuthorizationManager
インターフェースには 2 つのメソッドが含まれています。
AuthorizationDecision check(Supplier<Authentication> authentication, Object secureObject);
default void 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 を確認する必要があります。
AuthorizationManagers
AuthorizationManagers
(Javadoc) には、個々の AuthorizationManager
をより洗練された式に構成するための便利な静的ファクトリもあります。
カスタム認可マネージャー
もちろん、カスタム 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<ConfigAttribute> 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<ConfigAttribute> 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<ConfigAttribute> 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" ロールも含める必要があるすべてのアクセス制約を変更できます。アプリケーションにさまざまなロールがある場合、これは非常に複雑になる可能性があります。
ロール階層を使用すると、どのロール (または権限) に他のロールを含めるかを設定できます。これは、HttpSecurity#authorizeHttpRequests
のフィルターベースの認可と、事前リアクティブアノテーションの DefaultMethodSecurityExpressionHandler
、@Secured
の SecuredAuthorizationManager
、および JSR-250 アノテーションの Jsr250AuthorizationManager
によるメソッドベースの認可でサポートされています。次の方法で、これらすべての動作を一度に設定できます。
Java
XML
@Bean
static RoleHierarchy roleHierarchy() {
return RoleHierarchyImpl.withDefaultRolePrefix()
.role("ADMIN").implies("STAFF")
.role("STAFF").implies("USER")
.role("USER").implies("GUEST")
.build();
}
// and, if using pre-post method security also add
@Bean
static MethodSecurityExpressionHandler methodSecurityExpressionHandler(RoleHierarchy roleHierarchy) {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setRoleHierarchy(roleHierarchy);
return expressionHandler;
}
<bean id="roleHierarchy"
class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl" factory-method="fromHierarchy">
<constructor-arg>
<value>
ROLE_ADMIN > ROLE_STAFF
ROLE_STAFF > ROLE_USER
ROLE_USER > ROLE_GUEST
</value>
</constructor-arg>
</bean>
<!-- and, if using method security also add -->
<bean id="methodSecurityExpressionHandler"
class="org.springframework.security.access.expression.method.MethodSecurityExpressionHandler">
<property ref="roleHierarchy"/>
</bean>
ここでは、階層 ROLE_ADMIN ⇒ ROLE_STAFF ⇒ ROLE_USER ⇒ ROLE_GUEST
に 4 つのロールがあります。ROLE_ADMIN
で認証されたユーザーは、フィルターまたはメソッドベースのルールに対してセキュリティ制約が評価されるときに、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
であると想定します。MethodInvocation
に任意の Customer
引数を照会してから、AccessDecisionManager
に何らかのセキュリティロジックを実装して、プリンシパルがその顧客での操作を認可されていることを確認できます。アクセスが拒否された場合、実装は AccessDeniedException
をスローすることが期待されます。
supports(ConfigAttribute)
メソッドは、起動時に AbstractSecurityInterceptor
によって呼び出され、AccessDecisionManager
が渡された ConfigAttribute
を処理できるかどうかを判別します。supports(Class)
メソッドは、セキュリティインターセプターの実装によって呼び出され、構成された AccessDecisionManager
がセキュリティインターセプターが提示する型のセキュアオブジェクトをサポートするようにします。
投票ベースの AccessDecisionManager 実装
ユーザーは独自の AccessDecisionManager
を実装して認可のすべての側面を制御できますが、Spring Security には投票に基づくいくつかの AccessDecisionManager
実装が含まれています。投票決定マネージャーは、関連するクラスについて説明しています。
次のイメージは、AccessDecisionManager
インターフェースを示しています。
このアプローチを使用することにより、一連の AccessDecisionVoter
実装が認可決定でポーリングされます。次に、AccessDecisionManager
は、投票の評価に基づいて AccessDeniedException
をスローするかどうかを決定します。
AccessDecisionVoter
インターフェースには 3 つのメソッドがあります。
int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attrs);
boolean supports(ConfigAttribute attribute);
boolean supports(Class clazz);
具体的な実装は int
を返し、可能な値は ACCESS_ABSTAIN
、ACCESS_DENIED
、ACCESS_GRANTED
という名前の AccessDecisionVoter
静的フィールドに反映されます。投票の実装は、認可の決定について意見がない場合、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
と正確に等しい String
表現(getAuthority()
メソッドから)を返す GrantedAuthority
がある場合、アクセスを許可することに投票します。ROLE_
で始まる ConfigAttribute
の完全一致がない場合、RoleVoter
はアクセスを拒否するために投票します。ConfigAttribute
が ROLE_
で始まらない場合、投票者は棄権します。
AuthenticatedVoter
暗黙のうちに見たもう 1 つの投票者は、AuthenticatedVoter
です。これを使用して、匿名ユーザー、完全認証ユーザー、覚えているユーザーを区別できます。多くのサイトでは、remember-me 認証で特定の制限付きアクセスが許可されていますが、ユーザーはフルアクセスのためにログインして ID を確認する必要があります。
IS_AUTHENTICATED_ANONYMOUSLY
属性を使用して匿名アクセスを許可した場合、この属性は AuthenticatedVoter
によって処理されていました。詳細については、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
構成属性によって実現されます。