66

我必须从域 B.com 调用域 A.com(它使用 http 设置 cookie)。我在域 B.com 上所做的只是(javascript):

var head = document.getElementsByTagName("head")[0];
var script = document.createElement("script");
script.src = "A.com/setCookie?cache=1231213123";
head.appendChild(script);

这会在我测试过的每个浏览器(Safari 除外)上设置 A.com 上的 cookie。令人惊讶的是,即使没有 P3P 标头,这也适用于 IE6。

有什么办法可以在 Safari 中完成这项工作吗?

4

18 回答 18

61

来自Safari Developer FAQ

Safari 附带了一个保守的 cookie 策略,该策略将 cookie 写入仅限于用户选择(“导航到”)的页面。此默认保守策略可能会混淆尝试写入 cookie 并失败的基于框架的网站。

我发现没有办法解决这个问题。

如果值得的话,如果您使用<script> 附加方法,Chrome 也不会设置 cookie,但如果您有一个<img具有相同来源的隐藏 >,Chrome 可以与其他浏览器一起工作(再次,Safari 除外)

于 2009-01-28T05:29:29.813 回答
13

这是一个有效的解决方案:

http://anantgarg.com/2010/02/18/cross-domain-cookies-in-safari/

于 2010-02-18T12:30:19.007 回答
12

这可能不适用于所有人,但我遇到了这个问题,因为我从与 API 不同的主机提供 React 应用程序,最终可行的解决方案是使用 DNS:

我们的客户从 www.company-name.com 获得服务,我们的 API 在 company-name.herokuapp.com 上。通过创建CNAME记录 api.company-name.com --> company-name.herokuapp.com,并让我们的客户使用该子域进行 API 调用,Safari 不再将其视为“第三方”cookie。

好处是涉及的代码很少,而且都使用了成熟的东西......缺点是如果您要使用 https,您需要对 API 主机进行一些控制/所有权 - 他们需要一个有效的证书对于客户端域,否则用户将收到证书警告 - 因此,如果有问题的 API 不是您的或合作伙伴的,这将不起作用(至少不适用于面向最终用户的东西)。

于 2018-11-05T15:50:34.987 回答
7

2014-2016工作方法:

您必须对域执行 window.open / 分配 cookie / 关闭弹出窗口,该域现在已列入安全列表。

原帖@PHP 多个 cookie 在 iPad / iPhone 浏览器上不起作用

于 2014-11-17T06:39:04.037 回答
4

假设他们安装了闪存,那就有点邪恶了。

我不确定它是否仍然有效,但 Flash 的“本地共享对象”又名Flash Cookie可以帮助您绕过 Safari 的同域策略。

本地共享对象教程

但是,至少可以说,实施起来可能很复杂。

此外,LSO 正在成为一场安全噩梦:

所以在使用它们之前要仔细考虑。

于 2009-02-04T13:09:14.603 回答
3

隐藏的帖子<iframe>可以让您绕过 Safari 中的此限制 - http://gist.github.com/586182

<?php
  header('P3P: CP=HONK');
  setcookie('test_cookie', '1', 0, '/');
?>
<div id="test_cookie" style="position: absolute; top: -10000px"></div>
<script>
  window.setTimeout(function() {
    if (document.cookie.indexOf('test_cookie=1') < 0) {
      var      
        name = 'test_cookie',
        div = document.getElementById(name),
        iframe = document.createElement('iframe'),
        form = document.createElement('form');

      iframe.name = name;
      iframe.src = 'javascript:false';
      div.appendChild(iframe);

      form.action = location.toString();
      form.method = 'POST';
      form.target = name;
      div.appendChild(form);

      form.submit();
    }
  }, 10);
</script>
于 2010-10-01T07:51:38.503 回答
3

在 2015 年有一个适当的解决方法。假设有一个网站 y.com,其中包括 iframe 和网站 x.com。x.com iframe 想要存储一个 cookie。Safari 政策不允许这样做,但是 y.com 可以存储它。所以 y.com 必须监听来自 x.com 的消息,然后存储 cookie 本身。

var _cookieEvMth = window.addEventListener ? "addEventListener" : "attachEvent";
var _cookieEvAction = window[_cookieEvMth];
var _cookieEv = _cookieEvMth == "attachEvent" ? "onmessage" : "message";
_cookieEvAction(_cookieEv, function(evt){
  if(evt.data.indexOf('cookieset')!=-1){
    var datack = evt.data.split('|');
    YOUR_CUSTOM_COOKIE_SAVE_METHOD(datack[1],datack[2],datack[3]);
  }
},false);

当 x.com 需要存储 cookie 时,它​​必须向 y.com 发布消息:

window.parent.postMessage('cookieset|'+ckName+'|'+ckVal+'|'+days,'*');

如果您想阅读 cookie,您也可以按照自己的方式将消息发布到 iframe。或者您可以使用 javascript 将其作为参数包含在 x.com iframe url 中:

iframe.setAttribute('url','x.com/?cookieval='+YOUR_COOKIE_GET_METHOD('cookiename'));
于 2015-06-12T13:05:26.687 回答
2

我们刚刚在我的工作中提出的一个解决方法是通过 window.open() 设置 cookie - 它可能对你来说不是最佳的(因为你会打开一个丑陋的屁股弹出窗口),但它对我们很有效. 我们必须为 OAuth 身份验证打开一个弹出窗口。

所以我们所做的主要是:

  1. 用户点击来自 B.com 的链接
  2. 弹出窗口打开到 A.com/setCookie
  3. A.com 设置它的 cookie,然后在适当的位置重定向到 B.com

同样,并非在所有解决方案中都有效,但它在我们的解决方案中有效。希望这可以帮助。

