【问题标题】:Tomcat behind Nginx: how to proxy both HTTP and HTTPS, possibly on non-standard ports?Nginx 背后的 Tomcat:如何代理 HTTP 和 HTTPS,可能在非标准端口上?
【发布时间】:2015-08-16 16:04:09
【问题描述】:

说明

我们正在为不同的客户端安装一些在 Nginx 后面运行 Tomcat 6 的应用程序。其中一些安装仅是 HTTP,一些仅是 HTTPS,或者两者兼而有之。由于缺少公共 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;
}

期望的行为

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

    如果会有两个&lt;Connector&gt; 标签,那么配置 Nginx 会更加困难。

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

  3. Nginx 可以监听任意端口(例如listen 8071 ssl spdy;)。
  4. Tomcat 生成的链接应该是相对的,或者包括 Nginx 提供的架构、主机和端口。

附加信息

我尝试将schemaproxyPort 属性添加到&lt;Connector&gt;,之后Tomcat 将始终从HTTP 重定向到HTTPS(至少这样更好)。

我无法在 google 上搜索这样的配置,也没有使用过 Tomcat 的经验。请帮忙。

【问题讨论】:

  • 试试:webapp.org.ua/sysadmin/… 我想这就是你正在“尝试”做的事情。
  • 我已经阅读了这篇文章。这是仅 HTTPS 设置的配置,但我需要 HTTP 和 HTTPS。无论如何,谢谢你的帮助。
  • 你不能在同一个端口中拥有 http 和 https。

标签: tomcat nginx reverse-proxy


【解决方案1】:

如果你用的是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() {
    }
}

【讨论】:

    【解决方案2】:

    其实我真正想要的是不可能的,所以需要在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:// 而不是 http:// 开头(通过属性scheme="https" secure="true") 并将插入链接端口,在proxyPort 属性中指定。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-14
      • 1970-01-01
      • 2021-10-16
      • 2020-04-06
      • 1970-01-01
      • 2012-06-17
      • 2017-05-16
      • 2013-03-03
      相关资源
      最近更新 更多