# Spring 集成中的安全性
# Spring 集成中的安全性
安全性是任何现代 Enterprise(或云)应用程序中的重要功能之一。此外,它对于分布式系统至关重要,例如那些建立在 Enterprise 集成模式上的系统。消息传递独立性和松耦合使目标系统可以使用消息payload
中的任何类型的数据彼此通信。我们既可以信任所有这些消息,也可以保护我们的服务不受“感染”消息的影响。
Spring 集成,连同Spring Security (opens new window),提供了一种简单而全面的方式来保护消息通道,以及集成解决方案的其他部分。
你需要在项目中包含此依赖项:
Maven
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-security</artifactId>
<version>5.5.9</version>
</dependency>
Gradle
compile "org.springframework.integration:spring-integration-security:5.5.9"
# 保护信道
Spring 集成提供了ChannelSecurityInterceptor
拦截器,它扩展了AbstractSecurityInterceptor
并拦截了信道上的发送和接收调用。然后参照ChannelSecurityMetadataSource
做出访问决策,该决策提供了描述特定通道的发送和接收访问策略的元数据。拦截器要求通过使用 Spring 安全性进行身份验证来建立有效的SecurityContext
。有关详细信息,请参见Spring Security Reference Guide (opens new window)。
Spring 集成提供了名称空间支持,以允许安全约束的轻松配置。这种支持由安全通道标记组成,它允许结合用于发送和接收的安全配置的定义来定义一个或多个通道名称模式。模式是java.util.regexp.Pattern
。
下面的示例展示了如何配置包含安全性的 Bean,以及如何使用模式设置策略:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-security="http://www.springframework.org/schema/integration/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/integration
https://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/security
https://www.springframework.org/schema/integration/security/spring-integration-security.xsd">
<int-security:secured-channels>
<int-security:access-policy pattern="admin.*" send-access="ROLE_ADMIN"/>
<int-security:access-policy pattern="user.*" receive-access="ROLE_USER"/>
</int-security:secured-channels>
默认情况下,secured-channels
名称空间元素需要一个名为authenticationManager
的 Bean(它实现AuthenticationManager
)和一个名为accessDecisionManager
的 Bean(它实现AccessDecisionManager
)。如果不是这种情况,则可以将对适当 bean 的引用配置为secured-channels
元素的属性,如下例所示:
<int-security:secured-channels access-decision-manager="customAccessDecisionManager"
authentication-manager="customAuthenticationManager">
<int-security:access-policy pattern="admin.*" send-access="ROLE_ADMIN"/>
<int-security:access-policy pattern="user.*" receive-access="ROLE_USER"/>
</int-security:secured-channels>
从版本 4.2 开始,@SecuredChannel
注释可用于@Configuration
类中的 Java 配置。
下面的示例展示了与前面的 XML 示例类似的 Java:
@Configuration
@EnableIntegration
public class ContextConfiguration {
@Bean
@SecuredChannel(interceptor = "channelSecurityInterceptor", sendAccess = "ROLE_ADMIN")
public SubscribableChannel adminChannel() {
return new DirectChannel();
}
@Bean
@SecuredChannel(interceptor = "channelSecurityInterceptor", receiveAccess = "ROLE_USER")
public SubscribableChannel userChannel() {
return new DirectChannel();
}
@Bean
public ChannelSecurityInterceptor channelSecurityInterceptor(
AuthenticationManager authenticationManager,
AccessDecisionManager accessDecisionManager) {
ChannelSecurityInterceptor channelSecurityInterceptor = new ChannelSecurityInterceptor();
channelSecurityInterceptor.setAuthenticationManager(authenticationManager);
channelSecurityInterceptor.setAccessDecisionManager(accessDecisionManager);
return channelSecurityInterceptor;
}
}
# 安全上下文传播
为了确保我们与应用程序的交互是安全的,根据其安全系统规则,我们应该通过身份验证(主体)对象提供一些安全上下文。 Spring 安全项目提供了一种灵活的、规范的机制,用于通过 HTTP、 WebSocket 或 SOAP 协议对我们的应用程序客户端进行身份验证(对于具有简单的 Spring 安全扩展的任何其他集成协议,都可以这样做)。它还提供了SecurityContext
用于对应用程序对象(例如消息通道)进行进一步的授权检查。默认情况下,SecurityContext
通过使用(ThreadLocalSecurityContextHolderStrategy
)绑定到当前Thread
的执行状态。安全方法上的 AOP(面向方面的编程)拦截器访问它,以检查(例如)调用的principal
是否具有调用该方法的足够权限。这在当前线程中很好地工作。不过,处理逻辑通常可以在另一个线程、多个线程上执行,甚至可以在外部系统上执行。
如果我们的应用程序构建在 Spring 集成组件及其消息通道上,那么标准的线程绑定行为很容易配置。在这种情况下,安全对象可以是任何服务激活器或转换器,在其<request-handler-advice-chain>
(参见向端点添加行为)甚至MessageChannel
(参见保护通道,更早)中使用MethodSecurityInterceptor
进行安全保护。当使用DirectChannel
通信时,SecurityContext
将自动可用,因为下游流在当前线程上运行。然而,在QueueChannel
、ExecutorChannel
和PublishSubscribeChannel
具有Executor
的情况下,消息通过这些通道的性质从一个线程传输到另一个(或多个)。为了支持这种情况,我们有两个选择:
在消息头中传输
Authentication
对象,并在安全的对象访问之前在另一端对其进行提取和验证。将
SecurityContext
传播到接收传输消息的线程。
4.2 版引入了SecurityContext
传播。它被实现为SecurityContextPropagationChannelInterceptor
,你可以将其添加到任何MessageChannel
或配置为@GlobalChannelInterceptor
。该拦截器的逻辑基于从当前线程(从preSend()
方法)提取SecurityContext
并从postReceive()
(beforeHandle()
)方法将其填充到另一个线程。实际上,这个拦截器是更通用的ThreadStatePropagationChannelInterceptor
的扩展,它将要发送的消息与要传播的状态包装在一个内部的Message<?>
扩展(MessageWithThreadState<S>
)中,并在另一侧提取要传播的原始消息和状态。你可以为任何上下文传播用例扩展ThreadStatePropagationChannelInterceptor
,SecurityContextPropagationChannelInterceptor
就是这样做的一个很好的例子。
ThreadStatePropagationChannelInterceptor 的逻辑是基于消息修改的(它返回一个要发送的内部MessageWithThreadState 对象)。因此,在将此拦截器与任何其他也可以修改消息的拦截器组合时(例如,通过 MessageBuilder.withPayload(…)…build() ).传播的状态可能会丢失。 在大多数情况下,为了克服这个问题,可以为信道订购拦截器,并确保 ThreadStatePropagationChannelInterceptor 是堆栈中的最后一个。 |
---|
SecurityContext
的传播和种群只是工作的一半。由于消息不是消息流中线程的所有者,并且我们应该确保对任何传入消息都是安全的,因此我们必须清理SecurityContext
中的ThreadLocal
。SecurityContextPropagationChannelInterceptor
提供了afterMessageHandled()
拦截器方法实现。它通过将调用结束时的线程从传播的主体中释放出来来清理操作。这意味着,当处理传递消息的线程完成处理消息(成功或其他)时,上下文将被清除,以便在处理另一条消息时不会在无意中使用它。
在使用异步网关时,应该使用 Spring Security并发支持 (opens new window)中的适当的AbstractDelegatingSecurityContextSupport 实现,以确保通过网关调用进行安全上下文传播。下面的示例演示如何这样做: <br/>@Configuration<br/>@EnableIntegration<br/>@IntegrationComponentScan<br/>public class ContextConfiguration {<br/><br/> @Bean<br/> public AsyncTaskExecutor securityContextExecutor() {<br/> return new DelegatingSecurityContextAsyncTaskExecutor(<br/> new SimpleAsyncTaskExecutor());<br/> }<br/><br/>}<br/><br/>...<br/><br/>@MessagingGateway(asyncExecutor = "securityContextExecutor")<br/>public interface SecuredGateway {<br/><br/> @Gateway(requestChannel = "queueChannel")<br/> Future<String> send(String payload);<br/><br/>}<br/> |
---|