Tomcat Nginx 背后:如何在非标准端口上同时代理 HTTP 和 HTTPS?
Tomcat behind Nginx: how to proxy both HTTP and HTTPS, possibly on non-standard ports?
描述
我们正在为不同的客户端在 Nginx 后面安装一些应用程序 运行 Tomcat 6。其中一些安装仅支持 HTTP,一些仅支持 HTTPS,有些则两者兼而有之。由于缺少 public IP,其中一个安装的 HTTP 和 HTTPS 在非标准端口(8070 和 8071)上工作。手头的应用程序在另一个应用程序中显示为 iframe。
当前行为
Tomcat 将所有 HTTPS 请求重定向到 HTTP(因此由于浏览器对混合内容的限制,iframe 中不显示任何内容)。
当前配置
iframe 代码:
<iframe src="/saiku-ui">
Tomcat的server.xml
:
<Connector port="8080" protocol="HTTP/1.1"/>
<!-- A bit later... -->
<Valve className="org.apache.catalina.valves.RemoteIpValve"
remoteIpHeader="x-forwarded-for"
protocolHeader="x-forwarded-proto"
/>
Nginx 虚拟主机:
server {
listen 80;
listen 443 ssl spdy;
location /saiku-ui {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://saiku-server; # This is upstream name
proxy_redirect off;
}
}
upstream saiku-server {
server ip.of.tomcat.server:8080;
}
期望的行为
Tomcat 应该在一个端口上侦听 HTTP 和 HTTPS 请求。
如果有两个 <Connector>
标签,配置 Nginx 会更难。
Tomcat 不应在模式之间重定向。
- Nginx 可以监听任意端口(例如
listen 8071 ssl spdy;
)。
- 由 Tomcat 生成的链接应该是相对的,或者包括 Nginx 提供的架构、主机和端口。
附加信息
我尝试将 schema
和 proxyPort
属性添加到 <Connector>
,之后 Tomcat 将始终从 HTTP 重定向到 HTTPS(至少更好) .
我不能google这样的配置,也没有使用过Tomcat。请帮忙
实际上我想要的是不可能的,所以需要在Nginx中有两个独立的Connector
标签和两个上游,像这样:
Tomcat的server.xml
:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
proxyPort="80"
/>
<Connector port="8443" protocol="HTTP/1.1"
connectionTimeout="20000"
proxyPort="443"
scheme="https" secure="true"
/>
匹配的 Nginx 配置:
server {
listen 80;
listen 443 ssl spdy;
location /saiku-ui {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://saiku-server-$scheme; # This is upstream name, note the variable $scheme in it
proxy_redirect off;
}
}
upstream saiku-server-http {
server ip.of.tomcat.server:8080;
}
upstream saiku-server-https {
server ip.of.tomcat.server:8443;
}
请注意,Tomcat 在 8080 和 8443 端口上接收纯 HTTP 流量(那里没有 SSL,它被 Nginx 终止),但是对于 8443 端口上的连接,它将生成必须以 [=14 开头的链接=] 而不是 http://
(通过属性 scheme="https" secure="true"
)并将插入链接端口,在 proxyPort
属性中指定。
Nginx 将终止 SSL 并通过 saiku-server-https
上游代理到 Tomcat 的 8443 端口的所有安全连接,其中 https
是 $scheme
Nginx 请求变量的值(参见 location
版块)
如果您正在使用 Spring 并且不想更改 tomcat 配置,还有另一种基于 this answer.
的解决方案
几个小时后google,发现没有像官方支持配置这样的标准方案,参考。
请参阅 java 文档 HttpServletResponse.sendRedirect。
Sends a temporary redirect response to the client using the specified redirect location URL. This method can accept relative URLs; the servlet container must convert the relative URL to an absolute URL before sending the response to the client. If the location is relative without a leading '/' the container interprets it as relative to the current request URI. If the location is relative with a leading '/' the container interprets it as relative to the servlet container root.
HttpServletResponseWrapper
import org.apache.commons.lang.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;
public class SendRedirectOverloadedResponseBasedOnXForwardedProtocol extends HttpServletResponseWrapper {
private HttpServletRequest request;
public SendRedirectOverloadedResponseBasedOnXForwardedProtocol(HttpServletRequest request,
HttpServletResponse response) {
super(response);
this.request = request;
}
public void sendRedirect(String location) throws IOException {
String xForwardedProtocol = request.getHeader("X-Forwarded-Protocol");
String host = request.getHeader("Host");
if (StringUtils.isNotBlank(xForwardedProtocol) && StringUtils.isNotBlank(host) && !isUrlAbsolute(location)) {
location = xForwardedProtocol + "://" + host + location;
}
super.sendRedirect(location);
}
public boolean isUrlAbsolute(String location) {
location = location == null ? "" : location;
return location.toLowerCase().startsWith("http");
}
}
过滤器
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class SendRedirectBasedOnXForwardedProtocolFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, new SendRedirectOverloadedResponseBasedOnXForwardedProtocol((HttpServletRequest) request, (HttpServletResponse) response));
}
@Override
public void destroy() {
}
}
描述
我们正在为不同的客户端在 Nginx 后面安装一些应用程序 运行 Tomcat 6。其中一些安装仅支持 HTTP,一些仅支持 HTTPS,有些则两者兼而有之。由于缺少 public IP,其中一个安装的 HTTP 和 HTTPS 在非标准端口(8070 和 8071)上工作。手头的应用程序在另一个应用程序中显示为 iframe。
当前行为
Tomcat 将所有 HTTPS 请求重定向到 HTTP(因此由于浏览器对混合内容的限制,iframe 中不显示任何内容)。
当前配置
iframe 代码:
<iframe src="/saiku-ui">
Tomcat的server.xml
:
<Connector port="8080" protocol="HTTP/1.1"/>
<!-- A bit later... -->
<Valve className="org.apache.catalina.valves.RemoteIpValve"
remoteIpHeader="x-forwarded-for"
protocolHeader="x-forwarded-proto"
/>
Nginx 虚拟主机:
server {
listen 80;
listen 443 ssl spdy;
location /saiku-ui {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://saiku-server; # This is upstream name
proxy_redirect off;
}
}
upstream saiku-server {
server ip.of.tomcat.server:8080;
}
期望的行为
Tomcat 应该在一个端口上侦听 HTTP 和 HTTPS 请求。
如果有两个
<Connector>
标签,配置 Nginx 会更难。Tomcat 不应在模式之间重定向。
- Nginx 可以监听任意端口(例如
listen 8071 ssl spdy;
)。 - 由 Tomcat 生成的链接应该是相对的,或者包括 Nginx 提供的架构、主机和端口。
附加信息
我尝试将 schema
和 proxyPort
属性添加到 <Connector>
,之后 Tomcat 将始终从 HTTP 重定向到 HTTPS(至少更好) .
我不能google这样的配置,也没有使用过Tomcat。请帮忙
实际上我想要的是不可能的,所以需要在Nginx中有两个独立的Connector
标签和两个上游,像这样:
Tomcat的server.xml
:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
proxyPort="80"
/>
<Connector port="8443" protocol="HTTP/1.1"
connectionTimeout="20000"
proxyPort="443"
scheme="https" secure="true"
/>
匹配的 Nginx 配置:
server {
listen 80;
listen 443 ssl spdy;
location /saiku-ui {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://saiku-server-$scheme; # This is upstream name, note the variable $scheme in it
proxy_redirect off;
}
}
upstream saiku-server-http {
server ip.of.tomcat.server:8080;
}
upstream saiku-server-https {
server ip.of.tomcat.server:8443;
}
请注意,Tomcat 在 8080 和 8443 端口上接收纯 HTTP 流量(那里没有 SSL,它被 Nginx 终止),但是对于 8443 端口上的连接,它将生成必须以 [=14 开头的链接=] 而不是 http://
(通过属性 scheme="https" secure="true"
)并将插入链接端口,在 proxyPort
属性中指定。
Nginx 将终止 SSL 并通过 saiku-server-https
上游代理到 Tomcat 的 8443 端口的所有安全连接,其中 https
是 $scheme
Nginx 请求变量的值(参见 location
版块)
如果您正在使用 Spring 并且不想更改 tomcat 配置,还有另一种基于 this answer.
的解决方案几个小时后google,发现没有像官方支持配置这样的标准方案,参考
请参阅 java 文档 HttpServletResponse.sendRedirect。
Sends a temporary redirect response to the client using the specified redirect location URL. This method can accept relative URLs; the servlet container must convert the relative URL to an absolute URL before sending the response to the client. If the location is relative without a leading '/' the container interprets it as relative to the current request URI. If the location is relative with a leading '/' the container interprets it as relative to the servlet container root.
HttpServletResponseWrapper
import org.apache.commons.lang.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;
public class SendRedirectOverloadedResponseBasedOnXForwardedProtocol extends HttpServletResponseWrapper {
private HttpServletRequest request;
public SendRedirectOverloadedResponseBasedOnXForwardedProtocol(HttpServletRequest request,
HttpServletResponse response) {
super(response);
this.request = request;
}
public void sendRedirect(String location) throws IOException {
String xForwardedProtocol = request.getHeader("X-Forwarded-Protocol");
String host = request.getHeader("Host");
if (StringUtils.isNotBlank(xForwardedProtocol) && StringUtils.isNotBlank(host) && !isUrlAbsolute(location)) {
location = xForwardedProtocol + "://" + host + location;
}
super.sendRedirect(location);
}
public boolean isUrlAbsolute(String location) {
location = location == null ? "" : location;
return location.toLowerCase().startsWith("http");
}
}
过滤器
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class SendRedirectBasedOnXForwardedProtocolFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, new SendRedirectOverloadedResponseBasedOnXForwardedProtocol((HttpServletRequest) request, (HttpServletResponse) response));
}
@Override
public void destroy() {
}
}