Spring 集成中的安全

安全性是任何现代企业(或云)应用程序的重要功能之一。此外,对于分布式系统(例如基于企业集成模式构建的系统)而言,安全性至关重要。消息独立性和松耦合允许目标系统使用消息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的执行状态。它被安全方法上的AOP(面向方面编程)拦截器访问,以检查(例如)调用方的principal是否具有调用该方法的足够权限。这在当前线程中运行良好。但是,处理逻辑通常可以在另一个线程、多个线程甚至外部系统上执行。

如果我们的应用程序构建在Spring Integration组件及其消息通道之上,则标准线程绑定行为易于配置。在这种情况下,安全对象可以是任何服务激活器或转换器,在其<request-handler-advice-chain>中使用MethodSecurityInterceptor进行保护(请参阅向端点添加行为),甚至MessageChannel(请参阅前面部分的保护通道)。当使用DirectChannel通信时,SecurityContext会自动可用,因为下游流在当前线程上运行。但是,在QueueChannelExecutorChannel和具有ExecutorPublishSubscribeChannel的情况下,消息通过这些通道的本质从一个线程传输到另一个线程(或多个线程)。为了支持此类场景,我们有两个选择

  • 在消息头中传输Authentication对象,并在另一端提取并对其进行身份验证,然后再访问安全对象。

  • SecurityContext传播到接收传输消息的线程。

这在spring-security-messaging模块中实现为org.springframework.security.messaging.context.SecurityContextPropagationChannelInterceptor,可以将其添加到任何MessageChannel中或配置为@GlobalChannelInterceptor。此拦截器的逻辑基于从当前线程(来自preSend()方法)提取SecurityContext并从postReceive()beforeHandle())方法将其填充到另一个线程。有关更多信息,请参阅SecurityContextPropagationChannelInterceptor的Javadoc。

SecurityContext的传播和填充只是工作的一半。由于消息不是消息流中线程的所有者,并且系统应确保其免受任何传入消息的攻击,因此必须从ThreadLocal中清理SecurityContextSecurityContextPropagationChannelInterceptor提供了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);

}