为什么 Chrome 不能设置 cookie
我收到了来自我的服务器的响应.响应头包含有效的 Set-Cookie.Chrome 响应/Cookies 说我有 1 个 cookie,好的.但是... cookie 没有设置到 DevTools/Application/Cookies
I have a response from my server. Response Headers contains a valid Set-Cookie. Chrome Response/Cookies says that I have 1 cookie, ok. But... The cookie is not setting into DevTools/Application/Cookies
/*
My frontend on Vue
I renamed 127.0.0.1 to www.example.com
So running on www.example.com:80 or just www.example.com
*/
let response = await axios({
method: 'post',
url: 'http://127.0.0.1:3000/api/login', // my Node server on 3000 port
data: {
email : login,
password: password,
},
})
/*
My backend part on Node.js+Express
Running on localhost:3000 or 127.0.0.1:3000
CookieParser() is setted
*/
let options = {
maxAge: 345600000,
httpOnly: true,
path: '/',
}
res
.cookie('test', 'test', options)
.cookie('access_token', token, options) //token contains a JWT string
.send('');
/*
Chrome Response Headers
*/
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 0
Content-Type: text/html; charset=utf-8
Date: Sun, 09 Feb 2020 08:52:22 GMT
ETag: W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"
Set-Cookie: test=test; Max-Age=345600; Path=/; Expires=Thu, 13 Feb 2020 08:52:22 GMT; HttpOnly
Set-Cookie: access_token=example; Max-Age=345600; Path=/; Expires=Thu, 13 Feb 2020 08:52:22 GMT; HttpOnly
X-Powered-By: Express
/*
Chrome request headers
*/
Accept: application/json, text/plain, '*/*'
Accept-Encoding: gzip, deflate, br
Accept-Language: ru-RU,ru;q=0.9
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 45
Content-Type: application/json;charset=UTF-8
Host: 127.0.0.1:3000
Origin: http://www.example.com
Pragma: no-cache
Referer: http://www.example.com/
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36
此外,我尝试使用 Microsoft Edge,但问题并未消失.既然浏览器知道接收 Cookie,为什么浏览器不能设置 cookie?
Moreover I tried to use Microsoft Edge, but the problem isn't gone. Why browsers can't set-cookie considering they know about receiving Cookies?
如果你想做一些背景阅读,那么我建议你寻找 CORS(跨域资源共享)指南,特别是如何设置 cookie使用 CORS.
If you want to do some background reading then I suggest you look for guides to CORS (cross-origin resource sharing) and, specifically, how to set cookies when using CORS.
使用您的原始代码,您需要更改它以将 withCredentials
设置为 true
:
Using your original code you'll need to change it to set withCredentials
to true
:
let response = await axios({
method: 'post',
url: 'http://127.0.0.1:3000/api/login', // my Node server on 3000 port
data: {
email : login,
password: password,
},
withCredentials: true
})
在幕后,这将在 XMLHttpRequest
上设置 withCredentials
标志:
Behind the scenes this will set the withCredentials
flag on XMLHttpRequest
:
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials
然后,您会在控制台中收到 CORS 错误.如果您按照自己的方式解决这些错误,您应该能够使其正常工作.这些错误将包括...
You'll then get CORS errors in your console. If you work your way through those errors you should be able to get it working. These errors will include...
首先,服务器需要返回标头Access-Control-Allow-Credentials: true
.这也适用于预检选项请求.
Firstly, the server will need to return the header Access-Control-Allow-Credentials: true
. This also applies to the preflight OPTIONS request.
其次,您当前正在使用 Access-Control-Allow-Origin: *
但不允许使用凭据的请求.这需要是 Access-Control-Allow-Origin: http://www.example.com
.预检也是如此.
Secondly, you're currently using Access-Control-Allow-Origin: *
but that isn't allowed for requests using credentials. That'll need to be Access-Control-Allow-Origin: http://www.example.com
. Likewise for the preflight.
此时应该在大多数浏览器中设置 cookie(稍后将详细介绍浏览器怪癖).
At that point the cookie should be set in most browsers (more on browser quirks later).
要将 cookie 添加到后续请求中,您还需要在这些请求上设置 withCredentials: true
.还需要应用上面概述的其他标题更改.cookie 不会包含在预检请求中,只会包含在主要请求中.
To get the cookies added to subsequent requests you'll also need to set withCredentials: true
on those requests. The other header changes outlined above also need to be applied. The cookies will not be included on the preflight request, just the main request.
在浏览器的开发人员工具中查看 cookie 并不完全简单.通常,这些工具只显示为与当前页面相同来源设置的 cookie,不包括通过 CORS 请求设置的 cookie.要查看这些 cookie,您需要打开另一个浏览器选项卡并将 URL 设置为以与 CORS 请求相同的来源开头的内容.不过要小心,您需要选择一个不会自行设置 cookie 的 URL,否则这并不能证明什么.
Seeing the cookies in the browser's developer tools is not completely straightforward. Generally the tools only show cookies set for the same origin as the current page, which doesn't include the cookies set via CORS requests. To see those cookies you'll need to open another browser tab and set the URL to something that starts with the same origin as the CORS request. Be careful though, you need to pick a URL that won't set the cookies itself, otherwise that doesn't really prove anything.
检查设置了哪些 cookie 的另一种方法是发送另一个请求(使用 withCredentials
)并查看发送了哪些 cookie.
Another way to check what cookies are set is to send another request (with withCredentials
) and see what cookies get sent.
然后我们有浏览器特定的问题...
Then we have the browser-specific issues...
在 Safari 中,很难通过 CORS 设置 cookie.如果 Safari 是您的目标浏览器,那么我建议您对此进行进一步研究.
In Safari it is really difficult to get cookies to set over CORS. If Safari is a target browser for you then I suggest doing some further research into that.
其次,Chrome 80 将显着更改有关 CORS cookie 的规则.见:
Secondly, Chrome 80 is due to change the rules around CORS cookies significantly. See:
https://www.chromium.org/updates/same-site
您已经在使用旧版 Chrome 的控制台中看到了有关此问题的警告.简而言之,CORS cookie 需要通过 https
提供,并设置指令 Secure
和 SameSite=None
.
You'll already see warnings about this in your console with older versions of Chrome. In short, CORS cookies will need to be served over https
and set the directives Secure
and SameSite=None
.
更新:
自从写下这个答案以来,我已经为常见的 CORS 问题创建了一个 FAQ.可以在 https://cors-errors.info/faq#cdc8 中找到解释 cookie 使用的部分.
Since writing this answer I have created an FAQ for common CORS problems. The section explaining the use of cookies can be found at https://cors-errors.info/faq#cdc8.