Spring Integration
  1. Spring Integration
  2. INT-2166

Adding support for SecurityContext Propagation

    Details

    • Type: New Feature New Feature
    • Status: Open
    • Priority: Minor Minor
    • Resolution: Unresolved
    • Affects Version/s: None
    • Fix Version/s: General Backlog
    • Component/s: Security
    • Labels:
      None

      Description

      There are a lot of cases when it is necessary to use the SecurityContext in async mesage flow inside Web application.
      The principal is appeared in HttpRequest from springSecurityFilterChain and stored in the ThreadLocal holder.
      So, at first, I propose to add supoprt of some global ChannelInterceptor which can propagetes that securityContext to async mesasge flow.
      This is my solution. Sorry for Groovy code.

       
      class SecurityContextPropagationChannelInterceptor extends ChannelInterceptorAdapter {
          @Override
          Message<?> preSend(Message<?> message, MessageChannel channel) {
              if (!(channel in AbstractPollableChannel) || message.headers.securityContext || !SecurityContextHolder.context.authentication) return message
              return MessageBuilder.fromMessage(message).with {
                  setHeader 'securityContext', SecurityContextHolder.context
                  build()
              }
          }
      
          @Override
          Message<?> postReceive(Message<?> message, MessageChannel channel) {
              if (channel in AbstractPollableChannel && message.headers.securityContext) {
                  SecurityContextHolder.context = message.headers.securityContext
              }
              return message
          }
      
      
      }
      

      In this case the end-programmer can use

      SecurityContextHolder.getContext()

      As well as always in any place of his application.

        Issue Links

          Activity

          Hide
          Artem Bilan added a comment -

          Hi, Damien!

          Thank you for attention to this.

          We still decide to keep out of this implementation in the framework.

          What is a problem to use in your case suggested workaround in face of <channel-interceptor>?

          Show
          Artem Bilan added a comment - Hi, Damien! Thank you for attention to this. We still decide to keep out of this implementation in the framework. What is a problem to use in your case suggested workaround in face of <channel-interceptor> ?
          Hide
          Damien Hollis added a comment -

          Hi Artem,

          The problem with <channel-interceptor> is that I can setup the SecurityContext in postReceive but there is no opportunity to clean it up when the Message has been processed. This isn't the end of the world but I don't like the idea of leaving a SecurityContext bound to a ThreadLocal in case the thread gets used for some other processing that shouldn't have access to the SecurityContext.

          Is there a solution to this problem that I'm not aware of?

          Regards,
          Damien

          Show
          Damien Hollis added a comment - Hi Artem, The problem with <channel-interceptor> is that I can setup the SecurityContext in postReceive but there is no opportunity to clean it up when the Message has been processed. This isn't the end of the world but I don't like the idea of leaving a SecurityContext bound to a ThreadLocal in case the thread gets used for some other processing that shouldn't have access to the SecurityContext. Is there a solution to this problem that I'm not aware of? Regards, Damien
          Hide
          Artem Bilan added a comment -

          Well, what you need is here:
          1. <channel-interceptor> to propagate SecurityContext for non-direct channels
          2. Wrap TaskExecutor for Executor Channels with DelegatingSecurityContextExecutor from Spring Security: https://spring.io/blog/2012/12/17/spring-security-3-2-m1-highlights-servlet-3-api-support
          3. For Queue Channels you should implement some SecurityContextCleanInterceptor, because <poller> has the advice-chain attrbiute:

          public class SecurityContextCleanInterceptor implements MethodInterceptor {
               	public Object invoke(MethodInvocation invocation) throws Throwable {
          		try {
          			return invocation.proceed();
          		}
          		finally {
                                  SecurityContextHolder.clearContext();
          		}
          	}
          }
          

          Is it reasonable for you?

          Show
          Artem Bilan added a comment - Well, what you need is here: 1. <channel-interceptor> to propagate SecurityContext for non-direct channels 2. Wrap TaskExecutor for Executor Channels with DelegatingSecurityContextExecutor from Spring Security: https://spring.io/blog/2012/12/17/spring-security-3-2-m1-highlights-servlet-3-api-support 3. For Queue Channels you should implement some SecurityContextCleanInterceptor , because <poller> has the advice-chain attrbiute: public class SecurityContextCleanInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { try { return invocation.proceed(); } finally { SecurityContextHolder.clearContext(); } } } Is it reasonable for you?
          Hide
          Damien Hollis added a comment -

          Hi Artem,

          I think your suggests would work for Executor and Queue Channels. However, I couldn't see how to make either work for a Jms Channel. So instead I implemented a MessageHandler and configured it at the beginning of a chain of handlers:

          public class ContextPropagatingMessageHandler extends AbstractMessageHandler implements MessageProducer {
          
              private final MessagingTemplate messagingTemplate = new MessagingTemplate();
          
              private MessageChannel outputChannel;
          
              @Override
              public void setOutputChannel(MessageChannel outputChannel) {
                  this.outputChannel = outputChannel;
              }
          
              @Override
              protected void handleMessageInternal(Message<?> message) throws Exception {
                  SecurityContext securityContext = (SecurityContext) message.getHeaders().get(ContextPropagatingChannelInterceptor.SECURITY_CONTEXT_KEY);
          
                  if (securityContext == null) {
                      sendMessage(message);
                  }
                  else {
                      try {
                          SecurityContextHolder.setContext(securityContext);
                          sendMessage(message);
                      }
                      finally {
                          SecurityContextHolder.clearContext();
                      }
                  }
              }
          
              private void sendMessage(Message<?> message) {
                  messagingTemplate.send(outputChannel, message);
              }
          }
          

          Can you recommend a better solution?

          Show
          Damien Hollis added a comment - Hi Artem, I think your suggests would work for Executor and Queue Channels. However, I couldn't see how to make either work for a Jms Channel. So instead I implemented a MessageHandler and configured it at the beginning of a chain of handlers: public class ContextPropagatingMessageHandler extends AbstractMessageHandler implements MessageProducer { private final MessagingTemplate messagingTemplate = new MessagingTemplate(); private MessageChannel outputChannel; @Override public void setOutputChannel(MessageChannel outputChannel) { this.outputChannel = outputChannel; } @Override protected void handleMessageInternal(Message<?> message) throws Exception { SecurityContext securityContext = (SecurityContext) message.getHeaders().get(ContextPropagatingChannelInterceptor.SECURITY_CONTEXT_KEY); if (securityContext == null) { sendMessage(message); } else { try { SecurityContextHolder.setContext(securityContext); sendMessage(message); } finally { SecurityContextHolder.clearContext(); } } } private void sendMessage(Message<?> message) { messagingTemplate.send(outputChannel, message); } } Can you recommend a better solution?
          Hide
          Artem Bilan added a comment -

          Hi, Damien!

          Sorry for delay.
          However this issue is very specific and has a lot of places for the imagination
          Instead of coding a MessageHandler you can build some gereric Advice and use it withing <request-handler-advice-chain>: http://docs.spring.io/spring-integration/docs/2.2.6.RELEASE/reference/html/messaging-endpoints-chapter.html#message-handler-advice-chain

          And, as you noticed, provided solution isn't robust and it doesn't cover all use-cases. There is still a window for involving an end-user to achieve the real solution.
          That's why I said that it shouldn't be provided by framework by default.
          SecurityContext is very simple and even Serializable object and Srping Security provides enough tools to work with it in the application with Spring infrastructure.

          Thanks

          Show
          Artem Bilan added a comment - Hi, Damien! Sorry for delay. However this issue is very specific and has a lot of places for the imagination Instead of coding a MessageHandler you can build some gereric Advice and use it withing <request-handler-advice-chain> : http://docs.spring.io/spring-integration/docs/2.2.6.RELEASE/reference/html/messaging-endpoints-chapter.html#message-handler-advice-chain And, as you noticed, provided solution isn't robust and it doesn't cover all use-cases. There is still a window for involving an end-user to achieve the real solution. That's why I said that it shouldn't be provided by framework by default. SecurityContext is very simple and even Serializable object and Srping Security provides enough tools to work with it in the application with Spring infrastructure. Thanks

            People

            • Assignee:
              Mark Fisher
              Reporter:
              Artem Bilan
            • Votes:
              1 Vote for this issue
              Watchers:
              8 Start watching this issue

              Dates

              • Created:
                Updated: