背景
事情是这样的,公司以前开发了一套系统,但是因为国家政策原因下线了,时隔两年,现在可以重新上线了,但是只有代码,相关环境的配置是没有的。在重新上线的过程中,遇到了系统无法登陆的问题。这套系统架构是这样的:nginx 作为 web 接入层,数据处理会反向代理到 tomcat 上跑的 java 服务。一开始我怀疑是不是代码逻辑有问题,但是开发称代码是没问题的,为此,开发还将获取数据请求跳过 nginx 直接抛给 Tomcat,结果就真的没问题了。但是实际生产中是不能这样跑的,因为这样会暴露处理数据的 Tomcat。所以只能在 nginx 这里找问题。
排查过程
现象是这样的,登录的时候会成功进入系统,但是会立刻跳回登录页面。登录的过程会有一条 post 请求将登录信息上传给系统,还有一条 get 请求获取登录状态,但是我不清楚获取登录状态的逻辑是怎样的,判断条件是什么,本想让开发看一下 代码逻辑的,但是开发也看不出个所以然来。接着我看了 post 和 get 请求的包头信息,包头信息如下:
post 请求包头信息
get 请求头区别
然而仅仅看这两个包头信息我看不出什么异常,所以我让开发将代码改为直接跳过 nginx 可以登录的情况,我想对比能登录和不能登录的情况下有什么区别,以下是能登录的情况下的包头信息:
post 请求包头信息
get 请求头区别
对比了两种情况的包头信息,我发现了一个区别(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 信息
不能登录情况下 get 请求的 cookie 信息
可以登录情况下 post 请求的 cookie 信息
可以登录情况下 get 请求的 cookie 信息
可以看到的出来,可以登录的情况下,确实是 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 丢失的问题