0. 环境信息

Linux:Linux i-8emt1zr1 2.6.32-573.el6.x86_64 #1 SMP Wed Jul 1 18:23:37 EDT 2015 x86_64 x86_64 x86_64 GNU/Linux

nginx:nginx version: openresty/1.9.3.2

Tomcat:Server version: Apache Tomcat/7.0.64

1. 问题描述
我们开发的客服系统,因为消息的到来,有的谷歌浏览器(V62)不支持http的消息提醒,要求https,故而,我们的系统,要将系统改造成https模式,另外,我们的系统,也有必要转化为https,为后续推广做准备。

2. 系统架构
LB+nginx+tomcat集群

3. 当前配置情况
SSL证书配置在LB上,nginx和tomcat服务器上,任然采用http协议通讯。即LB在接收到客户浏览器https请求消息后,将转发给LB下挂载的nginx上,都是以http的方式转发,nginx对这些请求进行反向代理,代理到后面的tomcat服务器上。

4. 遇到的问题
客服系统,有权限控制,基于tomcat的web应用,用户登录后,执行redirect跳转到指定的服务页面。就是这个跳转,遇到了问题,redirect在这里都被当做http跳转了。

登录前的样子:

登录后的样子:

问题主要发生在Tomcat7上,验证过tomcat8,是不存在问题的。

5. 如何解决

针对Tomcat7的这个问题,思路很简单,重点是解决redirect的时候,通知客户端浏览器以正确的scheme(https还是http)进行再次发起请求。
问题是, nginx这个时候收到的请求是来自LB的http请求了,怎么弄?其实是有办法的,可以利用HttpRequest中的referer字段,这个字段的含义,自行科普吧。将referer的请求scheme信息,用来作为当前请求的scheme,如此可以保证所有的请求都是同一个scheme,不会因为redirect而遗漏信息。

nginx里面相应的配置如下:

     location /CSS/websocket {
proxy_pass http://css_ws_svr;
proxy_set_header Host $host;
proxy_set_header Remote_Addr $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
} location /CSS {
proxy_pass http://css_svr;
proxy_set_header Host $host;
proxy_set_header Remote_Addr $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
set $mscheme $scheme;
if ($http_referer ~* ^https.*) {
set $mscheme "https"
;
}

proxy_set_header X-Forwarded-Proto $mscheme;
}

如上配置,经过nginx反向代理后的HttpServletRequest中header部分就带上了字段X-Forwarded-Proto。

另外一方面,就是tomcat里面,要做一个配置,让tomcat在解析请求和做重定向的时候,知道用什么协议。主要的配置在server.xml里面的Engine下,定义一个Value元素。

具体配置如下:

<Engine name="Catalina" defaultHost="localhost">

      <Realm className="org.apache.catalina.realm.LockOutRealm">
<!-- This Realm uses the UserDatabase configured in the global JNDI
resources under the key "UserDatabase". Any edits
that are performed against this UserDatabase are immediately
available for use by the Realm. -->
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm> <Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true"> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log." suffix=".txt"
pattern="%h %l %u %t &quot;%r&quot; %s %b" /> <Valve className="org.apache.catalina.valves.RemoteIpValve" remoteIpHeader="X-Forwarded-For" protocolHeader="X-Forwarded-Proto" protocolHeaderHttpsValue="https"/> <Context path="/CSS" docBase="/home/tomcat/app/cssv2"/>
</Host>
</Engine>

这个配置里面,重点是protocolHeader字段,意思就是说,当protocolHeader字段的值为protocolHeaderHttpsValue的https的时候,认为是安全连接,否则就是http的非安全连接。
对应的代码逻辑,可以看org.apache.catalina.valves.RemoteIpValve这个类的源码

public void invoke(org.apache.catalina.connector.Request request, Response response)
throws IOException, ServletException
{
......
if (protocolHeader != null) {
String protocolHeaderValue = request.getHeader(protocolHeader);
if (protocolHeaderValue != null)
{ if (protocolHeaderHttpsValue.equalsIgnoreCase(protocolHeaderValue)) {
request.setSecure(true); request.getCoyoteRequest().scheme().setString("https"); setPorts(request, httpsServerPort);
} else {
request.setSecure(false); request.getCoyoteRequest().scheme().setString("http"); setPorts(request, httpServerPort);
}
}
}
......
}

经过上面的分析和配置修改,最终很灵活的实现https和http同时工作。搞定这个问题,重点还是要对Http协议工作的流程有所了解,才能很容易的找到解决问题的思路。

若各位伙伴有更好的解决方案,也请分享或者一起探讨。

