最近发现一个问题。

在做一个前后端分离的项目中,Safari浏览器下,用户登录始终不成功。在chrome下又是正常的。不论是fetch 还是XHR(Ajax)。

抓包一看,两个浏览器发出的请求,也都响应的成功.

这就奇怪了,参数完全一致,响应完全一致,登录状态确不一致,难不成问题出在了cookie上?开玩笑怎么可能..

cookie上的问题?

仔细一查果然出问题了

POST|/login.do HTTP/1.1
Host|119.23.29.194
Accept /
Accept-Encoding gzip, deflate
Accept-Language zh-cn
Content-Type application/json
Origin http://ifml.me
User-Agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8
Connection keep-alive
Referer http://ifml.me/
Content-Length 62
Cookie JSESSIONID=70FA6B08BXXXXXXXXXXXXXXXXX033490E6C6
Pragma no-cache
Cache-Control no-cache

在Safari中 Cookie 压根就没提交,这就奇怪了,命名我在fetch()中做了设置啊

    let promise = fetch(url, {
        method: 'POST',
        body: JSON.stringify(param),
        mode: 'cors',credentials: 'include'// 带上认证信息
     })

难道是fetch()方法不被Safari支持?

/// cosole
> fetch
<· function fetch() {
    [native code]
}

明明就有啊,不管了,换jQuery试试

let req = new Promise(function (resolve, reject) {
    $.ajax({
      url: url,
      type: "post",
      success: function(data){
        resolve(data)
      },
      error:function(data){
        reject(data)
      },
      data: JSON.stringify(param),
      xhrFields: {withCredentials: true},// here
      crossDomain: true, // here
      contentType: "application/json"
    });

然后再试,仍然没有带上cookie ,这就尴尬了啊,不符合逻辑啊,前一个版本还是好好的,做完前后端分离就这样了?

等等,前后端分离?

前后端分离下cors要注意的地方

赶紧抓包一看,尴尬,果然是分离的锅,问题是这么来的

最开始项目用jsp来做,根本不存在这个问题
后来进行了前后端分离,但还是打包在同一个war包下,依然没有问题
再后来进行了包拆分,前端包被扔到了另一台服务器,于是问题就来了

所以这个问题就有这么几个条件
– 页面地址和接口地址不在同一个域下
– Safari在10.1的版本
– 跨域方式为cors模式

其实,是不是只限于cors我还没验证,但是就这个BUG会带来几个问题
1. 在Safari中,CDN过来的页面,其接口发送状态不再稳定
2. 在全面采用Safari的ios手机中,使用chrome进行调试不再可靠
3. 除非用户进行浏览器设置,否则,页面和接口分离将始终有此问题

对于第3点的具体设置操作如下
设置

在Safari的隐私设置中,Cookie和网站数据一项,设置为始终允许即可解决这个问题,因为默认是倒数第三项。

但是我们都知道,不可能这么做,原因有二:
1. 你不能要求用户去修改自己的设置来兼容你的页面
2. 这项设置属于安全性设置(具体请参考CSRF攻击),修改可能会造成一些未知隐患。

其实还是要回到用户体验上,那解决办法就只有两个
1. 将html和接口放到同一个服务器下(开什么玩笑)
2. 将html和接口放到同一个域下(用NGIX做反向代理)

问题的解决

是的,也只能用nginx来解决了,其实我反而觉得这更简单,现在大部分公司的主域名绑定的也的确是nginx。
1. 在主域名下,要么以下级域名的形式,要么以path的形式来组织静态页
2. 然后以REST的形式组织web api的地址。
3. 静态页对接口的访问全部都通过nginx。

这样一来,因为静态页本身就在nginx下,其对接口的访问也就相当于在同域了,其他接口也不过是通过nginx做了下透传。但完成了同域。

其实说这么多,提到的也都是一些老的东西,无非也就是对Safari这项设置的不满,因为我记得以前还不是这样的,莫名奇妙就来了这项设置。

ps:
将html和web api放到同一个服务器下为什么不可行,以为平台化和服务化,很多公司的业务规模已经不单单是几个web service能解决的了,更多的是服务化(关注点分离)的形式,和分布式来部署业务。

CORS 跨域模式下,Safari浏览器默认无法携带cookie提交,以及处理建议
Tagged on:     

发表评论

电子邮件地址不会被公开。 必填项已用*标注