Spring 集成中的安全性

Spring Integration 安全性

安全性是任何现代企业(或云)应用程序的重要功能之一。 此外,对于分布式系统(例如基于企业集成模式构建的系统)而言,安全性至关重要。 消息独立性和松耦合允许目标系统通过消息的 payload 以任意类型的数据相互通信。 我们可以选择信任所有此类消息,也可以保护我们的服务免受“感染性”消息的攻击。spring-doc.cadn.net.cn

Spring Integration 与 Spring Security 一起,提供了一种简单且全面的方式来保护消息通道以及集成解决方案的其他部分。 从 6.0 版本开始,ChannelSecurityInterceptor 及其通过 @SecuredChannel 注解和 XML <secured-channels> 的配置已被弃用,转而使用来自 spring-security-messaging 模块的 AuthorizationChannelInterceptor。 相应的 AuthorizationManager 基础设施完全覆盖了之前支持的基于角色的身份验证,并允许配置任何其他可能的授权策略。spring-doc.cadn.net.cn

Spring Integration 唯一剩下的特性是一个 SecurityContextPropagationChannelInterceptor,未来也可能被提升到提到的 spring-security-messaging 模块中。spring-doc.cadn.net.cn

您需要将以下依赖项包含到您的项目中:spring-doc.cadn.net.cn

Maven
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-security</artifactId>
    <version>6.1.9</version>
</dependency>
Gradle
compile "org.springframework.integration:spring-integration-security:6.1.9"

安全通道

为了在集成流程中保护消息通道,必须向这些通道添加一个AuthorizationChannelInterceptor,或者将其配置为具有相应模式的全局通道拦截器:spring-doc.cadn.net.cn

Java
@Bean
@GlobalChannelInterceptor(patterns = "secured*")
AuthorizationChannelInterceptor authorizationChannelInterceptor() {
    return new AuthorizationChannelInterceptor(AuthorityAuthorizationManager.hasAnyRole("ADMIN", "PRESIDENT"));
}
XML
<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-doc.cadn.net.cn

安全上下文传播

为确保我们与应用程序的交互符合其安全系统规则且安全可靠,我们需要提供包含认证(主体)对象的安全上下文。Spring Security 项目提供了一种灵活且标准的机制,用于通过 HTTP、WebSocket 或 SOAP 协议(也可通过简单的 Spring Security 扩展支持任何其他集成协议)对我们的应用程序客户端进行身份验证。它还为应用程序对象(如消息通道)提供进一步的授权检查,并返回 SecurityContext。默认情况下,SecurityContext 通过 (ThreadLocalSecurityContextHolderStrategy) 与当前 Thread 的执行状态绑定。它通过一个在安全方法上应用的 AOP(面向切面编程)拦截器来访问,用于检查(例如)该调用的 principal 是否具有调用该方法的足够权限。这与当前线程配合良好。不过,处理逻辑可以在另一个线程上执行,也可以在多个线程上执行,甚至可以在外部系统上执行。spring-doc.cadn.net.cn

如果我们的应用程序是建立在 Spring Integration 组件及其消息通道之上,那么配置标准线程绑定行为非常容易。 在这种情况下,受保护的对象可以是任何服务激活器或转换器,它们在其 MethodSecurityInterceptor 中使用 <request-handler-advice-chain>(参见 为端点添加行为)进行保护,甚至可以使用 MessageChannel(参见 保护通道,前文)。 在使用 DirectChannel 通信时,SecurityContext 会自动可用,因为下游流程运行在当前线程上。 然而,在 QueueChannelExecutorChannelPublishSubscribeChannel 且带有 Executor 的情况下,消息会通过这些通道的特性从一个线程(或多个线程)转移到另一个线程。 为了支持此类场景,我们有两个选择:spring-doc.cadn.net.cn

  • 在消息头中传输 Authentication 对象,并在另一侧提取和验证该对象后,再进行安全对象访问。spring-doc.cadn.net.cn

  • SecurityContext 传播到接收已传输消息的线程。spring-doc.cadn.net.cn

版本 4.2 引入了 SecurityContext 传播机制。 它被实现为一个 SecurityContextPropagationChannelInterceptor,您可以将其添加到任何 MessageChannel 中,或配置为 @GlobalChannelInterceptor。 该拦截器的逻辑基于从当前线程(来自 preSend() 方法)提取的 SecurityContext,并将其填充到另一个来自 postReceive() (beforeHandle()) 方法的线程中。 实际上,该拦截器是更通用的 ThreadStatePropagationChannelInterceptor 的扩展,它在内部 Message<?> 扩展(MessageWithThreadState<S>)的一侧将待发送的消息与要传播的状态进行包装,并在另一侧提取原始消息和要传播的状态。 您可以为任何上下文传播用例扩展 ThreadStatePropagationChannelInterceptor,而 SecurityContextPropagationChannelInterceptor 就是这样一个很好的示例。spring-doc.cadn.net.cn

ThreadStatePropagationChannelInterceptor 的逻辑基于消息修改(它返回一个内部 MessageWithThreadState 对象用于发送)。 因此,当将此拦截器与任何其他可能修改消息的拦截器(例如通过 MessageBuilder.withPayload(…​)…​build())结合使用时,您应格外小心。 传播的状态可能会丢失。 在大多数情况下,为了解决该问题,您可以对通道的拦截器进行排序,并确保 ThreadStatePropagationChannelInterceptor 是堆栈中的最后一个。

传播和填充 SecurityContext 只是工作的一半。 由于消息不拥有消息流中的线程,并且我们必须确保对任何传入消息都是安全的,因此我们需要从 ThreadLocal 中清理 SecurityContextSecurityContextPropagationChannelInterceptor 提供了 afterMessageHandled() 拦截器方法的实现。 它通过在调用结束时释放该传播主体(principal)的线程来完成清理操作。 这意味着,当处理移交的消息的线程完成消息处理(无论成功与否)时,上下文将被清除,以防止在处理其他消息时被意外使用。spring-doc.cadn.net.cn

在使用 异步网关 时,您应当使用 Spring Security 并发支持 中合适的 AbstractDelegatingSecurityContextSupport 实现,以确保在网关调用过程中安全上下文传播得到保证。 以下示例展示了如何实现:spring-doc.cadn.net.cn

@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);

}