Spring 集成中的安全性

Spring Integration 中的安全性

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

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

唯一剩下的 Spring Integration 功能是SecurityContextPropagationChannelInterceptor可以提升为上述spring-security-messaging模块。spring-doc.cadn.net.cn

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

专家
<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 Security 项目提供了一种灵活的规范机制,用于通过 HTTP、WebSocket 或 SOAP 协议对我们的应用程序客户端进行身份验证(就像对于具有简单 Spring Security 扩展的任何其他集成协议一样)。它还提供了一个SecurityContext用于对应用程序对象(例如消息通道)进行进一步的授权检查。默认情况下,SecurityContext与当前Thread通过使用 (ThreadLocalSecurityContextHolderStrategy). 它由安全方法上的 AOP(面向方面编程)拦截器访问,以检查(例如)是否principal的调用具有足够的权限来调用该方法。这适用于当前线程。但是,通常情况下,处理逻辑可以在另一个线程、多个线程甚至外部系统上执行。spring-doc.cadn.net.cn

如果我们的应用程序是基于 Spring Integration 组件及其消息通道构建的,则标准线程绑定行为很容易配置。在这种情况下,受保护的对象可以是任何服务激活器或转换器,并使用MethodSecurityInterceptor在他们的<request-handler-advice-chain>(请参阅向端点添加行为)甚至MessageChannel(请参阅前面的保护通道)。使用DirectChannel通信,则SecurityContext自动可用,因为下游流在当前线程上运行。但是,在QueueChannel,ExecutorChannelPublishSubscribeChannel使用Executor,消息根据这些通道的性质从一个线程传输到另一个(或多个)线程。为了支持此类方案,我们有两种选择:spring-doc.cadn.net.cn

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

的逻辑ThreadStatePropagationChannelInterceptor基于消息修改(它返回内部MessageWithThreadStateobject 发送)。 因此,在将此拦截器与任何其他也可以修改消息的拦截器组合在一起时,您应该小心(例如,通过MessageBuilder.withPayload(…​)…​build()). 要传播的状态可能会丢失。 在大多数情况下,要克服此问题,您可以为信道订购拦截器并确保ThreadStatePropagationChannelInterceptor是堆栈中的最后一个。

繁殖和种群SecurityContext只是工作的一半。 由于消息不是消息流中线程的所有者,并且我们应该确保我们对任何传入消息都是安全的,因此我们必须清理SecurityContextThreadLocal. 这SecurityContextPropagationChannelInterceptor提供afterMessageHandled()拦截器方法实现。 它通过在调用结束时从传播的主体中释放线程来清理作。 这意味着,当处理已传递消息的线程完成对消息的处理(成功或其他消息)时,将清除上下文,以便在处理另一条消息时不会无意中使用它。spring-doc.cadn.net.cn

使用异步网关时,应使用适当的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);

}