このバージョンはまだ開発中であり、まだ安定しているとは見なされていません。最新の安定バージョンについては、Spring Integration 6.4.3 を使用してください!

Spring Integration のセキュリティ

セキュリティは、現代のエンタープライズ(またはクラウド)アプリケーションの重要な機能の 1 つです。さらに、エンタープライズ統合パターンに基づいて構築されたシステムなどの分散システムにとっても重要です。メッセージングの独立性と疎結合により、ターゲットシステムは、メッセージの payload の任意の型のデータと相互に通信できます。これらすべてのメッセージを信頼するか、「感染」メッセージからサービスを保護することができます。

バージョン 6.3 以降、spring-integration-security モジュール全体が削除され、より一般的な spring-security-messaging ライブラリによって提案された API が採用されています。

チャネルの保護

統合フローでメッセージチャネルを保護するには、AuthorizationChannelInterceptor をそれらのチャネルに追加する必要があります。または、それぞれのパターンでグローバルチャネルインターセプターとして構成することもできます。

  • Java

  • XML

@Bean
@GlobalChannelInterceptor(patterns = "secured*")
AuthorizationChannelInterceptor authorizationChannelInterceptor() {
    return new AuthorizationChannelInterceptor(AuthorityAuthorizationManager.hasAnyRole("ADMIN", "PRESIDENT"));
}
<channel-interceptor pattern="securedChannel*">
    <beans:bean class="org.springframework.security.messaging.access.intercept.AuthorizationChannelInterceptor">
        <beans:constructor-arg>
            <beans:bean class="org.springframework.security.authorization.AuthorityAuthorizationManager"
                        factory-method="hasAnyRole">
                <beans:constructor-arg>
                    <beans:array>
                        <beans:value>ADMIN</beans:value>
                        <beans:value>PRESIDENT</beans:value>
                    </beans:array>
                </beans:constructor-arg>
            </beans:bean>
        </beans:constructor-arg>
    </beans:bean>
</channel-interceptor>

詳細については、グローバルチャネルインターセプターの構成を参照してください。

セキュリティコンテキストの伝播

アプリケーションとの対話がセキュリティシステムルールに従って安全であることを確認するには、セキュリティコンテキストに認証(プリンシパル)オブジェクトを提供する必要があります。Spring Security プロジェクトは、HTTP、WebSocket、SOAP プロトコルを介してアプリケーションクライアントを認証するための柔軟で標準的なメカニズムを提供します(単純な Spring Security 拡張を備えた他の統合プロトコルで実行できます)。また、メッセージチャネルなどのアプリケーションオブジェクトの認証チェックのために SecurityContext も提供します。デフォルトでは、SecurityContext は(ThreadLocalSecurityContextHolderStrategy)を使用して現在の Thread の実行状態に関連付けられています。これは、protected メソッドの AOP(アスペクト指向プログラミング)インターセプターによってアクセスされ、呼び出しの principal がそのメソッドを呼び出すための十分な認可を持っているかどうかをチェックします。これは現在のスレッドでうまく機能します。ただし、多くの場合、処理ロジックは別のスレッド、複数のスレッド、さらには外部システムで実行できます。

アプリケーションが Spring Integration コンポーネントとそのメッセージチャネル上に構築されている場合、標準のスレッドバインド動作は簡単に構成できます。この場合、保護されたオブジェクトは、<request-handler-advice-chain> (エンドポイントへの動作の追加を参照)または MessageChannel (前述のチャネルの保護を参照)で MethodSecurityInterceptor で保護された任意のサービスアクティベーターまたはトランスフォーマーにすることができます。DirectChannel 通信を使用する場合、ダウンストリームフローは現在のスレッドで実行されるため、SecurityContext は自動的に使用可能になります。ただし、Executor を備えた QueueChannelExecutorChannelPublishSubscribeChannel の場合、メッセージは、それらのチャネルの性質により、あるスレッドから別のスレッド(または複数)に転送されます。このようなシナリオをサポートするために、2 つの選択肢があります。

  • セキュアなオブジェクトアクセスの前に、メッセージヘッダー内で Authentication オブジェクトを転送し、反対側でそれを抽出して認証します。

  • SecurityContext を、転送されたメッセージを受信するスレッドに伝播します。

これは spring-security-messaging モジュールの org.springframework.security.messaging.context.SecurityContextPropagationChannelInterceptor として実装されており、任意の MessageChannel に追加したり、@GlobalChannelInterceptor として構成したりできます。このインターセプターのロジックは、現在のスレッドから (preSend() メソッドから) SecurityContext を抽出し、postReceive() (beforeHandle()) メソッドから別のスレッドに移入することに基づいています。詳細については、SecurityContextPropagationChannelInterceptor Javadoc を参照してください。

SecurityContext の増殖と増殖は作業の半分にすぎません。メッセージはメッセージフロー内のスレッドの所有者ではなく、システムは受信メッセージに対してセキュリティが確保されていることを確認する必要があるため、SecurityContext を ThreadLocal からクリーンアップする必要があります。SecurityContextPropagationChannelInterceptor は、afterMessageHandled() インターセプターメソッドの実装を提供します。呼び出しの終了時に伝播されたプリンシパルからスレッドを解放することで、操作をクリーンアップします。これは、ハンドオフされたメッセージを処理するスレッドがメッセージの処理を終了すると (成功かどうかに関係なく)、コンテキストがクリアされるため、別のメッセージを処理するときにコンテキストが誤って使用されないようになります。

非同期ゲートウェイを使用する場合は、Spring Security 並行性サポートの適切な AbstractDelegatingSecurityContextSupport 実装を使用して、ゲートウェイ呼び出しを介したセキュリティコンテキストの伝播を保証する必要があります。次の例は、その方法を示しています。

@Configuration
@EnableIntegration
@IntegrationComponentScan
public class ContextConfiguration {

    @Bean
    public AsyncTaskExecutor securityContextExecutor() {
        return new DelegatingSecurityContextAsyncTaskExecutor(
                         new SimpleAsyncTaskExecutor());
    }

}

@MessagingGateway(asyncExecutor = "securityContextExecutor")
public interface SecuredGateway {

    @Gateway(requestChannel = "queueChannel")
    Future<String> send(String payload);

}