在跨域 ajax 请求后保留 cookie

Keeping the cookie after a cross-domain ajax request

本文关键字:保留 cookie 请求 ajax      更新时间:2023-09-26
运行

10.0.0.1上的JavaScript应用程序尝试使用跨域ajax调用对其用户进行身份验证。

请求如下所示:

function test(again){
  $.ajax({
    type: 'GET',
    url: 'http://example.com/userinfo',
    dataType: 'json',
    success: function(userinfo){
      if(again)
        test(false);}});}
test(true);

来自服务器的第一个响应尝试设置 cookie:

Access-control-allow-origin:http://10.0.0.1
Set-Cookie:PHPSESSID=uuj599r4k1ohp48f1poobil665; expires=Sat, 28-Jan-2012 17:10:40 GMT; path=/

但是第二个请求不包括此cookie,也不包括对该域的任何其他ajax请求。

我不是在尝试读取另一个域的cookie,我只是希望另一个域上的应用程序能够设置和读取自己的cookie。

这可能吗?

我已经在Chrome和Firefox 9中进行了测试。

服务器应该设置标头:

response.Headers.Add("Access-Control-Allow-Credentials", "true");

客户端设置为:

xhrFields: {
  withCredentials: true
}

只要您使用支持 CORS 的浏览器,AJAX 请求上的 cookie 就应该可以工作。但是你必须把XMLHttpRequest withCredentials设置为真。

请参阅: withCredentials属性

我不使用 JQuery

,但这里有一个专门处理通过 JQuery 设置withCredentials的问题。

使用跨域帖子发送凭据?

不可以,Cookie 不能跨域共享。假设浏览器支持Access-Control-*标头,对于使用 AJAX 调用,可以规避同源策略,但对于 cookie,则没有办法。

+Darin Dimitrov怀疑"浏览器没有保存cookie,因为它来自另一个域,而不是托管此调用源页面的域"。

但是,当使用 JSONP 时,cookie 会根据需要进行设置,但 JSONP 仅适用于 GET 请求。

我的解决方案是通过在<script>中加载以下 php 文件来检索 cookie(PHP 会话 ID(:

<? echo $_GET['callback'] . '("' . session_id() . '")'; ?>

并将会话 ID 作为请求变量传递到所有跨域 POST 请求中。

我需要使用 AJAX 和 PHP 将 cookie 从多个子域传递到单个 API 域。

这是挑战和解决方案:

1 - api.example.com 上的后端 PHP。

2 - 多个 JS 前端,如 one.example.com、two.example.com 等。

3 - Cookie 需要双向传递。

4 - 从多个前端到 PHP 后端的 AJAX 调用 api.example.com

5 - 在PHP中,我不喜欢使用$_SERVER["HTTP_ORIGIN"],在我看来并不总是可靠/安全的(我有一些浏览器的HTTP-ORIGIN总是空的(。

在具有单前端域的 PHP 中执行此操作的正常方法是使用以下方式启动 PHP 代码:

header('Access-Control-Allow-Origin: https://one.example.com');  
header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token');  
header('Access-Control-Allow-Credentials: true');  

在 one.example.com 域上的JS中:

jQuery.ajax({
    url: myURL,
    type: "POST",
    xhrFields: {withCredentials: true},
    dataType: "text",
    contentType: "text/xml; charset='"utf-8'"",
    cache: false,
    headers: "",
    data: myCallJSONStr,
    success: function(myResponse) {.....}

但是,这不可行,因为我使用多个子域来调用我的 API 域。

并且此解决方案将不起作用,因为我想传递cookie:

header('Access-Control-Allow-Origin: *');  

它与JS网站上的传递cookie设置冲突:

xhrFields: {withCredentials: true}

这是我所做的:

1 - 使用 GET 参数传递子域。

2 - 在PHP中对主域进行硬编码,以便只允许(所有(子域。

这是我的解决方案的 JS/JQuery AJAX 部分:

函数 getSubDomain(({

let mySubDomain = "";
let myDomain = window.location.host;
let myArrayParts = myDomain.split(".");
if (myArrayParts.length == 3){
    mySubDomain = myArrayParts[0];
}
return mySubDomain;

}

在 AJAX 调用中:

let mySubDomain = getSubDomain();
if (mySubDomain != ""){
    myURL += "?source=" + mySubDomain + "&end"; //use & instead of ? if URL already has GET parameters
}
jQuery.ajax({
    url: myURL,
    type: "POST",
    xhrFields: {withCredentials: true},
    dataType: "text",
    contentType: "text/xml; charset='"utf-8'"",
    cache: false,
    headers: "",
    data: myCallJSONStr,
    success: function(myResponse) {.....}

最后,PHP部分:

<?php
$myDomain = "example.com";
$mySubdomain = "";
if (isset($_GET["source"])) {
    $mySubdomain = $_GET["source"].".";
}
$myDomainAllowOrigin = "https://".$mySubdomain.$myDomain;
$myAllowOrigin = "Access-Control-Allow-Origin: ".$myDomainAllowOrigin;
//echo $myAllowOrigin;
header($myAllowOrigin);  
header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token');  
header('Access-Control-Allow-Credentials: true');

重要提示,不要忘记为所有子域设置 cookie,在这种情况下,cookie 的域将是:.example.com(因此在主域前面有一个点(:

<?php
    //////////////// GLOBALS /////////////////////////////////
    
    $gCookieDomain = ".example.com";
    $gCookieValidForDays = 90;
    
    //////////////// COOKIE FUNTIONS /////////////////////////////////
    
    function setAPCookie($myCookieName, $myCookieValue, $myHttponly){
        global $gCookieDomain;
        global $gCookieValidForDays;
        
        $myExpires = time()+60*60*24*$gCookieValidForDays;
        setcookie($myCookieName, $myCookieValue, $myExpires, "/", $gCookieDomain, true, $myHttponly);   
        
        return $myExpires;
    }

此解决方案允许我从 example.com 上的任何子域调用 api.example.com 上的 API。

注意:对于只有一个调用子域的情况,我更喜欢使用 .htaccess 来设置 CORS 而不是 PHP。以下是仅 one.example.com 调用 api.example.com 的.htaccess(linux/apache(示例:

<IfModule mod_headers.c>
    Header set Access-Control-Allow-Origin "https://one.example.com"
    Header set Access-Control-Allow-Headers "Origin, Content-Type, X-Auth-Token"
    Header set Access-Control-Allow-Credentials "true"
</IfModule>

并将此 .htaccess 放在 api.example.com 的根目录中。