目录
问题描述
这两天写接口给iOS和Android端联调,客户端在请求头写了一个Authorization传给后端,后端需要拿到Authorization换取登录的用户信息。
结果发现了一个非常诡异的问题:iOS端请求带的头Authorization后端可以正常取到,自己通过postman请求也能拿到,而唯独Android端传过来的Authorization头取不到!!
这时候一般就到了互相丢锅的时候了,客户端说后端有问题,吧啦吧啦。。。服务端说客户端有问题,吧啦吧啦。。。
问题定位
既然客户端觉得是服务端有问题,那就换台后端服务验证下呗(有问题的是测试环境),改成请求我本地接口服务,本地debug发现能拿到!!此时客户端同学更加坚信了是我后端的锅:你们的测试环境服务肯定有问题。。
好吧,我也开始怀疑自己了,那就对比测试环境和本地环境的,测试环境本身有nginx做转发,所以开始怀疑是不是nginx的锅,没把这个头转发到后端去。于是在nginx的日志加了一个参数$http_Authorization,观察nginx是否有拿到这个Authorization头。
修改日志格式生效之后的结果:
前面两条是安卓的请求记录,最后一条是postman请求的记录。很明显得出结论:在客户端发请求到nginx这一层头就丢了!不应该是nginx的问题。
此时的我只能冲到安卓端同学那里吧啦吧啦一波,意思就是说:你们发请求的代码是不是有问题。得知安卓端用的okhttp框架做的请求,那就逼着安卓同学一起看框架源码,定位最终发请求的时候头是否在。
看了很久,最后得出一个很关键的线索:客户端会请求两次,第一次请求后端,后端会返回一个301的响应给后端,接着客户端根据301的响应又会发起一次请求给后端。而第二次请求的时候果然Authorization头不见了!!
然后不停地跟踪request参数,最终找到一条非常关键的代码:
这行代码的大致意思就是说:如果经过重定向之后,重定向之后的url和原来第一次请求的url不一样,为了安全会把Authorization头删除掉!好吧,竟然还有这么诡异的代码!
强忍愤怒与激动之后,冷静下来思考:
1、为什么第一次请求会返回301?
仔细观察了前后两次请求的url,第一次访问的是http://xxxx,服务端响应301告诉客户端你要请求https,所以这就是这里为什么要返回301的原因(本身我后端nginx配置了所有http请求都要转成https再转发后端),所以第一次请求nginx,nginx会通过响应301,同时告诉https://xxxd地址给客户端,客户端通过https最终请求服务端。
2、为什么要删除掉这个头?
重定向如果到另外一个不安全的地址去,这个Authorization很敏感的内容岂不会被这个不安全的服务器拿到,好像是这么个道理?!but。。。这就是从http转成了https协议而已,为啥也要删除!浏览器怎么不删除?postman怎么不删除??
问题结论
所以,得出结论: 这是okhttp的bug!不应该把http变成https当成是不同的请求地址,针对这种情况不要做移除Authorization的操作。
问题解决
安卓端配置后端请求地址直接配置https地址就好,避免经过一次301的重定向。但是,关于okhttp这个bug如何避免还需要安卓端同学想个办法去解决。