Security in Spring Integration

安全性是任何现代企业(或云)应用程序中的重要功能之一。此外,它对于 Enterprise Integration Patterns 中构建的分布式系统(如那些基于)也很重要。通过消息独立性和松散耦合,目标系统可以使用消息“有效负载”中的任何类型数据相互通信。我们可以信任所有这些消息,或者可以保护我们的服务以防止“感染”消息。

6.3 版本开始,整个 spring-integration-security 模块已删除,取而代之的是一种由更常见的 spring-security-messaging 库提出的 API。

Securing channels

要在集成流中保护消息通道,必须在这些通道中添加 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>

有关更多信息,请参见 Global Channel Interceptor Configuration

Security Context Propagation

为了确保我们与应用程序的交互按照其安全系统规则是安全的,我们应提供一些带有身份验证(主体)对象的安全性上下文。Spring Security 项目提供了一个灵活的规范机制,可以在 HTTP、WebSocket 或 SOAP 协议上对我们的应用程序客户端进行身份验证(使用简单的 Spring Security 扩展,可以对其他任何与集成协议进行身份验证)。它还提供一个 SecurityContext,以对应用程序对象(如消息通道)进行进一步的授权检查。默认情况下,SecurityContext 通过使用 (ThreadLocalSecurityContextHolderStrategy) 绑定到当前 Thread 的执行状态。它可通过面向切面的编程 (AOP) 拦截器访问安全方法,以检查(例如)调用者的 principal 是否有足够的权限来调用该方法。这适用于当前线程。不过,通常,处理逻辑可以在另一个线程、多个线程甚至外部系统上执行。

如果我们的应用程序是基于 Spring Integration 组件及其消息通道构建的,那么标准线程绑定行为很容易配置。在这种情况下,受保护的对象可以是任何服务激活器或转换器,它们用一个 MethodSecurityInterceptor 保护,在它们的 <request-handler-advice-chain> 中(参见 Adding Behavior to Endpoints)甚至 MessageChannel 中(参见 Securing channels,前面)。在使用 DirectChannel 通信时, SecurityContext 会自动可用,因为下游流在当前线程中运行。然而,在 QueueChannelExecutorChannelPublishSubscribeChannel 的情况下,其 Executor 会按这些通道的性质将消息从一个线程传输到另一个(或几个)线程。为了支持这样的场景,我们有两个选择:

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

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

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

SecurityContext 的传播和填充只是这项工作的一半。由于消息不是消息流中线程的所有者,并且系统应确保它已针对任何传入的消息受到保护,因此必须从 ThreadLocal 清除 SecurityContextSecurityContextPropagationChannelInterceptor 提供 afterMessageHandled() 拦截器方法实现。它通过在该传播主体调用时释放线程来清除操作。这意味着,当处理移交消息的线程完成处理消息(不管成功与否)后,将清除上下文,以便在处理另一条消息时不会无意中使用它。

当使用 asynchronous gateway 时,你应该使用 Spring Security Concurrency Support 中的一个合适的 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);

}