Spring Security and @Async (Authenticated Users mixed up)

后端 未结 6 556
离开以前
离开以前 2020-11-27 13:03

I asynchronously invoke method with Spring, using @Async.This method invokes other method annotated with @PreAuthorize, Spring Security Annotation. To make authorization wor

相关标签:
6条回答
  • 2020-11-27 13:17

    Jus to add to the answer from @axtavt, you would also want to override other method.

    @Override
        public <T> Future<T> submit(Callable<T> task) {
            ExecutorService executor = getThreadPoolExecutor();
            final Authentication a = SecurityContextHolder.getContext().getAuthentication();
            try {
                return executor.submit(new Callable<T>() {
                    @Override
                    public T call() throws Exception {
                        try {
                            SecurityContext ctx = SecurityContextHolder.createEmptyContext();
                            ctx.setAuthentication(a);
                            SecurityContextHolder.setContext(ctx);
                            return task.call();
                        } catch (Exception e) {
                            slf4jLogger.error("error invoking async thread. error details : {}", e);
                            return null;
                        } finally {
                            SecurityContextHolder.clearContext();
                        }
                    }
                });
            } catch (RejectedExecutionException ex) {
                throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
            }
        }
    
    0 讨论(0)
  • 2020-11-27 13:22

    I also ran into that problem. It is important to configure the ThreadPoolTaskExecutor correctly using the DelegatingSecurityContextAsyncTaskExecutor. Also it is important to call the initialize() method, otherwise an error is thrown.

    // define the TaskExecutor as a bean
    @Bean("threadPoolTaskExecutor")
    public TaskExecutor getAsyncExecutor() {
    
      ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
      executor.setCorePoolSize(20);
      executor.setMaxPoolSize(1000);
      executor.setWaitForTasksToCompleteOnShutdown(true);
      executor.setThreadNamePrefix("Async-");
      executor.initialize(); // this is important, otherwise an error is thrown
      return new DelegatingSecurityContextAsyncTaskExecutor(executor); // use this special TaskExecuter
    }
    

    // the method in your business logic which is called async
    @Override
    @Async("threadPoolTaskExecutor")
    public void yourLogic() {
      [..]
    }
    
    0 讨论(0)
  • 2020-11-27 13:22

    Based on @Ralph answer one can achieve Aync event with Spring with threadpooling and delegate the security using http://docs.spring.io/autorepo/docs/spring-security/4.0.0.M1/apidocs/org/springframework/security/task/DelegatingSecurityContextAsyncTaskExecutor.html

    Sample code

    <bean id="applicationEventMulticaster"
        class="org.springframework.context.event.SimpleApplicationEventMulticaster">
        <property name="taskExecutor">
            <ref bean="delegateSecurityAsyncThreadPool"/>
        </property>
    </bean>
    
    <bean id="threadsPool"
        class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    </bean>
    
    
    <bean id="delegateSecurityAsyncThreadPool"
        class="org.springframework.security.task.DelegatingSecurityContextTaskExecutor">
        <constructor-arg ref="threadsPool"/>
    </bean>
    
    0 讨论(0)
  • 2020-11-27 13:27

    I guess MODE_INHERITABLETHREADLOCAL doesn't work correctly with thread pool.

    As a possible solution you can try to subclass ThreadPoolTaskExecutor and override its methods to propagate SecurityContext manually, and then declare that executor instead of <task:executor>, something like this:

    public void execute(final Runnable r) {
        final Authentication a = SecurityContextHolder.getContext().getAuthentication();
    
        super.execute(new Runnable() {
            public void run() {
                try {
                    SecurityContext ctx = SecurityContextHolder.createEmptyContext();
                    ctx.setAuthentication(a);
                    SecurityContextHolder.setContext(ctx);
                    r.run();
                } finally {
                    SecurityContextHolder.clearContext();
                }
            }
        });
    }
    
    0 讨论(0)
  • 2020-11-27 13:27

    Using the information from Ralph and Oak -

    If you want to get @Async working with the standard task executor tag, you would set up your Spring XML config like this

    <task:annotation-driven executor="_importPool"/>
    <task:executor id="_importPool" pool-size="5"/>
    
    <bean id="importPool"
              class="org.springframework.security.task.DelegatingSecurityContextAsyncTaskExecutor">
         <constructor-arg ref="_importPool"/>
    </bean>
    

    Then in your @Async method, you would specify the pool you want to use

    @Async("importPool")
    public void run(ImportJob import) {
       ...
    }
    

    That should work so when whenever you call your @Async method, the threadpool thread will use the same security context as the calling thread

    0 讨论(0)
  • 2020-11-27 13:28

    This is just a hint that needs future investigation (I am too tired, but maybe somebody find this useful for future investigation):

    Today I stumbled over org.springframework.security.task.DelegatingSecurityContextAsyncTaskExecutor see GitHub.

    it looks like that his designed to delegate the security context so that it is "passed" through the @Async call.

    Also have a look at this post: Spring Security 3.2 M1 Highlights, Servlet 3 API Support is sounds like it is strongly related.

    0 讨论(0)
提交回复
热议问题