记一次nginx反向代理引起的无法登录问题

背景

事情是这样的,公司以前开发了一套系统,但是因为国家政策原因下线了,时隔两年,现在可以重新上线了,但是只有代码,相关环境的配置是没有的。在重新上线的过程中,遇到了系统无法登陆的问题。这套系统架构是这样的:nginx作为web接入层,数据处理会反向代理到tomcat上跑的java服务。一开始我怀疑是不是代码逻辑有问题,但是开发称代码是没问题的,为此,开发还将获取数据请求跳过nginx直接抛给Tomcat,结果就真的没问题了。但是实际生产中是不能这样跑的,因为这样会暴露处理数据的Tomcat。所以只能在nginx这里找问题。

排查过程

现象是这样的,登录的时候会成功进入系统,但是会立刻跳回登录页面。登录的过程会有一条post请求将登录信息上传给系统,还有一条get请求获取登录状态,但是我不清楚获取登录状态的逻辑是怎样的,判断条件是什么,本想让开发看一下 代码逻辑的,但是开发也看不出个所以然来。接着我看了post和get请求的包头信息,包头信息如下:
post请求包头信息
image.png
get请求头区别
image.png
然而仅仅看这两个包头信息我看不出什么异常,所以我让开发将代码改为直接跳过nginx可以登录的情况,我想对比能登录和不能登录的情况下有什么区别,以下是能登录的情况下的包头信息:
post请求包头信息
image.png
get请求头区别
image.png
对比了两种情况的包头信息,我发现了一个区别(ps:请忽略Access-Control相关头信息差异,这是后面解决跨域加的),那就是不能登录的时候post和get请求都有set-cookie,而可以登录的时候,只有post请求有set-cookie。所以我猜想是post请求的时候登录成功会生成一个cookie,然后get请求去读取cookie,获取到cookie即为登录状态。所以我又对比了两种情况的cookie。不过在看cookie的具体情况之前,我要先说清楚两种情况下请求的区别。不能登录的情况下,使用了nginx做反向代理,http://域名/字符串/XXX 会被反向代理到 http://域名:端口/项目名/XXX 即用项目名替代固定的字符串(据说这样做是为了区分测试环境和生产环境的),然后可以登录的情况是不经过nginx代理,直接请求了http://域名:端口/项目名/XXX 。因为cookie里面涉及到这些,所以要先说清楚。下面是cookie的具体信息
不能登录情况下post请求的cookie信息
1.jpg
不能登录情况下get请求的cookie信息
2.jpg
可以登录情况下post请求的cookie信息
image.png
可以登录情况下get请求的cookie信息
4.jpg
可以看到的出来,可以登录的情况下,确实是get请求去请求了post请求响应的cookie。那么为什么经过nginx之后,get请求就没有这样去获取post响应的cookie呢?我查找‘nginx cookie’关键词,看到《解决nginx proxy_pass反向代理cookie,session丢失的问题》,了解到cookie_path要匹配请求的url才能获取到cookie,于是不能登录问题就能得到合理的解释:当post请求http://域名/字符串/XXX 时,经过nginx反向代理到http://域名:端口/项目名/XXX ,此时cookie_path是在项目名下(从post请求包头可以看到),然后get请求http://域名/字符串/YYY ,但是这条请求里面没有项目名,获取不到上面post请求响应的放在项目名目录下面的cookie,所以被判断为没有登录的状态,返回了登录页,同时get请求也生成了一个cookie放在了项目名目录下。

问题解决

第一种方案:修改cookie_path,nginx里面提供了proxy_cookie_path指令修改cookie_path,用法:proxy_cookie_path 原地址 修改地址 官网传送门
经过测试,修改地址只要满足请求url域名后的目录最左匹配规则都是可以的,比如我的请求是http://域名/a/b/c/d/xxx ,反向代理到http://域名:端口/E/b/c/d/xxx ,那么cookie_path修改地址可以设置为/,/a,/a/b,/a/b/c,/a/b/c/d,示例:

location /a/ {
    proxy_pass   http://127.0.0.1:8080/E/;
    proxy_cookie_path  /E /a;
}

第二种方案:修改请求地址,让请求地址和反向代理地址相匹配,如请求地址http://域名/字符串/XXX 被反向代理到 http://域名:端口/项目名/XXX ,那么可以修改请求地址为http://域名/项目名/XXX ,这样就不需要配置proxy_cookie_path。

参考:
解决nginx proxy_pass反向代理cookie,session丢失的问题

阅读 304

推荐阅读