于 2009-05-08T19:53:24.653 回答
2

我知道这个问题相当老了,但这帮助我解决了 cookie 问题:

var cookieForm = document.createElement("form");
cookieForm.action = "A.com/setCookie?cache=1231213123";
cookieForm.method = "post";
document.body.appendChild(cookieForm);

cookieForm.submit();

在设置 cookie 的页面上发布表单的想法。

于 2010-07-27T08:57:45.650 回答
1

*编辑* 此解决方法已报告在 WebKit 中关闭。

卢卡,

好的,所以这个答案已有两年历史了,但是......如果您将表单发布到隐藏的 iframe,您可以从 iframe 设置 cookie。您可以通过创建一个表单来做到这一点:

<form id="myiframe" action="http://yourdomain.com" method="POST" target="iframe_target">

然后在 Javascript 中,获取对表单的引用并调用提交:

document.getElementsByTagName('form')[0].submit();

您可以收听 iframe 的 onload,或者您可以让 iframe 操作页面发出一些 javascript 来发出负载信号。我已经在 Safari 和 Chrome 中对此进行了测试,并且可以正常工作。

干杯。

于 2011-01-26T02:03:51.997 回答
0

也许务实地创建并单击带有href="A.com/setCookie?cache=1231213123"指向隐藏 iframe 的目标属性的链接。这可能会绕过 Safari 设置 cookie 的用户导航策略(我没有 Safari 方便测试。)

于 2009-01-28T05:50:09.290 回答
0

当我试图部署一个使用 Windows Live ID 的站点时,我对此进行了一些广泛的调查,这取决于能够设置 3rd 方 cookie 以注销的能力。只是……没用。我们无能为力。Live ID 团队也进行了广泛的调查,他们的回答是“不能让它工作”。

于 2009-02-04T05:55:47.677 回答
0

注意这一行:

script.src = "A.com/setCookie?cache=1231213123";

在我添加 http 之前,我无法让它工作,即

script.src = "http://A.com/setCookie?cache=1231213123";
于 2009-07-28T01:24:11.930 回答
0

我找到了一个简单的解决方案。您只需要第一次设置 cookie 来检查请求是否来自同一个来源,如果不是像往常一样,您需要向 iframe 返回一个将重复此请求的脚本,已经有权分配 cookie。之后,您可以直接通过 iframe 访问此 cookie 进行其他请求。这对我的跟踪系统有所帮助。试试看,这个效果很好。

于 2016-01-29T22:15:05.347 回答
0

值得注意的是,Safari 中的此限制不适用于子域。因此,如果您直接访问 sitea.com,那么您可以从 subdomain.sitea.com 设置 cookie,而无需直接用户交互 (iframe/JavaScript)。

这与我在开发 API 时的情况有关。如果您的访问者正在访问 mysite.com,然后您希望一些 JavaScript 与您的 API 进行交互,那么如果 API 托管在 api.mysite.com,那么它将在 Safari 上运行。

于 2018-06-08T11:39:01.730 回答
0

将此 JavaScript 放在发出跨域请求的页面http://example1.com/index.html上:

  <script>
  var gup = function(name, url) {
     if(!url) url = location.href;
     name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
     var regexS = "[\\?&]"+name+"=([^&#]*)";
     var regex = new RegExp( regexS );
     var results = regex.exec( url );
     return results == null ? null : results[1];
  }
  var isSafari = navigator.vendor && navigator.vendor.indexOf('Apple') > -1 && navigator.userAgent && !navigator.userAgent.match('CriOS');
  var n = gup("activated");
  if(isSafari && n == null) {
     //browser is Safari and cookies have not yet been activated
     var current_url = location.protocol + '//' + location.host + location.pathname;
     var query_string = '?callback=' + encodeURIComponent(current_url + '?activated=1');
     var new_url = 'http://example2.com/activate.php' + query_string;
     window.location.href = new_url;
  }
  //the rest of your code goes here, and you can now set cross-domain cookies on Safari
  </script>

然后在另一台服务器上创建一个需要设置cookies的文件,http ://example2.com/activate.php :

  <?php
  if(isset($_GET['callback'])) {
     header('Location: '.$_GET['callback']);
     exit();
  } else {
     //in case callback param is not set, simply go back to previous page
     echo "<script>";
     echo "window.history.back();";
     echo "</script>";
     exit();
  }
  ?>

这是它的工作原理:

  1. 当第一次访问http://example1.com/index.html时,会检查浏览器是否为 Safari,以及名称“激活”的 GET 参数是否不存在。如果满足这两个条件(这将在 Safari 浏览器的第一次访问时发生),则浏览器将重定向到http://example2.com/activate.php并带有 GET 参数“回调”,其中包含附加的调用 URL带有“激活”参数。

  2. http://example2.com/activate.php只是重定向回 GET 参数“回调”中包含的 URL。

  3. http://example1.index.html在被重定向到后第二次被点击时,GET 参数“激活”现在将被设置,因此步骤 1 中的条件将不会执行,从而允许脚本继续执行。

这满足了 Safari 的要求,即让浏览器至少访问第 3 方域一次以开始设置 cookie。

于 2018-08-13T20:12:59.577 回答
-2

尝试类似:

var w = window.open("A.com/setCookie?cache=1231213123");
w.close();

它可能会绕过 Safari 的安全策略。

于 2009-02-02T16:16:03.543 回答
-13

这不是让您烦恼的缺少的类型属性吗?-)

<script type="text/javascript">
  var head = document.getElementsByTagName("head")[0];
  var script = document.createElement("script");
  script.setAttribute("type","text/javascript");
  script.src = "A.com/setCookie?cache=1231213123";
  head.appendChild(script);
</script>
于 2009-01-03T11:31:28.767 回答