安全性是任何现代企业(或云)应用程序中的重要功能之一。 此外,它对于分布式系统至关重要,例如基于企业集成模式构建的系统。 消息传递独立性和松散耦合使目标系统能够与消息中的任何类型的数据相互通信。 我们可以信任所有这些消息,也可以保护我们的服务免受“感染”消息的影响。payload
Spring 集成与 Spring Security 一起,提供了一种简单而全面的方法来保护消息通道以及集成解决方案的其他部分。 从版本 6.0 开始,通过注释和 XML 进行的配置及其配置已被弃用,转而从模块中使用。 相应的基础结构完全涵盖了以前支持的基于角色的身份验证,并且允许配置任何其他可能的授权策略。ChannelSecurityInterceptor@SecuredChannel<secured-channels>AuthorizationChannelInterceptorspring-security-messagingAuthorizationManager
唯一剩下的 Spring 集成功能是将来也可能升级到上述模块的功能。SecurityContextPropagationChannelInterceptorspring-security-messaging
您需要将此依赖项包含在项目中:
<dependency> <groupId>org.springframework.integration</groupId> <artifactId>spring-integration-security</artifactId> <version>6.0.0</version></dependency>保护通道
要保护集成流中的消息通道,必须将 添加到这些通道中,或者可以将其配置为具有相应模式的全局通道拦截器:AuthorizationChannelInterceptor
@Bean@GlobalChannelInterceptor(patterns = "secured*")AuthorizationChannelInterceptor authorizationChannelInterceptor() { return new AuthorizationChannelInterceptor(AuthorityAuthorizationManager.hasAnyRole("ADMIN", "PRESIDENT"));}有关详细信息,请参阅全局通道拦截器配置。
安全上下文传播
为了确保我们与应用程序的交互是安全的,根据其安全系统规则,我们应该为一些安全上下文提供身份验证(主体)对象。 Spring 安全性项目提供了一种灵活的规范机制,通过 HTTP、WebSocket 或 SOAP 协议对我们的应用程序客户端进行身份验证(就像任何其他具有简单 Spring Security 扩展的集成协议一样)。 它还提供了对应用程序对象(如消息通道)的进一步授权检查。 默认情况下,通过使用 () 与当前事件的执行状态相关联。 它由安全方法上的 AOP(面向方面编程)拦截器访问,以检查(例如)调用的拦截器是否有足够的权限来调用该方法。 这适用于当前线程。 但是,处理逻辑通常可以在另一个线程、多个线程甚至外部系统上执行。SecurityContextSecurityContextThreadThreadLocalSecurityContextHolderStrategyprincipal
如果我们的应用程序基于 Spring 集成组件及其消息通道构建,则很容易配置标准线程绑定行为。 在这种情况下,受保护的对象可以是任何服务激活器或转换器,在其(请参阅向终结点添加行为)甚至(请参阅前面的保护通道)中使用 进行保护。 使用通信时,会自动可用,因为下游流在当前线程上运行。 但是,在 、 和带有 的情况下,消息会根据这些通道的性质从一个线程传输到另一个(或多个)线程。 为了支持这样的场景,我们有两个选择:MethodSecurityInterceptor<request-handler-advice-chain>MessageChannelDirectChannelSecurityContextQueueChannelExecutorChannelPublishSubscribeChannelExecutor
- 在消息头中传输对象,并在安全对象访问之前在另一端提取并对其进行身份验证。Authentication
- 将 传播到接收传输消息的线程。SecurityContext
版本 4.2 引入了传播。 它以 的形式实现,您可以将其添加到任何 或配置为 . 此拦截器的逻辑基于从当前线程(从方法)提取并从 () 方法填充到另一个线程。 实际上,这个拦截器是更通用的扩展,它将要发送的消息与要传播的状态包装在一端的内部扩展()中,并在另一端提取原始消息和要传播的状态。 您可以针对任何上下文传播用例进行扩展,这是一个很好的示例。SecurityContextSecurityContextPropagationChannelInterceptorMessageChannel@GlobalChannelInterceptorSecurityContextpreSend()postReceive()beforeHandle()ThreadStatePropagationChannelInterceptorMessage<?>MessageWithThreadState<S>ThreadStatePropagationChannelInterceptorSecurityContextPropagationChannelInterceptor
的逻辑基于消息修改(它返回要发送的内部对象)。 因此,将此侦听器与任何其他也可以修改消息的侦听器(例如,通过 )组合时应小心。 要传播的状态可能会丢失。 在大多数情况下,为了克服这个问题,您可以订购通道的拦截器,并确保拦截器是堆栈中的最后一个拦截器。ThreadStatePropagationChannelInterceptorMessageWithThreadStateMessageBuilder.withPayload(…)…build()ThreadStatePropagationChannelInterceptor
繁殖和人口只是工作的一半。 由于消息不是消息流中线程的所有者,并且我们应该确保我们对任何传入消息都是安全的,因此我们必须清理 from . 提供侦听器方法实现。 它通过在调用结束时从传播的主体中释放线程来清理操作。 这意味着,当处理移交消息的线程完成对消息的处理(成功或其他)时,将清除上下文,以便在处理另一条消息时不会无意中使用它。SecurityContextSecurityContextThreadLocalSecurityContextPropagationChannelInterceptorafterMessageHandled()
使用异步网关时,应使用 Spring 安全并发支持的适当实现,以确保安全上下文在网关调用时传播。 以下示例演示如何执行此操作:AbstractDelegatingSecurityContextSupport
@Configuration@EnableIntegration@IntegrationComponentScanpublic 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);}