【问题标题】:How to run DelegatingSecurityContextRunnable every time when tomcat creates new Thread每次tomcat创建新线程时如何运行DelegatingSecurityContextRunnable
【发布时间】:2017-11-16 04:33:15
【问题描述】:

我有一个使用带有 websockets 的 tomcat 的 spring 应用程序。我想使用 DelegatingSecurityContextRunnable 每次当tomcat创建一个新线程时执行,即扭曲tomcat线程。有谁知道这是怎么做到的。提问的原因可以查到。here

也许这可以通过使用 AOP 和一些建议来完成?

【问题讨论】:

  • 你不能用 Spring AOP 做到这一点,因为线程是由 Tomcat 而不是 Spring 创建的。你可以求助于 AspectJ weaving,但我认为这会给你带来很多其他问题。您链接到的问题与 Mock 测试有关,我强烈反对,因为在 MockMVC 测试中包含 Spring SecurityFilterChain 相当容易,这意味着您只能模拟 HttpLayer。
  • 克劳斯我不明白为什么这仅与模拟测试有关。我看到的问题是,当tomcat创建一个新的兄弟线程时,安全上下文在那里不可用,因为线程本身是一个siblig线程。如前所述,第一个线程具有安全上下文,但所有后续线程都没有。在这种情况下,我不需要 spring 安全过滤器链,因为我的身份验证是 websocket 的一部分,而不是 http 交换的一部分,并且由于所有这些过滤器都针对 http,我不太确定为什么我需要它们?
  • 我正在按照想法覆盖那里的 tomcat 执行程序并在那里插入 DelegatingSecurityContextRunnable 但到目前为止没有任何成功

标签: spring spring-boot spring-security


【解决方案1】:

在 Spring Boot 中,您可以通过挂钩到 Tomcat 连接器来配置 Wrapper。看这个例子:

@Bean
    public EmbeddedServletContainerFactory servletContainerFactory() {
        TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();

        factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {

            @Override
            public void customize(Connector connector) {
                AbstractProtocol protocolHandler = (AbstractProtocol) connector.getProtocolHandler();
                TaskQueue taskqueue = new TaskQueue() {
                    @Override
                    public boolean offer(Runnable e, long timeout, TimeUnit unit) throws InterruptedException {
                        return super.offer(new MyRunnable(e), timeout, unit);
                    }

                    @Override
                    public boolean offer(Runnable o) {
                        return super.offer(new MyRunnable(o));
                    }
                };
                TaskThreadFactory tf = new TaskThreadFactory("artur-" + "-exec-", false, 0);
                ThreadPoolExecutor e = new ThreadPoolExecutor(10, 10, 1000, TimeUnit.SECONDS, taskqueue);
                taskqueue.setParent(e);
                protocolHandler.setExecutor(e);
            }
        });
        return factory;
    }

这是我的自定义 Runable(这可以是任何包装器,我没有费心实现你的):

static class MyRunnable implements Runnable {

        private Runnable r;

        public MyRunnable(Runnable r) {
            this.r = r;
        }

        @Override
        public void run() {
            System.out.println("Custom runable");
            runInner();
        }

        void runInner() {
            r.run();
        }

    }

这是我的导入:

import java.util.concurrent.TimeUnit;

import org.apache.catalina.connector.Connector;
import org.apache.coyote.AbstractProtocol;
import org.apache.tomcat.util.threads.TaskQueue;
import org.apache.tomcat.util.threads.TaskThreadFactory;
import org.apache.tomcat.util.threads.ThreadPoolExecutor;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.PropertySource;

这是做什么的:

Tomcat 连接器会自行初始化。您可以将执行程序设置为使用,在这种情况下,Tomcat 将停止创建自己的配置,而是使用您的配置。

通过覆盖队列中的报价方法,您有机会将您的Runnable 包装在任何自定义Runnable 中。就我而言,为了进行测试,我只是添加了Sysout 以查看一切是否正常。

我使用的Threadpool 实现是tomcat 默认值的精确副本(减去属性)。这样,行为保持不变,除了任何 Runnable 现在是您的委托包装器。

当我测试它时,我的控制台会打印:

 Custom runable

我希望这就是你要找的。​​p>

我使用 spring boot,但这本质上是一个 tomcat 问题而不是一个 spring 问题。您可以根据具体情况调整解决方案。

-- 阿图尔

【讨论】:

  • Arthur 非常感谢这个,你能告诉我这个例子中的 ThreadPoolExecutor 是来自 tomcat 的 ThreadPoolExecutor,而不是来自 Spring 的那个吗?即我的意思是包 org.apache.tomcat.util.threads.ThreadPoolExecutor。
  • @Tito 是的。那个只是实现相同的接口,所以没关系。无论如何,我会用相关的导入更新我的帖子。
  • @Arthur 如果我理解正确,这是在包装我的可运行文件,但不应该反过来说,即包装 tomcat 可运行文件?我现在正在测试这个。
  • offer 方法获取 tomcat 正在创建的Runnable。您可以通过提供自己的实现来包装那个 (new MyRunnable(e))。在 MyRunnable 里面,你只需委托给原来的那个。报价只是将其添加到队列中。 Executor 最终选择并执行它
  • 请注意,听起来您想传递一个安全上下文。在创建 Runnable 时,它​​没有安全上下文。那是相当低级的,并且该请求尚未被处理。你的问题对我的意图不清楚,所以我只是演示如何在其中挂钩你自己的代码,我不知道 DelegateSecurityRunable 做了什么
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-11
相关资源
最近更新 更多