Junhc

岂止于博客

Nginx SSL+Tomcat集群,如何获取到https协议

服务器部署

遇到问题

浏览器访问地址如下
https://m.xxx.com/index.html

服务端request.getRequestURL()输出结果为
http://m.xxx.com/index.html

request.getScheme()  //总是http,而不是实际的http或https  
request.isSecure()  //总是false(因为总是http)  
request.getRemoteAddr()  //总是nginx请求的IP,而不是用户的IP  
request.getRequestURL()  //总是nginx请求的URL,而不是用户实际请求的URL  
response.sendRedirect(相对url)  //总是重定向到http上(因为认为当前是http请求)
解决方案

Nginx配置新增 proxy_set_header X-Forwarded-Proto $scheme;

location  / {
	index   index.html;
	proxy_set_header   Host   $host;
	proxy_set_header   X-Real-IP   $remote_addr;
	proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
	proxy_set_header   X-Forwarded-Proto   $scheme;
	break;
}

Tomcat的server.xml配置文件中Engine模块下新增一个Valve

<Engine name="Catalina" defaultHost="localhost">
    <Valve className="org.apache.catalina.valves.RemoteIpValve" remoteIpHeader="X-Forwarded-For"  protocolHeader="X-Forwarded-Proto" protocolHeaderHttpsValue="https"/>
    ...

有关更多信息,请参阅 http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/valves/RemoteIpValve.html 源码如下

if (protocolHeader != null) {  
    String protocolHeaderValue = request.getHeader(protocolHeader);  
    if (protocolHeaderValue == null) {  
        // don't modify the secure,scheme and serverPort attributes  
        // of the request  
    } else if (protocolHeaderHttpsValue.equalsIgnoreCase(protocolHeaderValue)) {  
        request.setSecure(true);  
        // use request.coyoteRequest.scheme instead of request.setScheme() because request.setScheme() is no-op in Tomcat 6.0  
        request.getCoyoteRequest().scheme().setString("https");  

        request.setServerPort(httpsServerPort);  
    } else {  
        request.setSecure(false);  
        // use request.coyoteRequest.scheme instead of request.setScheme() because request.setScheme() is no-op in Tomcat 6.0  
        request.getCoyoteRequest().scheme().setString("http");  

        request.setServerPort(httpServerPort);  
    }  
}
HTTP标头

HTTP 请求和 HTTP 响应使用标头字段发送有关 HTTP 消息的信息。标头字段为冒号分隔的名称值对,各个值对之间由回车符 (CR) 和换行符 (LF) 进行分隔。RFC 2616 信息标头中定义了标准 HTTP 标头字段集。此外还有应用程序广泛使用的非标准 HTTP 标头。某些非标准 HTTP 标头具有 X-Forwarded 前缀。传统负载均衡器 支持以下 X-Forwarded 标头。

先决条件 确认您的侦听器设置支持 X-Forwarded 标头。 配置您的 Web 服务器以记录客户端 IP 地址。

X-Forwarded 标头
  • X-Forwarded-For
  • X-Forwarded-Proto
  • X-Forwarded-Port
X-Forwarded-For

在您使用 HTTP 或 HTTPS 负载均衡器时,X-Forwarded-For 请求标头可帮助您识别客户端的 IP 地址。因为负载均衡器会拦截客户端和服务器之间的流量,因此您的服务器访问日志中将仅含有负载均衡器的 IP 地址。如需查看客户端的 IP 地址,使用 X-Forwarded-For 请求标题。Elastic Load Balancing 会在 X-Forwarded-For 请求标头中存储客户端的 IP 地址,并将标头传递到您的服务器。

X-Forwarded-For 请求标题将采用以下形式:
X-Forwarded-For: client-ip-address

下面是 IP 地址为 203.0.113.7 的客户端的 X-Forwarded-For 请求标头的示例。
X-Forwarded-For: 203.0.113.7

下面是 IPv6 地址为 2001:DB8::21f:5bff:febf:ce22:8a2e 的客户端的 X-Forwarded-For 请求标头的示例。
X-Forwarded-For: 2001:DB8::21f:5bff:febf:ce22:8a2e

如果来自客户端的请求已包含 X-Forwarded-For 标头,则 Elastic Load Balancing 会将客户端的 IP 地址附加到标头值的末尾。在这种情况下,列表中的最后一个 IP 地址是客户端的 IP 地址。
例如,以下标头包含由客户端添加的可能不可信的两个 IP 地址,以及由 Elastic Load Balancing 添加的客户端 IP 地址:
X-Forwarded-For: ip-address-1, ip-address-2, client-ip-address

X-Forwarded-Proto

X-Forwarded-Proto 请求标头可帮助您识别客户端与您的负载均衡器连接时所用的协议 (HTTP 或 HTTPS)。您的服务器访问日志仅包含在服务器和负载均衡器之间使用的协议;不含任何关于在客户端和负载均衡器之间使用的协议之信息。如需判断在客户端和负载均衡器之间使用的协议,使用 X-Forwarded-Proto 请求标题。Elastic Load Balancing 会在 X-Forwarded-Proto 请求标题中存储客户端和负载均衡器之间使用的协议,并随后将标题传递到您的服务器。 您的应用程序或网站可以使用存储在 X-Forwarded-Proto 请求标题中的协议来表现重新导向至适用 URL 的响应。

X-Forwarded-Proto 请求标题将采用以下形式:
X-Forwarded-Proto: originatingProtocol

下例中包含的 X-Forwarded-Proto 请求标题源自作为 HTTPS 请求生成自客户端的请求:
X-Forwarded-Proto: https

X-Forwarded-Port

X-Forwarded-Port 请求标头可帮助您识别客户端与您的负载均衡器连接时所用的端口。