为啥 AJAX 请求返回后浏览器不设置 cookie?

Posted

技术标签:

【中文标题】为啥 AJAX 请求返回后浏览器不设置 cookie?【英文标题】:Why is the browser not setting cookies after an AJAX request returns?为什么 AJAX 请求返回后浏览器不设置 cookie? 【发布时间】:2014-04-21 08:59:09 【问题描述】:

我正在使用 $.ajax 发出 ajax 请求。响应具有 Set-Cookie 标头集(我已经在 Chrome 开发工具中验证了这一点)。但是,浏览器在收到响应后设置cookie!当我导航到我域中的另一个页面时,不会发送 cookie。 (注意:我没有做任何跨域 ajax 请求;请求与文档在同一个域中。)

我错过了什么?

编辑:这是我的 ajax 请求的代码:

$.post('/user/login', JSON.stringify(data));

这是请求,如 Chrome 开发工具所示:

Request URL:https://192.168.1.154:3000/user/login
Request Method:POST
Status Code:200 OK

Request Headers:
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Content-Length:35
Content-Type:application/x-www-form-urlencoded; charset=UTF-8
DNT:1
Host:192.168.1.154:3000
Origin:https://192.168.1.154:3000
Referer:https://192.168.1.154:3000/
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/33.0.1750.154 Safari/537.36
X-Requested-With:XMLHttpRequest

Form Data:
"UserId":"blah","Password":"blah":

回复:

Response Headers:
Content-Length:15
Content-Type:application/json; charset=UTF-8
Date:Sun, 16 Mar 2014 03:25:24 GMT
Set-Cookie:SessionId=MTM5NDk0MDMyNHxEdi1CQkFFQ180SUFBUkFCRUFBQVRfLUNBQUVHYzNSeWFXNW5EQXNBQ1ZObGMzTnBiMjVKWkFaemRISnBibWNNTGdBc1ZFcDNlU3RKVFdKSGIzQlNXRkkwVjJGNFJ6TlRVSHA0U0ZJd01XRktjMDF1Y1c1b2FGWXJORzV4V1QwPXwWf1tz-2Fy_Y4I6fypCzkMJyYxhgM3LjVHGAlKyrilRg==; HttpOnly

【问题讨论】:

所以这可能是一个旧线程,但我偶然发现它正在寻找其他东西,我注意到您的请求在标题中有DNT: 1。如果我记得,这是 Do Not Track,浏览器要求不允许设置 cookie。 如果您在使用 Apollo 时遇到此问题,请查看他们的文档中的 this section 【参考方案1】:

@atomkirk 的 answer 不适用于我,因为

    我不使用fetch API 我正在发出跨站点请求(即 CORS)

注意:如果您的服务器使用Access-Control-Allow-Origins:*(又名“所有来源”/“通配符来源”),您可能无法发送凭据(请参阅下面)。

至于fetch API; CORS 请求将 need credentials:'include' 用于发送和接收 cookie

对于 CORS 请求,使用“include”值来允许发送 其他域的凭据:

fetch('https://example.com:1234/users',    
            credentials: 'include' 
)

...要选择接受来自服务器的 cookie,您必须使用凭据选项。


credentials:'include' 只是设置xhr.withCredentials=true

Check fetch code

if (request.credentials === 'include') 
      xhr.withCredentials = true
  

所以plain javascript/XHR.withCredentials是重要的部分。


如果你使用 jQuery,你 can set withCredentials(记得使用 crossDomain: true)使用 $.ajaxSetup(...)

$.ajaxSetup(
             crossDomain: true,
             xhrFields: 
                 withCredentials: true
             
         );

如果您使用的是 AngularJS,$http service config arg 接受 withCredentials 属性:

$http(
    withCredentials: true
);

如果您使用 Angular (Angular IO),common.http.HttpRequest service options arg 接受 withCredentials 属性:

this.http.post<Hero>(this.heroesUrl, hero, 
    withCredentials: true
);

至于请求,当xhr.withCredentials=true;发送 Cookie 标头

在我改变之前xhr.withCredentials=true

    我可以在响应中看到 Set-Cookie 名称和值,但开发者工具中 Chrome 的“应用程序”选项卡向我显示了名称和 空值 后续请求未发送Cookie 请求标头。

更改后xhr.withCredentials=true

    可以在 Chrome 的“应用程序”选项卡中看到 cookie 的 名称 和 cookie 的 (与 Set-Cookie 一致的值标题)。 后续请求确实发送了具有相同值的 Cookie 请求标头,因此我的服务器将我视为“已验证”

至于响应:服务器可能需要某些Access-Control... 标头

例如,我将服务器配置为返回这些标头:

访问控制允许凭据:true Access-Control-Allow-Origin:https://your-origin:your-port

编辑:如果您允许all origins/wildcard origins, as described here(感谢@ChandanBhattad),此方法将不起作用:

