QQ返利(http://fanli.qq.com)跟elong(http://www.elong.com)合作开发的联合登录方案在进行联合调试的过程中发现了一个奇怪的现象:同一个qq号码多次跳转到elong的站点,有的时候发现elong侧的登录态可以置上,而有的时候却发现根本无法设置登录态。
通过httpwatch进行抓包,发现能够正常登录的请求系列是:
302 http://app.elong.com/fanli/qq_login.php?xx
200 http://trip.elong.com/home/passport.php?xx
200 http://www.elong.com/myelong/lohoologin.aspx?xxx
这个跳转序列都是正常的返回码:302和200;
但偶尔的跳转的请求系列是:
302 http://app.elong.com/fanli/qq_login.php?xx
200 http://trip.elong.com/home/passport.php?xx
aborted http://www.elong.com/myelong/lohoologin.aspx?xxx
说明最后的一个请求在服务器还没返回的时候就已被浏览器abort掉了,跟elong的朋友沟通后得知,lohoologin.aspx是设置登录态的核心部分,如果该aspx无法正常设置则用户就无法正常登录。
这里已经基本找到问题的原因了,那就是最后的请求lohoologin.aspx是被abort掉导致的,那么,究竟是什么原因导致这个请求无法正常发送呢,由于lohoologin.aspx是由passport.php发出的,看看passport.php的响应就应该明白了:
<iframe src=http://www.elong.com/myelong/lohoologin.aspx?xxxx></iframe>
<script>
setTimeout('window.top.location="http://fanli.qq.com/go.php?xxx"',3000);
</script>
也就是说passport.php首先通过iframe发出请求lohoologin.aspx的请求,然后等待3s的时间自己重定向到http://fanli.qq.com/go.php?xxx
这里的逻辑假设是:lohoologin.aspx是可以在3s内返回的。在网络条件比较好的情况下,这里的逻辑确实是没有问题的,但,我们所处的网络环境是很恶劣的,在一定程度上我们是没办法保证该aspx在3s内可以返回的,一旦3s无法返回,而setTimeout就会执行:window.top.location="http://fanli.qq.com/go.php?",从而把当前的页面进行重定向,而又当前页面发出的http请求但还未返回的就会被浏览器abort掉,一旦lohoologin.aspx无法响应,就无法在浏览器设置登录态的cookie,当然也就无法实现联合登录了。
至此,我们可以认为问题的原因已经“水落石出”了。但tenfy认为目前还是属于用自己的知识推理出来的原因,为了进一步的证实这个结论,tenfy在自己的开发环境写了两个测试代码:
testAbort.html:
<body><iframe id="loginIframe" src="testAbort.php" mce_src="testAbort.php" ></iframe> <script type="text/javascript"> <!-- function _redirect(){ window.location.href ='http://fanli.qq.com'; }; setTimeout(_redirect,3000); // --> </script></boby>
testAbort.php:
<?php sleep(5); echo "hello"; ?>
很简单,主要测试在iframe请求的testAbort.php sleep 5s,使得testAbort.html在超过3s后被重定向,此时抓包可以看到:
+ 0.000 0.019 708 954 GET 200 text/html http://fanli2.qq.com:13601/test/testAbort.html
+ 0.073 3.152 714 0 GET (Aborted) * http://fanli2.qq.com:13601/test/testAbort.php
这里可以证明:之前的实现确实因为iframe里面的返回时间大于3s,导致请求被浏览器abort掉。
问题的原因比较清楚后,解决方案就比较简单了:
把重定向的时机不由setTimeout决定,而是由iframe里面的响应onload后再执行重定向就可以避免这个问题:
var iframe = document.getElementById('loginIframe'); if(iframe){ iframe.src='testAbort.php'; if(iframe.attachEvent) { iframe.attachEvent("onload", function(){ _redirect(); }); } else { iframe.onload = function(){ _redirect(); }; } }