Spring 集成中的安全性

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

Spring Integration 与Spring Security一起提供了一种简单而全面的方法来保护消息通道以及集成解决方案的其他部分。

您需要将此依赖项包含到您的项目中:

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

保护渠道

Spring Integration 提供了ChannelSecurityInterceptor拦截器,它扩展AbstractSecurityInterceptor和拦截了通道上的发送和接收调用。然后参考 a 做出访问决策ChannelSecurityMetadataSource,它提供了描述某些通道的发送和接收访问策略的元数据。拦截器要求SecurityContext已通过 Spring Security 进行身份验证建立有效。有关详细信息,请参阅Spring Security 参考指南

Spring Integration 提供命名空间支持以允许轻松配置安全约束。这种支持由安全通道标签组成,它允许定义一个或多个通道名称模式以及定义发送和接收的安全配置。模式是一个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(实现AuthenticationManager)的 bean 和一个名为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 Security 项目提供了一种灵活、规范的机制来通过 HTTP、WebSocket 或 SOAP 协议对我们的应用程序客户端进行身份验证(就像任何其他具有简单 Spring Security 扩展的集成协议一样)。它还提供了SecurityContext对应用程序对象(例如消息通道)的进一步授权检查。默认情况下,使用 ( )SecurityContext与当前的执行状态相关联。它由安全方法上的 AOP(面向方面​​编程)拦截器访问,以检查(例如)是否ThreadThreadLocalSecurityContextHolderStrategyprincipal调用具有足够的权限来调用该方法。这适用于当前线程。但是,处理逻辑通常可以在另一个线程、多个线程甚至外部系统上执行。

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

  • 在消息头中传输一个Authentication对象,并在安全对象访问之前在另一端提取和验证它。

  • 传播SecurityContext到接收传输消息的线程。

4.2 版引入了SecurityContext传播。它被实现为SecurityContextPropagationChannelInterceptor,您可以将其添加到任何MessageChannel或配置为@GlobalChannelInterceptor。此拦截器的逻辑基于SecurityContext从当前线程(从方法)中提取,并将其从( ) 方法preSend()填充到另一个线程。实际上,这个拦截器是更通用的扩展,它在一侧将要发送的消息和要传播的状态包装在一个内部扩展 ( ) 中,并在另一侧提取原始消息和要传播的状态。您可以扩展任何上下文传播用例,这是一个很好的例子。postReceive()beforeHandle()ThreadStatePropagationChannelInterceptorMessage<?>MessageWithThreadState<S>ThreadStatePropagationChannelInterceptorSecurityContextPropagationChannelInterceptor

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

传播和人口SecurityContext只是工作的一半。由于消息不是消息流中线程的所有者,我们应该确保我们对任何传入消息都是安全的,因此我们必须清理SecurityContextfrom ThreadLocal。提供拦截器方法实现SecurityContextPropagationChannelInterceptorafterMessageHandled()它通过在调用结束时从传播的主体中释放线程来清理操作。这意味着,当处理传递消息的线程完成对消息的处理(成功与否)时,上下文将被清除,以便在处理另一条消息时不会无意中使用它。

使用异步网关AbstractDelegatingSecurityContextSupport时,您应该使用Spring Security Concurrency Support中的适当实现,以确保通过网关调用确保安全上下文传播。以下示例显示了如何执行此操作:

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

}

1. see XML Configuration