春季靴子|如何动态添加新的tomcat连接器? [英] Spring Boot | How to dynamically add new tomcat connector?

查看:144
本文介绍了春季靴子|如何动态添加新的tomcat连接器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要使我的Spring Boot应用程序动态启动/停止在新端口上的侦听. 我知道为此需要在Spring上下文中注入新的tomcat连接器.

I need to make my Spring Boot application start/stop listening on a new port dynamically. I understand a new tomcat connector needs to be injected into Spring context for this.

我可以使用ServletWebServerFactory bean和tomcatConnectorCustomizer添加连接器.但是此bean仅在Spring Bootup期间加载.

I'm able to add a connector using a ServletWebServerFactory bean and tomcatConnectorCustomizer. But this bean is loaded only during Spring Bootup.

@Bean
public ServletWebServerFactory servletContainer() {

    TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
    TomcatConnectorCustomizer tomcatConnectorCustomizer = connector -> {
        connector.setPort(serverPort);

        connector.setScheme("https");
        connector.setSecure(true);

        Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();

        protocol.setSSLEnabled(true);
        protocol.setKeystoreType("PKCS12");
        protocol.setKeystoreFile(keystorePath);
        protocol.setKeystorePass(keystorePass);
        protocol.setKeyAlias("spa");
        protocol.setSSLVerifyClient(Boolean.toString(true));
        tomcat.addConnectorCustomizers(tomcatConnectorCustomizer);
        return tomcat;

    }
}

在运行时是否可以添加tomcat连接器?说方法调用吗?

Is there any way to add a tomcat connector during run time? Say on a method call?

我设法在运行时添加了Tomcat连接器.但是对该端口的请求不会发送到我的RestController.

I've managed to add a Tomcat connector at runtime. But the request made to that port are not going to my RestController.

    TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();

    TomcatConnectorCustomizer tomcatConnectorCustomizer = connector -> {
        Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
        connector.setScheme("http");
        connector.setSecure(false);
        connector.setPort(8472);
        protocol.setSSLEnabled(false);
    };
    tomcat.addConnectorCustomizers(tomcatConnectorCustomizer);

    tomcat.getWebServer().start();

我应该怎么做?

推荐答案

这是我的示例项目:示例项目

1-主要应用程序(DemoApplication.java):

1- Main Application (DemoApplication.java):

    @SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
    }
}

2-配置文件(AppConfig.java):

2 - Config file (AppConfig.java):

