最新の安定バージョンについては、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使用に変更することをお勧めします。

AuthorizationManagerAuthorizationFilter によって呼び出され、最終的なアクセス制御の決定を行う責任があります。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 の実装は、関連するクラスを示しています。

authorizationhierarchy
図 1: 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 を呼び出すには、次のようにします。

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 のみを呼び出すには、次のようにします。

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 実装が含まれています。投票決定マネージャーは、関連するクラスを示しています。

access decision voting
図 2: 投票決定マネージャー

このアプローチを使用して、一連の 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_ABSTAINACCESS_DENIEDACCESS_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 サイトには、投票者を使用して、アカウントが停止されたユーザーへのアクセスをリアルタイムで拒否する方法を説明したブログ記事 (英語) があります。

after invocation
図 3: 呼び出し実装後

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 構成属性によって実現されます。