区分多个connectors/ports in embedded Tomcat in Spring Boot 之间的请求
Distinguish requests between multiple connectors/ports in embedded Tomcat in Spring Boot
我有一个 Spring 基于 Boot rest 服务的应用程序配置在多个端口上,需要区分每个请求通过的端口。为应用程序设置多个端口的想法是由于不同的 public 和私有 sub-networks(具有不同的安全访问级别)可以访问应用程序公开的服务的不同部分。
从概念上讲,这个想法是向嵌入式 tomcat 添加额外的连接器,然后通过向每个指定 "channel" 添加自定义 header 来捕获所有更改它们的传入请求通过.
我面临的问题是我不知道如何在连接器级别捕获这些传入请求(在它到达任何过滤器或 servlet 之前)。
因此,对于 multi-port 解决方案,我有:
@Configuration
public class EmbeddedTomcatConfiguration {
@Value("${server.additional-ports}")
private String additionalPorts;
@Bean
public TomcatServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
Connector[] additionalConnectors = additionalConnector();
if(additionalConnectors != null && additionalConnectors.length > 0) {
tomcat.addAdditionalTomcatConnectors(additionalConnectors);
}
return tomcat;
}
private Connector[] additionalConnector() {
if(StringUtils.isNotBlank(additionalPorts)) {
return Arrays.stream(additionalPorts.split(","))
.map(String::trim)
.map(p -> {
Connector connector = new Connector(Http11NioProtocol.class.getCanonicalName());
connector.setScheme("http");
connector.setPort(Integer.valueOf(p));
return connector;
})
.toArray(Connector[]::new);
}
return null;
}
}
理论上,我可以为每个连接器注册一个自定义 LifecycleListener
,但据我所知,这无济于事。我也听说过一些关于阀门的事情,但我不确定如何为每个连接器实现它们。
或者我走的路完全错了。
我非常感谢任何帮助。
您似乎下定决心要尝试使用 Valve,但是,经过更多研究后,我建议您使用 ServletFilter 而不是 Valve 来完成这项工作。
我相信您可以在 Valve 中完成这项工作,但 Valve 必须部署到 tomcat/lib 目录中,而不是打包到您的应用程序中。我建议您考虑尝试将您的应用程序放在可部署工件中,而不必记住在创建新部署时将一个额外的 jar 文件部署到您的 tomcat 实例。
根据这个答案,Difference between getLocalPort() and getServerPort() in servlets您应该能够通过在 HttpServletRequest 上调用 getLocalPort() 来访问 tomcat 端口。
然后根据你的想法在题中加一个specific context name into the request header.
public class PortContextFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
int localPort = request.getLocalPort();
// if else or case statements
ServletRequest newRequest = PortContextHttpServletRequestWrapper(request, YourPortContext)
filterChain.doFilter(newRequest, response);
}
public void destroy() {
// Nothing to do
}
public void init(FilterConfig filterConfig) throws ServletException {
// Nothing to do.
}
}
完成后,理论上,你应该可以使用@RequestMapping注解根据header中的名称进行路由。
@PreAuthorize("hasPermission(#your_object, 'YOUR_OBJECT_WRITE')")
@RequestMapping("/yourobject/{identifier}", headers="context=<context_name>", method = RequestMethod.POST)
public String postYourObject(@PathVariable(value = "identifier") YourObject yourObject) {
// Do something here...
...
}
也可以使用 RequestCondition
基于端口路由请求,我认为这更接近库存 spring。见
我有一个 Spring 基于 Boot rest 服务的应用程序配置在多个端口上,需要区分每个请求通过的端口。为应用程序设置多个端口的想法是由于不同的 public 和私有 sub-networks(具有不同的安全访问级别)可以访问应用程序公开的服务的不同部分。
从概念上讲,这个想法是向嵌入式 tomcat 添加额外的连接器,然后通过向每个指定 "channel" 添加自定义 header 来捕获所有更改它们的传入请求通过.
我面临的问题是我不知道如何在连接器级别捕获这些传入请求(在它到达任何过滤器或 servlet 之前)。
因此,对于 multi-port 解决方案,我有:
@Configuration
public class EmbeddedTomcatConfiguration {
@Value("${server.additional-ports}")
private String additionalPorts;
@Bean
public TomcatServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
Connector[] additionalConnectors = additionalConnector();
if(additionalConnectors != null && additionalConnectors.length > 0) {
tomcat.addAdditionalTomcatConnectors(additionalConnectors);
}
return tomcat;
}
private Connector[] additionalConnector() {
if(StringUtils.isNotBlank(additionalPorts)) {
return Arrays.stream(additionalPorts.split(","))
.map(String::trim)
.map(p -> {
Connector connector = new Connector(Http11NioProtocol.class.getCanonicalName());
connector.setScheme("http");
connector.setPort(Integer.valueOf(p));
return connector;
})
.toArray(Connector[]::new);
}
return null;
}
}
理论上,我可以为每个连接器注册一个自定义 LifecycleListener
,但据我所知,这无济于事。我也听说过一些关于阀门的事情,但我不确定如何为每个连接器实现它们。
或者我走的路完全错了。
我非常感谢任何帮助。
您似乎下定决心要尝试使用 Valve,但是,经过更多研究后,我建议您使用 ServletFilter 而不是 Valve 来完成这项工作。
我相信您可以在 Valve 中完成这项工作,但 Valve 必须部署到 tomcat/lib 目录中,而不是打包到您的应用程序中。我建议您考虑尝试将您的应用程序放在可部署工件中,而不必记住在创建新部署时将一个额外的 jar 文件部署到您的 tomcat 实例。
根据这个答案,Difference between getLocalPort() and getServerPort() in servlets您应该能够通过在 HttpServletRequest 上调用 getLocalPort() 来访问 tomcat 端口。
然后根据你的想法在题中加一个specific context name into the request header.
public class PortContextFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
int localPort = request.getLocalPort();
// if else or case statements
ServletRequest newRequest = PortContextHttpServletRequestWrapper(request, YourPortContext)
filterChain.doFilter(newRequest, response);
}
public void destroy() {
// Nothing to do
}
public void init(FilterConfig filterConfig) throws ServletException {
// Nothing to do.
}
}
完成后,理论上,你应该可以使用@RequestMapping注解根据header中的名称进行路由。
@PreAuthorize("hasPermission(#your_object, 'YOUR_OBJECT_WRITE')")
@RequestMapping("/yourobject/{identifier}", headers="context=<context_name>", method = RequestMethod.POST)
public String postYourObject(@PathVariable(value = "identifier") YourObject yourObject) {
// Do something here...
...
}
也可以使用 RequestCondition
基于端口路由请求,我认为这更接近库存 spring。见