背景
最近做了个项目,大致流程是这样的:微信中打开A页面,判断是否登陆,未登录的话跳转至微信授权并设置登陆cookie信息,然后重定向回A页面。QA提了个bug,打开页面后一直在授权地址和A页面之间循环跳转。上去找QA看了下现场,发现确实是这样,而且只有iPhone 6 plus会这样,其他机型正常,捕获了授权链接等等也都是没有问题。让QA把链接发我就灰溜溜的下楼了……然而,下楼的过程中,灵光一闪突然想到了日期的问题(有时候找bug真的要看运气,要是没想到时间问题,真的不知道要耗多长时间……),会不会是那台机器时间设置有问题?立马问了QA,果然,时间设置到了2018年3月1号……一切都真相大白了。
原因
原因很简单,授权完毕后设置登陆信息cookie,过期时间是按照服务器的正常时间进行设置的,而那台机器的系统时间设置到了2018年的3月1号,因此会导致cookie信息过期,从而一直在授权页面之间跳转。
复现
本着负责的态度,打算写个demo验证下上述现象。
1.服务端代码
服务端代码很简单,就是设置cookie而已。
if (!isset($_COOKIE['name'])){
setCookie('name','111111',time()+300,'/');
}
var_dump($_COOKIE);
2.客户端表现
设置系统时间为一个月之后,然后访问localhost/cookie.php,输出如下:
cookie信息如下:
cookie设置成功,这里没有任何问题。
理论上来说,再次刷新浏览器,输出应该也是没有信息的,因为cookie过期了,然而事实却不是这样:
whatttttt??? 为什么cookie还在?
静下来想了想,发现了问题:我是用本机作为服务端,那么时间也是经过修改的,这样cookie是没有过期的。
第二次尝试,在虚拟机上搭建服务器,虚拟机上的时间是正常时间,然后访问虚拟机,
重复第一次的步骤,然而惊讶的发现……居然cookie还在!!!
查看响应头信息:
可以看到,cookie的过期时间是2018年2月2日,然而系统时间已经调到了2019年……
又试着改了下系统时间,然后刷新页面,发现cookie信息没了!然而再次刷新浏览器,cookie信息又出现了,并且刷新多次也还是一直在。再次更改系统时间,cookie信息消失了……
也就是说,只有种下cookie之后更改系统时间才会生效,在种cookie之前更改系统时间是没有效果的!
不信邪的我又换了Firefox、Opera等浏览器,结果同chrome一致。直到我换了IE,cookie信息一开始就不在了……
想来想去不明白是为什么?不由得对浏览器判断cookie过期的机制有点兴趣,查阅了一些文档之后终于知道为什么了。
原来,在HTTP/1.0的时候,cookie的过期是根据expires来判断的,在HTTP/1.1的时候,变成根据Max-Age来判断了。关于这两者的区别,简单来说是这样:
- expires是设置一个过期的时间点;Max-Age是设置还有多久过期。类似于相对路径和绝对路径的关系。
详细对比推荐看这篇文章:HTTP Cookies: What’s the difference between Max-age and Expires?
可以看出来,Max-Age是与系统时间无关的,不管系统时间设置的是什么时候,都是相对于系统时间一段时间之后过期。
chrome、Firefox等现代浏览器是支持Max-Age的,因此在种cookie之前,更改系统时间,是没有关系的,cookie会仍然在。然而种下cookie之后再更改系统时间,这个时候,浏览器判断当前时间减去种cookie的时间差大于Max-Age了,因此cookie就失效被删除了。
IE不支持Max-Age,因此,在种cookie之前修改系统时间,在判断过期的时候根据系统时间大于expires设置的时间点,cookie自然就被判过期从而被删除了。
到这里,还剩最后一个疑点:按照以上的说法,应该是微信不支持Max-Age选项,可是经过测试,实际上是支持的,那又是怎么回事呢?
最后终于找到了元凶:php的版本太低,设置cookie的时候没有Max-Age!!!php从5.5开始设置cookie的时候会有Max-Age,在5.5之前是没有Max-Age的,如下图:
所以只能通过expires来判断,所以一切都真相大白了。
总结
- 通过修改系统时间,可以更改cookie的存活时间
- expires和Max-Age都可以用来设置cookie过期信息,同时存在且均支持时以Max-Age为准。