security.md 12.1 KB
Newer Older
茶陵後's avatar
茶陵後 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
# Security in Spring Integration

## [](#security)Security in Spring Integration

Security is one of the important functions in any modern enterprise (or cloud) application.
Moreover, it is critical for distributed systems, such as those built on Enterprise Integration Patterns.
Messaging independence and loose coupling let target systems communicate with each other with any type of data in the message’s `payload`.
We can either trust all those messages or secure our service against “infecting” messages.

Spring Integration, together with [Spring Security](https://projects.spring.io/spring-security/), provides a simple and comprehensive way to secure message channels, as well as other part of the integration solution.

You need to include this dependency into your project:

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"
```

### [](#securing-channels)Securing channels

Spring Integration provides the `ChannelSecurityInterceptor` interceptor, which extends `AbstractSecurityInterceptor` and intercepts send and receive calls on the channel.
Access decisions are then made with reference to a `ChannelSecurityMetadataSource`, which provides the metadata that describes the send and receive access policies for certain channels.
The interceptor requires that a valid `SecurityContext` has been established by authenticating with Spring Security.
See the [Spring Security Reference Guide](https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/) for details.

Spring Integration provides Namespace support to allow easy configuration of security constraints.
This support consists of the secured channels tag, which allows definition of one or more channel name patterns in conjunction with a definition of the security configuration for send and receive.
The pattern is a `java.util.regexp.Pattern`.

The following example shows how to configure a bean that includes security and how to set up policies with patterns:

```
<?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>
```

By default, the `secured-channels` namespace element expects a bean named `authenticationManager` (which implements `AuthenticationManager`) and a bean named `accessDecisionManager` (which implements `AccessDecisionManager`).
Where this is not the case, references to the appropriate beans can be configured as attributes of the `secured-channels` element, as the following example shows:

```
<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>
```

Starting with version 4.2, the `@SecuredChannel` annotation is available for Java configuration in `@Configuration` classes.

The following example shows the Java equivalent of the preceding XML examples:

```
@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;
    }

}
```

### [](#security-context-propagation)Security Context Propagation

To be sure that our interaction with the application is secure, according to its security system rules, we should supply some security context with an authentication (principal) object.
The Spring Security project provides a flexible, canonical mechanism to authenticate our application clients over HTTP, WebSocket, or SOAP protocols (as can be done for any other integration protocol with a simple Spring Security extension).
It also provides a `SecurityContext` for further authorization checks on the application objects, such as message channels.
By default, the `SecurityContext` is tied to the execution state of the current `Thread` by using the (`ThreadLocalSecurityContextHolderStrategy`).
It is accessed by an AOP (Aspect-oriented Programming) interceptor on secured methods to check (for example) whether that `principal` of the invocation has sufficient permissions to call that method.
This works well with the current thread.
Often, though, processing logic can be performed on another thread, on several threads, or even on external systems.

Standard thread-bound behavior is easy to configure if our application is built on the Spring Integration components
and its message channels.
In this case, the secured objects can be any service activator or transformer, secured with a`MethodSecurityInterceptor` in their `<request-handler-advice-chain>` (see [Adding Behavior to Endpoints](./handler-advice.html#message-handler-advice-chain)) or even `MessageChannel` (see [Securing channels](#securing-channels), earlier).
When using `DirectChannel` communication, the `SecurityContext` is automatically available, because the downstream flow runs on the current thread.
However, in the cases of the `QueueChannel`, `ExecutorChannel`, and `PublishSubscribeChannel` with an `Executor`, messages are transferred from one thread to another (or several) by the nature of those channels.
In order to support such scenarios, we have two choices:

* Transfer an `Authentication` object within the message headers and extract and authenticate it on the other side before secured object access.

* Propagate the `SecurityContext` to the thread that receives the transferred message.

Version 4.2 introduced `SecurityContext` propagation.
It is implemented as a `SecurityContextPropagationChannelInterceptor`, which you can add to any `MessageChannel` or configure as a `@GlobalChannelInterceptor`.
The logic of this interceptor is based on the `SecurityContext` extraction from the current thread (from the `preSend()` method) and its populating to another thread from the `postReceive()` (`beforeHandle()`) method.
Actually, this interceptor is an extension of the more generic `ThreadStatePropagationChannelInterceptor`, which wraps the message to send with the state to propagate in an internal `Message<?>` extension (`MessageWithThreadState<S>`) on one side and extracts the original message and the state to propagate on the other side.
You can extend the `ThreadStatePropagationChannelInterceptor` for any context propagation use case, and `SecurityContextPropagationChannelInterceptor` is a good example of doing so.

|   |The logic of the `ThreadStatePropagationChannelInterceptor` is based on message modification (it returns an internal `MessageWithThreadState` object to send).<br/>Consequently, you should be careful when combining this interceptor with any other that can also modify messages (for example, through the `MessageBuilder.withPayload(…​)…​build()`).<br/>The state to propagate may be lost.<br/>In most cases, to overcome the issue, you can order the interceptors for the channel and ensure the `ThreadStatePropagationChannelInterceptor` is the last one in the stack.|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

Propagation and population of `SecurityContext` is just one half of the work.
Since the message is not an owner of the threads in the message flow and we should be sure that we are secure against any incoming messages, we have to clean up the `SecurityContext` from `ThreadLocal`.
The `SecurityContextPropagationChannelInterceptor` provides the `afterMessageHandled()` interceptor method implementation.
It cleans up operation by freeing the thread at the end of invocation from that propagated principal.
This means that, when the thread that processes the handed-off message finishes processing the message (successful or otherwise), the context is cleared so that it cannot inadvertently be used when processing another message.

|   |When working with an [asynchronous gateway](./gateway.html#async-gateway), you should use an appropriate `AbstractDelegatingSecurityContextSupport` implementation from Spring Security [Concurrency Support](https://docs.spring.io/spring-security/site/docs/current/reference/html/servlet-webclient.html#concurrency), to let security context propagation be ensured over gateway invocation.<br/>The following example shows how to do so:<br/><br/>```<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/>```|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|