尝试使用设置了凭据标志的 CORS 请求,但服务器使用通配符 ("*") 作为 Access-Control-Allow-Origin 的值进行配置,这不允许使用凭据。

在我对响应标头进行此服务器端更改之前,Chrome 会在控制台中记录错误,例如

无法加载 https://saml-domain/saml-authn:从 https://saml-domain/saml-redirect 重定向已被 CORS 策略阻止:

响应中'Access-Control-Allow-Credentials' 标头的值为'',当请求的凭据模式为'include' 时,必须为'true'。 Origin https://your-domain 因此不允许访问。

XMLHttpRequest 发起的请求的凭证模式由 withCredentials 属性控制。

在进行此 Access-* 标头更改后,Chrome 没有记录错误;浏览器让我检查所有后续请求的经过身份验证的响应。

【讨论】:

for both sending &amp; RECEIVING cookies 确实有帮助。 这似乎是正确的答案。在 ajax POST 请求中处理了用户身份验证,但未设置 cookie。有趣的是,这个问题只发生在移动浏览器上,而不是桌面浏览器上。 xhrFields: withCredentials: true 参数显然解决了这个问题。太糟糕了,我在解决问题后找到了这个答案,这让我发疯了几天。 发送和接收.. 真实的。我正在使用 withCredentials: false 设置 POST 到我的登录端点,并花了 4 个多小时试图弄清楚为什么我们没有设置 cookie。 谢谢。我和你的情况一样,使用跨站点cookies(我的后端在一个主机上运行,​​前端在另一个主机上运行)。除了我没有在登录请求中使用 withCredentials 标志外,我已经准备好了一切,只在后续请求中使用。一旦我在登录请求中使用它(必须以正确的方式配置我的 CORS 策略,因为它不喜欢允许的来源是 *),它工作正常。 谢谢。如果我设置了 "access-control-allow-origin:*" ,这似乎不起作用。基本上 - “凭据”:当 access-control-allow-origin 设置为 * 时不允许“包含”?【参考方案2】:

在我的例子中,cookie 大小超过了 4096 字节(谷歌浏览器)。我有一个会增加大小的动态 cookie 有效负载。

如果 cookie 超出浏览器限制,浏览器将忽略 set-cookie 响应头,并且不会设置 cookie。

See here 用于每个浏览器的 cookie 大小限制。

我知道这不是解决方案,但这是我的问题,我希望它可以帮助某人:)

【讨论】:

【参考方案3】:

这可能会帮助某人随机遇到这个问题。

我发现强制使用 https:// 而不是 http:// 的 URL,即使服务器没有证书并且 Chrome 抱怨会解决这个问题。

【讨论】:

【参考方案4】:

好的,所以我终于找到了问题所在。事实证明,在 AJAX 请求中发送 cookie 时,设置 Path 选项很重要。如果设置Path=/,例如:

Set-Cookie:SessionId=foo; Path=/; HttpOnly

...那么当您导航到不同的页面时,浏览器将设置 cookie。不设置Path,浏览器使用“默认”路径。显然,AJAX 请求设置的 cookie 的默认路径与直接导航到页面时使用的默认路径不同。我正在使用 Go/Martini,所以在服务器端我这样做:

session.Options(session.OptionsHttpOnly: true, Path:"/")

我猜是 Python/Ruby/等。有类似的机制来设置Path

另请参阅:cookies problem in php and AJAX

【讨论】:

Dayum,这很有用!在 java 中(使用 servlet API):cookie.setPath("/"); 终于!!我们两个人花了一天时间试图弄清楚为什么我们的 CORS+AJAX cookie 不起作用! 谢谢,我花了几个小时试图解决这个问题。 花了一些时间寻找解决方案并将路径添加到 cookie 中【参考方案5】:

如果您使用新的fetch API,可以尝试包含credentials

fetch('/users', 
  credentials: 'same-origin'
)

这就是为我解决的问题。

特别是使用 polyfill:https://github.com/github/fetch#sending-cookies

【讨论】:

我和@jag 在这个!!我只花了 4 个小时尝试使用 passport.js 通过 ajax 登录......直到我点击返回 cookie 的网络响应,这完全是个谜。它根本不会保存。您的解决方案修复了它。干杯 在 set-cookie 响应中添加凭据和路径后,它适用于我 注意:如果您的 api 位于另一个域中,则需要使用 credentials: 'include'。此外,如果您在使用 apollo 时遇到此问题,请查看他们的文档中的 this section。

以上是关于为啥 AJAX 请求返回后浏览器不设置 cookie?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 jQuery 的 .ajax() 方法不发送我的会话 cookie?

为啥我的ajax请求返回的页面不跳转

仅仅通过Ajax(XMLHttpRequest)请求,能否将cookie保存到游览器上?

ajax请求 spring mvc responsebody 注解的方法 为啥写不了cookie

为啥 AJAX 里写入的 cookie 无法显示出来

为啥我在 IE 9 中丢失了有关 Ajax 请求的 cookie 和会话