LB+nginx+tomcat7集群模式下的https请求重定向(redirect)后变成http的解决方案的更多相关文章

  1. Spark Streaming揭秘 Day31 集群模式下SparkStreaming日志分析(续)

    Spark Streaming揭秘 Day31 集群模式下SparkStreaming日志分析(续) 今天延续昨天的内容,主要对为什么一个处理会分解成多个Job执行进行解析. 让我们跟踪下Job调用过 ...

  2. Spark Streaming揭秘 Day30 集群模式下SparkStreaming日志分析

    Spark Streaming揭秘 Day30 集群模式下SparkStreaming日志分析 今天通过集群运行模式观察.研究和透彻的刨析SparkStreaming的日志和web监控台. Day28 ...

  3. 7.redis 集群模式的工作原理能说一下么?在集群模式下,redis 的 key 是如何寻址的?分布式寻址都有哪些算法?了解一致性 hash 算法吗?

    作者:中华石杉 面试题 redis 集群模式的工作原理能说一下么?在集群模式下,redis 的 key 是如何寻址的?分布式寻址都有哪些算法?了解一致性 hash 算法吗? 面试官心理分析 在前几年, ...

  4. 就publish/subscribe功能看redis集群模式下的队列技术(一)

    Redis 简介 Redis 是完全开源免费的,是一个高性能的key-value数据库. Redis 与其他 key - value 缓存产品有以下三个特点: Redis支持数据的持久化,可以将内存中 ...

  5. Redis集群模式下的redis-py-cluster方式读写测试

    与MySQL主从复制,从节点可以分担部分读压力不一样,甚至可以增加slave或者slave的slave来分担读压力,Redis集群中的从节点,默认是不分担读请求的,从节点只作为主节点的备份,仅负责故障 ...

  6. 【Redis集群原理专题】分析一下相关的Redis集群模式下的脑裂问题!

    技术格言 世界上并没有完美的程序,但是我们并不因此而沮丧,因为写程序就是一个不断追求完美的过程. 什么是脑裂 字面含义 首先,脑裂从字面上理解就是脑袋裂开了,就是思想分家了,就是有了两个山头,就是有了 ...

  7. Tomcat 集群模式下 Session 更新 Bug (redis memcached 及tomcat自已的集群)

    从 excel 中导入数据入系统,我们用的是先上传文件至服务器再分析所上传的文件逐行导入. 就是执行了一循环,在当前循环位置标识一下客户端就知道执行的进度了,以前的方式 是用 session.setA ...

  8. Hadoop框架:集群模式下分布式环境搭建

    本文源码:GitHub·点这里 || GitEE·点这里 一.基础环境配置 1.三台服务 准备三台Centos7服务,基础环境从伪分布式环境克隆过来. 133 hop01,134 hop02,136 ...

  9. 使用reids结合wcf实现集群模式下的聊天室功能

    1.reids的特点 Redis数据库完全在内存中,使用磁盘仅用于持久性. 相比许多键值数据存储,Redis拥有一套较为丰富的数据类型(字符串,哈希,列表,集合,有序集合),. Redis可以将数据复 ...

随机推荐

  1. HIVE点滴:group by和distinct语句的执行顺序

    同一条语句之中,如果同时有group by和distinct语句,是先group by后distinct,还是先distinct后group by呢? 先说结论:先group by后distinct. ...

  2. maven 细节 —— scope、坐标

    对于 idea 开发环境,测试代码便是在 src/test/java(该java目录会在创建时标注为测试文件夹) 目录下的 .java 代码为测试代码: 1. scope scope的分类 compi ...

  3. 数据文件resize扩容

    表空间不足 Alert日志报错 Mon Dec :: GMT+: Incremental checkpoint up to RBA[ox1af2d.3ddll.], current log tail ...

  4. html+css实现小米商城首页静态页面

    学了一个星期的html和css,用新学的东西写点东西,仿照小米商城的首页按照它的页面布局盗用它的图片写了个小米商城的静态页面. 源代码:链接:https://pan.baidu.com/s/1qf63 ...

  5. Python逻辑运算

    一.运算符种类 1.比较运算符 > ,< , >= ,<= , != , == 2.赋值运算符 =, +=,-=,*=,/=,**=,%= 3.成员运算符 in not in ...

  6. Liunx----vi编辑器

    关于vi编辑器基本的用法总结如下: vi是linux下的一款文编编辑器. 进入vi的方法:在系统启动起来后,直接键入vi或vi 文件名: 进去后输入内容方法:输入a或i,切换到编辑模式: 退出编辑模式 ...

  7. spring+springMVC,声明式事务失效,原因以及解决办法

    http://blog.csdn.net/z69183787/article/details/37819627#comments 一.声明式事务配置: <bean id="transa ...

  8. 【HAOI2011】problem a

    又看题解了,这样下去要跪啊QAQ 原题: 一次考试共有n个人参加,第i个人说:“有ai个人分数比我高,bi个人分数比我低.”问最少有几个人没有说真话(可能有相同的分数) 1≤n≤100000   0≤ ...

  9. js判断输入的字符是否是汉字

    <script type="text/javascript"> var c=prompt("请输入一个字符"); var isNumber = c& ...

  10. mysql修改用户密码

    修改自己的密码(root用户,其它用户应该也差不多) 方法一: [root@localhost /]# mysqladmin -u root -p password "root" ...