在跨域ajax请求之后保留cookie

时间:2012-01-13 17:39:37

标签: php javascript jquery ajax

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中测试过。

5 个答案:

答案 0 :(得分:11)

服务器应该设置标题:

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

客户端设置为:

xhrFields: {
  withCredentials: true
}

答案 1 :(得分:10)

只要您使用支持CORS的浏览器,AJAX请求上的cookie就可以运行。但您必须将withCredentials上的XMLHttpRequest设置为true。

请参阅:The withCredentials attribute

我不使用JQuery,但这是一个专门处理通过JQuery设置withCredentials的问题。

Sending credentials with cross-domain posts?

答案 2 :(得分:1)

不,cookie不能跨域共享。假设浏览器支持使用Access-Control-*标题,可以绕过AJAX调用来绕过same origin policy,但对于cookie,没有办法。

答案 3 :(得分:0)

<+> + Darin Dimitrov怀疑“浏览器没有保存cookie,因为它来自另一个域而不是托管该呼叫源的页面的域。”

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

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

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

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

答案 4 :(得分:0)

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

这是挑战和解决方案:

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

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

3 - Cookie 需要双向传递。

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

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站点上的pass on 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。下面是一个 .htaccess (linux/apache) 的例子,只有 one.example.com 调用 api.example.com:

<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 的根目录中。