我需要使我的Spring Boot应用程序开始/停止动态监听新端口。我知道为此需要在Spring上下文中注入新的tomcat连接器。
我可以使用ServletWebServerFactoryBean和添加连接器tomcatConnectorCustomizer。但是,此bean仅在Spring Bootup期间加载。
ServletWebServerFactory
tomcatConnectorCustomizer
@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连接器吗?说一个方法调用?
我设法在运行时添加了Tomcat连接器。但是对该端口的请求不会发送到我的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):
@SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
2-配置文件(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):
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):
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):
@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
3-尝试:
http:// localhost:9090 / alternative / hello
预期响应 :你好测试2
4-尝试预期的错误:
http:// localhost:9090 / alternative / addNewPort?port = 8181&context = alternative
预期响应(允许使用上下文[替代],但端点未为此控制器在控制器中注册) :Whitelabel错误页面…
http:// localhost:9090 / any / hello
预期的响应(不允许上下文[任何]) :Whitelabel错误页面…
http:// localhost:8888 / any / hello
预期的响应(无效的端口号) :ERR_CONNECTION_REFUSED
http:// localhost:8080 /你好
预期的响应(不允许上下文[/ hello]) :Whitelabel错误页面…
5-尝试删除端口:
http:// localhost:8080 / admin / removePort?port = 9090
6-检查已移除的端口:
预期响应(端口关闭) :ERR_CONNECTION_REFUSED
希望对您有所帮助。