【问题标题】:Keeping the cookie after a cross-domain ajax request在跨域 ajax 请求后保留 cookie
【发布时间】:2012-02-09 21:46:09
【问题描述】:

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

【问题讨论】:

    标签: php javascript jquery ajax


    【解决方案1】:

    服务器应该设置标题:

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

    客户端设置为:

    xhrFields: {
      withCredentials: true
    }
    

    【讨论】:

      【解决方案2】:

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

      见:The withCredentials attribute

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

      Sending credentials with cross-domain posts?

      【讨论】:

      • 为澄清起见,developer.mozilla.org 的文档说明了使用带有凭据的请求:“在响应凭据请求时,服务器必须指定域,并且不能使用通配符。”因此,请确保您的 Access-Control-Allow-Origin 标头包含请求来自的实际域,而不是 *
      【解决方案3】:

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

      【讨论】:

      • 感谢您的意见。但是,我并不想跨域共享 cookie。我只想在对同一域的请求之间共享 cookie。如果这就是你的意思,请澄清。
      • @atnnn 它应该可以工作,你 100% 确定不行吗?确保您的后续请求是针对完全相同的域,而不是子域或超级域。
      • 如果你看我的代码,你会发现它是完全相同的请求,并且没有竞争条件。
      • 在 10.0.0.1 上运行的应用程序向另一个域发出了两个 ajax 请求。对该其他域的第二个请求不包括该域的 cookie。我根本不想在域之间共享 cookie。
      • @atnnn,我怀疑 cookie 没有被浏览器保存,因为它来自另一个域,而不是托管此调用来源的页面的域。这就是为什么它没有与第二个请求一起发送的原因。
      【解决方案4】:

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

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

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

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

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

      【讨论】:

        【解决方案5】:

        我需要使用 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 的根目录中。

        【讨论】:

          猜你喜欢
          • 2015-12-21
          • 1970-01-01
          • 2014-01-13
          • 2013-01-05
          • 2017-02-02
          • 2018-03-12
          • 2013-03-06
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多