微信第三方登陆API在IE6上的bug
一句话总结:IE6在收到302响应后,在转发请求到服务器时,会把url中的hash fragment一并发送服务端,而微信open api服务器处理url时没过滤hash fragment导致出现错误。
一、Bug描述
点击爱奇艺主站登陆面板的微信第三方登陆按钮,浏览器会打开新的窗口访问http://passport.iqiyi.com/oauth/login.php?type=29&isiframe=1&_pos=1&agenttype=1,接着新开的窗口就会302跳转到https://open.weixin.qq.com/connect/qrconnect?scope=snsapi_login&response_type=code&redirect_uri=http%3A%2F%2Fpassport.iqiyi.com%2Fapis%2Fthirdparty%2Fwxcallback.action%3Ffrom%3D29&state=2ed3ea2b0cfaaf90c9c3da057e7cbb4f&appid=wx228cef5813cc7961#wechat_redirect。在chrome/firefox/safari/IE8+浏览器上都能正常显示出二维码图片,但在IE6上新开的窗口中显示的是“AppID错误”(是open.weixin.qq.com服务器返回的错误页面),再手动刷新错误页面时,又会显示出正常的扫码页面。


二、导致Bug的原因
问题出在从http://passport.iqiyi.com/oauth/login.php?type=29&isiframe=1&_pos=1&agenttype=1跳转到https://open.weixin.qq.com/connect/qrconnect?scope=snsapi_login&response_type=code&redirect_uri=http%3A%2F%2Fpassport.iqiyi.com%2Fapis%2Fthirdparty%2Fwxcallback.action%3Ffrom%3D29&state=2ed3ea2b0cfaaf90c9c3da057e7cbb4f&appid=wx228cef5813cc7961#wechat_redirect时候。
对于正常的浏览器,在收到passport.iqiyi.com返回302响应时,发送到微信open.weixin.qq.com服务器的url中是不携带hash部分,即请求行中不包含#wechat_redirect
。
GET /connect/qrconnect?scope=snsapi_login&response_type=code&redirect_uri=http%3A%2F%2Fpassport.iqiyi.com%2Fapis%2Fthirdparty%2Fwxcallback.action%3Ffrom%3D29&state=38b7c1ca03d9396f741bc915eb123af3&appid=wx228cef5813cc7961 HTTP/1.1
但在IE6上,它在收到passport.iqiyi.com返回302响应时,发送到微信open.weixin.qq.com服务器的url中是携带了hash部分,即请求行中包含#wechat_redirect
。
GET /connect/qrconnect?scope=snsapi_login&response_type=code&redirect_uri=http%3A%2F%2Fpassport.iqiyi.com%2Fapis%2Fthirdparty%2Fwxcallback.action%3Ffrom%3D29&state=38b7c1ca03d9396f741bc915eb123af3&appid=wx228cef5813cc7961#wechat_redirect HTTP/1.1
而open.weixin.qq.com服务器在处理IE6发过去的请求的url时,将hash作为查询字符串的一部分,把request的参数appid的值当做wx228cef5813cc7961#wechat_redirect
进行处理,从而导致了返回“AppID错误”。
三、Bug修复方法
这个是微信open api的bug(被奇葩IE6给撞上了),但是今天我们这边着急上线,就hack了一下,我们的passport.iqiyi.com服务器在返回302响应时,给跳转的url中私自加了一个参数splite,在appid=wx228cef5813cc7961和hash字符串#wechat_redirect之间,目的是避免微信open.weixin.qq.com服务器把hash值#wechat_redirect算作appid值的一部分,
即Location头的内容是:https://open.weixin.qq.com/connect/qrconnect?scope=snsapi_login&response_type=code&redirect_uri=http%3A%2F%2Fpassport.iqiyi.com%2Fapis%2Fthirdparty%2Fwxcallback.action%3Ffrom%3D29&state=5952f0ff13aa977322f074b811480484&appid=wx228cef5813cc7961&splite#wechat_redirect
其实最简单的是把跳转url中的hash部分给去掉,但我们这边passport的同事说微信那边不同意,所以就这么搞了。
四、Bug排查过程
在IE6上的bug是上周四QA组同事报过来的。我验证了一下,发现可能根本不是我们这边前端JS的问题。就到网上找其他使用微信第三方登陆功能的网站,如果他们的微信登陆功能在IE6上是OK的,那说明是iQiYi的问题,否则就是微信open api方的问题。
初步找了一下,只发现美丽说和蘑菇街有微信第三方登陆的功能,但他们的都是在当前页面就跳转到微信二维码页面了(扫码登陆成功再跳转回去),而我们这里是在当前页面弹窗显示微信二维码页面(扫码成功关闭弹窗)。无法用他们的网站来验证是否是我们的问题。
向直接主管求助,主管把弹窗打开的url: http://passport.iqiyi.com/oauth/login.php?type=29&isiframe=1&_pos=1&agenttype=1放到正常IE6新窗口中访问,最终微信open api服务器返回AppID错误。而把最终跳转到的url:https://open.weixin.qq.com/connect/qrconnect?scope=snsapi_login&response_type=code&redirect_uri=http%3A%2F%2Fpassport.iqiyi.com%2Fapis%2Fthirdparty%2Fwxcallback.action%3Ffrom%3D29&state=5952f0ff13aa977322f074b811480484&appid=wx228cef5813cc7961#wechat_redirect放到正常IE6新窗口中访问,微信open api服务器就返回正常的扫码页面。
主管的推断是:passport服务器和微信open api服务器之间的问题,让passport的同事去排查,可能是跨域cookie没种上,是不是P3P header没设置对。
我把bug和主管的建议都转给passport组的同事。
今天passport组的同事说即便设置了P3P header也不行,何况他在响应里面根本没有返回set-cookie的header。
我用fiddler抓包看了一下,发现到open.weixin.qq.com的请求的WebForms标签中appid参数的值是wx228cef5813cc7961#wechat_redirect:
这下觉察到应该是url中的hash导致出问题了。
最后自己用nodejs启动三个http服务器进程验证了下:
var http = require("http"); //不带hash的302跳转url var url="https://open.weixin.qq.com/connect/qrconnect?scope=snsapi_login&response_type=code&redirect_uri=http%3A%2F%2Fpassport.iqiyi.com%2Fapis%2Fthirdparty%2Fwxcallback.action%3Ffrom%3D29&state=38b7c1ca03d9396f741bc915eb123af3&appid=wx228cef5813cc7961"; //hash和appid分割开的302跳转url var url2="https://open.weixin.qq.com/connect/qrconnect?scope=snsapi_login&response_type=code&redirect_uri=http%3A%2F%2Fpassport.iqiyi.com%2Fapis%2Fthirdparty%2Fwxcallback.action%3Ffrom%3D29&state=38b7c1ca03d9396f741bc915eb123af3&appid=wx228cef5813cc7961&sx#wechat_redirect"; //出问题的302跳转url var url3="https://open.weixin.qq.com/connect/qrconnect?scope=snsapi_login&response_type=code&redirect_uri=http%3A%2F%2Fpassport.iqiyi.com%2Fapis%2Fthirdparty%2Fwxcallback.action%3Ffrom%3D29&state=38b7c1ca03d9396f741bc915eb123af3&appid=wx228cef5813cc7961#wechat_redirect"; http.createServer(function(req,res){ res.statusCode=302; res.setHeader("Location",url); res.end(); }).listen(10001); http.createServer(function(req,res){ res.statusCode=302; res.setHeader("Location",ur2); res.end(); }).listen(10002); http.createServer(function(req,res){ res.statusCode=302; res.setHeader("Location",url3); res.end(); }).listen(10003);
最后在虚拟机里面的IE6上分别访问http://10.1.227.91:10001/、http://10.1.227.91:10002/和http://10.1.227.91:10003/,发现前两请求都能拿到正常的扫码页面,第三个请求就重现了“AppID错误”页面。
fiddler抓包,发现302跳转的请求的Request Line中GET后面的uri中包含了查询字符串:
url中的hash不应该随请求发送到服务器的,ie6 在redirect时候冒泡了,不过微信open api服务器确实应该在处理请求时校验下url是否包含hash字符串。
五、总结
既然是IE6的bug,那早应该就被人发现记录过。
这篇文章记录了这个场景,不过作者的建议是,在给302响应时,Location中不要带hash fragment。
另外一篇也描述了同样的问题,最后的解决办法和我们这次所用的大体相同:他们只用一个&
把hash和uri的其他部分分开。
hash fragment与302 redirection相关的HTTP标准/草案:Handling of fragment identifiers in redirected URLs。而主流浏览器的实现情况可以看这里:URL Fragments and Redirects。
暂完。
blog comments powered by Disqus