全站HTTPS第二篇:Nginx SSL集群中request.getScheme() 取不到HTTPS协议

2018-02-04 13:06:42 +0000

在上一博客中完成Nginx HTTPS的配置,首页可显示但排版有问题,在Chrome Console查看发现静态文件CSS/JS被阻止加载。简单说就是我们访问的是HTTPS页面,但是页面内的静态文件路径前缀是http://... ,各浏览器因为安全性问题会禁止加载HTTPS混合内容。

问题的原因是在写JS/CSS路径时,我在程序中使用了代码request.getScheme() 。说明通过Nginx + HTTPS代理后,Tomcat (HTTP)无法通过request.getScheme() 获取正确的协议类型。

 

解决方法很简单,只需要分别配置一下 Nginx 和 Tomcat 就好了,不用改程序。

Nginx配置:

     proxy_set_header Host $host;  

     proxy_set_header X-Real-IP $remote_addr;   

     proxy_set_header X-Forwarded-Proto $scheme;

     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

 

Tomcat配置:

     <Valve className="org.apache.catalina.valves.RemoteIpValve" remoteIpHeader="X-Forwarded-For"

        protocolHeader="X-Forwarded-Proto" protocolHeaderHttpsValue="https" internalProxies="127\.0\.0\.1|0:0:0:0:0:0:0:1" proxiesHeader="x-forwarded-by"/>

 

 

Tips:

1. 注意internalProxies属性,顾名思义内部代理,实际就是内部代理(Nginx)的IP地址。

为什么要拿出来说这个?我通过网上的解决方案(没有配置internalProxies属性),有时可以解决request.getScheme() 问题,有时候问题还是存在。来回测试折腾多次,结果让我百思不得解!!!

后面我首先确定  Tomcat 配置RemoteIpValve 是否有作用。发现再不配置的情况下,request.getScheme()问题100%存在,而在配置情况下有概率性的正确。

查看RemoteIpValve.java源码中的处理规则,没有理由出现这种问题。internalProxies是有默认值的,127.0.0.1是包含在默认值里面的。一头雾水,只能通过日志输出request.getRemoteAddr(),看为什么又是处理规则没被执行。多次刷新访问都是127.0.0.1,页面也是正常的,再次刷新时页面排版不正常了,再后台输出0:0:0:0:0:0:0:1,妈蛋 这是IPv6 (IPv6本地回环地址0:0:0:0:0:0:0:1,可理解为IPv4的127.0.0.1) ...

 

2. 建议把RemoteIpValve放在<Host/>标签内。

Valve组件类似于过滤器,它可以工作于Engine和Host/Context之间、Host和Context之间以及Context和Web应用程序的某资源之间。一个容器内可以建立多个Valve,而且Valve定义的次序也决定了它们生效的次序。

 

3. 源码中关于 RemoteIpValve 的处理规则如下,可对照源码 line 565 ~ 702 查看:

This valve proceeds as follows:

If the incoming request.getRemoteAddr() matches the valve's list of internal proxies :

  • Loop on the comma delimited list of IPs and hostnames passed by the preceding load balancer or proxy in the given request's Http header named $remoteIpHeader (default value x-forwarded-for). Values are processed in right-to-left order.
  • For each ip/host of the list:
    • if it matches the internal proxies list, the ip/host is swallowed
    • if it matches the trusted proxies list, the ip/host is added to the created proxies header
    • otherwise, the ip/host is declared to be the remote ip and looping is stopped.
  • If the request http header named $protocolHeader (e.g. x-forwarded-for) equals to the value of protocolHeaderHttpsValue configuration parameter (default https) then request.isSecure = true,request.scheme = https and request.serverPort = 443. Note that 443 can be overwritten with the $httpsServerPort configuration parameter.

 

参考链接:

源码 doc:其它的更多的配置可参考,API help doc 。

源码:org.apache.catalina.valves.RemoteIpValve.java

 

全站HTTPS共3篇:

全站HTTPS第一篇:Nginx配置HTTPS

全站HTTPS第二篇:Nginx SSL集群中request.getScheme() 取不到HTTPS协议

全站HTTPS第三篇:Nginx 80端口自动转发到443端口