Nginx背后的Tomcat:如何在非标准端口上代理HTTP和HTTPS?

时间:2015-06-02 17:57:39

标签: tomcat nginx reverse-proxy

描述

我们正在为不同的客户安装一些在Nginx后面运行Tomcat 6的应用程序。其中一些安装仅限HTTP,仅部分HTTPS,两者都安装。其中一个安装由于缺少公共IP而在非标准端口(8070和8071)上运行HTTP和HTTPS。手头的应用程序在另一个应用程序中显示为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 vhost:

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;
}

期望的行为

  1. Tomcat应该在一个端口上侦听HTTP和HTTPS请求。

    如果有两个<Connector>标签,配置Nginx会更加困难。

  2. Tomcat不应该在模式之间重定向。

  3. Nginx可以侦听任意端口(例如listen 8071 ssl spdy;)。
  4. 由Tomcat生成的链接应该是相对的,或者包含Nginx提供的架构,主机和端口。
  5. 其他信息

    我尝试将schemaproxyPort属性添加到<Connector>,之后Tomcat将始终从HTTP重定向到HTTPS(至少它更好)。

    我不能谷歌这样的配置,没有经验丰富的Tomcat。请帮忙。

2 个答案:

答案 0 :(得分:1)

实际上我真正想要的是不可能的,因此需要在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端口上的连接,它将生成链接必须以https://而不是{{}开头。 1}}(通过属性http://)并将插入scheme="https" secure="true"属性中指定的链接端口。

Nginx将终止SSL并通过proxyPort上游代理到Tomcat的8443端口的所有安全连接,其中saiku-server-httpshttps Nginx请求变量的值(参见$scheme }块)

答案 1 :(得分:0)

如果您使用的是Spring,并且不想更改tomcat的配置,那么还有另一种基于this answer的解决方案。

经过数小时的Google搜索,我发现没有像官方支持的配置那样的标准解决方案,请参阅this comment

请参阅Java文档HttpServletResponse.sendRedirect

使用指定的重定向位置URL向客户端发送临时重定向响应。该方法可以接受相对URL; servlet容器必须在将响应发送到客户端之前将相对URL转换为绝对URL 。如果位置是相对的而没有前导“ /”,则容器会将其解释为相对于当前请求URI的相对位置。如果位置与前导“ /”相对,则容器将其解释为相对于servlet容器根。

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() {
    }
}