Spring Integration 中的安全性
安全是任何现代企业(或云)应用程序中的一个重要功能。此外,对于分布式系统,例如基于企业集成模式构建的系统,安全至关重要。消息独立性和松耦合让目标系统能够相互传递消息,并且消息的 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
) 与当前线程的执行状态绑定。它通过 AOP(面向切面编程)拦截器在安全方法上被访问,以检查(例如)调用的 principal
是否有足够的权限来调用该方法。这在线程内工作得很好。然而,处理逻辑通常可以在另一个线程上、多个线程上甚至是在外部系统上执行。
标准的线程绑定行为在我们的应用程序基于 Spring Integration 组件及其消息通道构建时很容易配置。在这种情况下,受保护的对象可以是任何服务激活器或转换器,在它们的 <request-handler-advice-chain>
中使用 MethodSecurityInterceptor
进行保护(参见向端点添加行为)或者甚至是 MessageChannel
(参见前面的保护通道)。当使用 DirectChannel
通信时,SecurityContext
会自动可用,因为下游流运行在当前线程上。然而,在 QueueChannel
、ExecutorChannel
和带有 Executor
的 PublishSubscribeChannel
的情况下,消息由这些通道的性质决定从一个线程转移到另一个(或多个)线程。为了支持这些场景,我们有两种选择:
-
在消息头中传输
Authentication
对象,并在另一端提取并验证它,然后再进行安全的对象访问。 -
将
SecurityContext
传播到接收已传输消息的线程。
这在 spring-security-messaging
模块中实现为 org.springframework.security.messaging.context.SecurityContextPropagationChannelInterceptor
,可以添加到任何 MessageChannel
或配置为 @GlobalChannelInterceptor
。此拦截器的逻辑基于从当前线程(从 preSend()
方法)提取 SecurityContext
并在 postReceive()
(beforeHandle()
) 方法中填充到另一个线程。有关更多信息,请参阅 SecurityContextPropagationChannelInterceptor
的 Javadoc。
SecurityContext
的传播和填充只是工作的一半。由于消息不是消息流中线程的所有者,而且系统必须确保其对任何传入消息的安全性,因此需要从 ThreadLocal
中清理 SecurityContext
。SecurityContextPropagationChannelInterceptor
提供了 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);
}