@Configuration
public class AppConfig {

@Autowired
private ServletWebServerApplicationContext server;

private static FilterConfig filterConfig = new FilterConfig();

@PostConstruct
void init() {
    //setting default port config
    filterConfig.addNewPortConfig(8080, "/admin");
}

@Bean
@Scope(value = WebApplicationContext.SCOPE_APPLICATION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public FilterConfig createFilterConfig() {
    return filterConfig;
}

public void addPort(String schema, String domain, int port, boolean secure) {
    TomcatWebServer ts = (TomcatWebServer) server.getWebServer();
    synchronized (this) {
        ts.getTomcat().setConnector(createConnector(schema, domain, port, secure));
    }
}

public void addContextAllowed(FilterConfig filterConfig, int port, String context) {
    filterConfig.addNewPortConfig(port, context);
}

 public void removePort(int port) {
    TomcatWebServer ts = (TomcatWebServer) server.getWebServer();
    Service service = ts.getTomcat().getService();
    synchronized (this) {
        Connector[] findConnectors = service.findConnectors();
        for (Connector connector : findConnectors) {
            if (connector.getPort() == port) {
                try {
                    connector.stop();
                    connector.destroy();
                    filterConfig.removePortConfig(port);
                } catch (LifecycleException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

private Connector createConnector(String schema, String domain, int port, boolean secure) {
    Connector conn = new Connector("org.apache.coyote.http11.Http11NioProtocol");
    conn.setScheme(schema);
    conn.setPort(port);
    conn.setSecure(true);
    conn.setDomain(domain);
    if (secure) {
        // config secure port...
    }
    return conn;
}
}

3-过滤器(NewPortFilter.java):

3 - Filter (NewPortFilter.java):

public class NewPortFilter {
@Bean(name = "restrictFilter")
public FilterRegistrationBean<Filter> retstrictFilter(FilterConfig filterConfig) {
    Filter filter = new OncePerRequestFilter() {

        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                FilterChain filterChain) throws ServletException, IOException {

            // get allowed url contexts
            Set<String> config = filterConfig.getConfig().get(request.getLocalPort());
            if (config == null || config.isEmpty()) {
                response.sendError(403);
            }
            boolean accepted = false;
            for (String value : config) {
                if (request.getPathInfo().startsWith(value)) {
                    accepted = true;
                    break;
                }
            }
            if (accepted) {
                filterChain.doFilter(request, response);
            } else {
                response.sendError(403);
            }
        }
    };
    FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<Filter>();
    filterRegistrationBean.setFilter(filter);
    filterRegistrationBean.setOrder(-100);
    filterRegistrationBean.setName("restrictFilter");
    return filterRegistrationBean;
}
}

4-过滤器配置(FilterConfig.java):

4 - Filter Config (FilterConfig.java):

public class FilterConfig {

    private Map<Integer, Set<String>> acceptedContextsByPort = new ConcurrentHashMap<>();

    public void addNewPortConfig(int port, String allowedContextUrl) {
        if(port > 0 && allowedContextUrl != null) {
            Set<String> set = acceptedContextsByPort.get(port);
            if (set == null) {
                set = new HashSet<>();
            }
            set = new HashSet<>(set);
            set.add(allowedContextUrl);
            acceptedContextsByPort.put(port, set);
        }
    }

    public void removePortConfig(int port) {
        if(port > 0) {
            acceptedContextsByPort.remove(port);
        }
    }

    public Map<Integer, Set<String>> getConfig(){
        return acceptedContextsByPort;
    }
}

5-控制器(TestController.java):

5 - Controller (TestController.java):

@RestController
public class TestController {
@Autowired
AppConfig config;

@Autowired
FilterConfig filterConfig;

@GetMapping("/admin/hello")
String test() {
    return "hello test";
}

@GetMapping("/alternative/hello")
String test2() {
    return "hello test 2";
}

@GetMapping("/admin/addNewPort")
ResponseEntity<String> createNewPort(@RequestParam Integer port, @RequestParam String context) {
    if (port == null || port < 1) {
        return new ResponseEntity<>("Invalid Port" + port, HttpStatus.BAD_REQUEST);
    }
    config.addPort("http", "localhost", port, false);
    if (context != null && context.length() > 0) {
        config.addContextAllowed(filterConfig, port, context);
    }

    return new ResponseEntity<>("Added port:" + port, HttpStatus.OK);
}

@GetMapping("/admin/removePort")
ResponseEntity<String> removePort(@RequestParam Integer port) {
    if (port == null || port < 1) {
        return new ResponseEntity<>("Invalid Port" + port, HttpStatus.BAD_REQUEST);
    }
    config.removePort(port);

    return new ResponseEntity<>("Removed port:" + port, HttpStatus.OK);
 }
}

如何进行测试?

在浏览器中:

1-尝试:

http://localhost:8080/admin/hello

预期的响应:您好测试

2-尝试:

http://localhost:8080/admin/addNewPort?port = 9090& context = alternative

预期的响应:已添加端口:9090

Expected Response: Added port:9090

3-尝试:

http://localhost:9090/alternative/hello

预期的响应:您好测试2

4-尝试预期的错误:

4 - try expected errors:

http://localhost:9090/alternative/addNewPort?port = 8181& context = alternative

预期的响应(允许上下文[替代],但端点未为此控制器在控制器中注册):白标错误页面...

Expected Response (context [alternative] allowed but endpoint not registered in controller for this context): Whitelabel Error Page...

http://localhost:9090/any/hello

预期的响应(不允许任何上下文):白标错误页面...

Expected Response (context [any] not allowed): Whitelabel Error Page...

http://localhost:8888/any/hello

预期的响应(无效的端口号):ERR_CONNECTION_REFUSED

Expected Response (invalid port number): ERR_CONNECTION_REFUSED

http://localhost:8080/hello

预期的响应(不允许上下文[/hello]):白标错误页面...

Expected Response (no context allowed [/hello]): Whitelabel Error Page...

5-尝试删除端口:

http://localhost:8080/admin/removePort?port = 9090

6-检查已删除的端口:

6 - check removed port:

http://localhost:9090/alternative/hello

预期的响应(端口关闭):ERR_CONNECTION_REFUSED

Expected Response (port closed): ERR_CONNECTION_REFUSED

希望对您有帮助.

这篇关于春季靴子|如何动态添加新的tomcat连接器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