为什么 cookie in res headers 没有在浏览器中设置?

Why cookie in res headers is not set in browser?

我 运行 后端在 http://localhost:4000,前端在 http://localhost:3000。在服务器 (Express) 中,我将 cors 策略设置为:

  app.use( cors({
    credentials: true,
    origin: "http://localhost:3000",
  }));

但是没有设置cookie。请求后

POST /graphql HTTP/1.1
Host: localhost:4000
Connection: keep-alive
Content-Length: 373
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36
content-type: application/json
Accept: */*
Origin: http://localhost:3000
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost:3000/register
Accept-Encoding: gzip, deflate, br
Accept-Language: et-EE,et;q=0.9,en-US;q=0.8,en;q=0.7

我得到回复 headers 正确 (?) Set-Cookie:

HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: http://localhost:3000
Vary: Origin
Access-Control-Allow-Credentials: true
Content-Type: application/json; charset=utf-8
Content-Length: 122
ETag: W/"7a-3NezKfolEhPugNDLsBCed9Xgfzk"
Set-Cookie: lirr=s%3A9JJT7_mt6W-zSWkg-DKkBmxK6O-i8bH3.1k88dMMeintjdSUMV6ig1DK5u4vgdYh4rnmtufww7nw; Path=/; Expires=Sat, 27 Nov 2021 09:31:14 GMT; HttpOnly; SameSite=Lax
Date: Fri, 27 Nov 2020 09:31:14 GMT
Connection: keep-alive
Keep-Alive: timeout=5

我尝试使用 Chrome 和 Firefox,但都没有设置 cookie。我没有发现我的设置有缺陷,headers 看起来不错。我错过了什么?

另外

如果我让从后端设置 cookie(暴露为 Graphql API,通过 Graphql 游乐场访问),它 工作正常 这样的请求 headers :

POST /graphql HTTP/1.1
Host: localhost:4000
Connection: keep-alive
Content-Length: 323
accept: */*
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36
content-type: application/json
Origin: http://localhost:4000
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost:4000/graphql
Accept-Encoding: gzip, deflate, br
Accept-Language: et-EE,et;q=0.9,en-US;q=0.8,en;q=0.7

据我所知,有 3 行(如预期的那样)不同:

Origin: http://localhost:4000
Sec-Fetch-Site: same-origin
Referer: http://localhost:4000/graphql

响应与前端的响应非常相似:

HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: http://localhost:3000
Vary: Origin
Access-Control-Allow-Credentials: true
Content-Type: application/json; charset=utf-8
Content-Length: 75
ETag: W/"4b-Ud8RJ2O7ZjwpTxFNSAQi92iV20w"
Set-Cookie: lirr=s%3A9CYQjFpVwG69-ojwqlk-FJWVNC02DMJP.Prt8U5%2Bcco9ktEL27JoOkAlACGKS%2Fy1NHfgugPypl00; Path=/; Expires=Sat, 27 Nov 2021 10:32:37 GMT; HttpOnly; SameSite=Lax
Date: Fri, 27 Nov 2020 10:32:37 GMT
Connection: keep-alive
Keep-Alive: timeout=5

不明白,为什么浏览器不通过前端设置cookie...

服务器端检查

我觉得你的设置不错。

  app.use( cors({
    credentials: true,
    origin: "http://localhost:3000",
  }));

我建议使用一些 API 工具测试您的代码,例如 POSTman,它 默认启用 CORS 请求。如果它与 POSTman 一起工作,那么你的服务器端代码没有问题。

CORS + Cookie 的浏览器问题

简单来说,CORS请求就是对不同域的XMLHttpRequest。 https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

根据以下文档, https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials

In addition, this flag is also used to indicate when cookies are to be ignored in the response. The default is false. XMLHttpRequest from a different domain cannot set cookie values for their own domain unless withCredentials is set to true before making the request. The third-party cookies obtained by setting withCredentials to true will still honor same-origin policy and hence can not be accessed by the requesting script through document.cookie or from response headers.

浏览器不会 accept/transfer 通过 CORS 请求 (XMLHttpRequest) 的 cookie,除非您启用它。

xhr.withCredentials = true;

通过浏览器控制台的示例

//from http://localhost:3000
const xhr = new XMLHttpRequest();
const url = 'http://localhost:4000/get-cookie-url';
   
xhr.open('GET', url);
xhr.withCredentials = true; // enable browser to transfer cookies
xhr.onload = function(e){
  console.log(e)
};
xhr.send();

Cookie 传输

举例来说,cookie 传输类似于握手

双方都需要发起握手。如果一方(服务器)发起而另一方(客户端)不发起,则不会发生握手。

服务器启动cookie传输时,

//Server response header
//Server will response with "Set-Cookie" header when browser requests
...
Set-Cookie: lirr=xxxx; ...
...

客户端(浏览器)发起cookie传输时,

//Client request header
//Client will request with "Cookie" header 
...
Cookie: lirr=xxxx; ....
...