为啥 Chrome 不能设置 cookie

Posted

技术标签:

【中文标题】为啥 Chrome 不能设置 cookie【英文标题】:Why Chrome can’t set cookie为什么 Chrome 不能设置 cookie 【发布时间】:2020-05-24 16:25:36 【问题描述】:

我收到了来自我的服务器的响应。响应标头包含有效的 Set-Cookie。 Chrome 响应/Cookie 说我有 1 个 cookie,好的。但是... cookie 没有设置到 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,但问题并没有消失。为什么浏览器知道接收 Cookies 却不能设置 cookie?

【问题讨论】:

请求是同源还是跨源?你用的是http还是https?您是否在 cookie 上设置了任何其他指令,例如ExpiresMax-AgeDomain 等等? 这里没有足够的细节来帮助我们。请阅读How do I ask a good question,这真的是“我如何提出人们可以快速准确地回答的问题?”。另请参阅How to create a minimal, reproducible example?。 如果您打开 Chrome 调试器并转到网络选项卡,并准确地向我们展示原始请求和响应的样子(特别是这样我们可以看到域并查看确切的服务器发回的cookie,那么我们也许可以知道发生了什么。 @skirtle 对不起,伙计们!昨天我有点生气,厌倦了解决这个问题。我已经重写了我的帖子并添加了额外的信息 @jfriend00 对不起,伙计们!昨天我有点生气,厌倦了解决这个问题。我已经重写了我的帖子并添加了额外的信息 【参考方案1】:

如果你想做一些背景阅读,那么我建议你寻找 CORS(跨域资源共享)的指南,特别是在使用 CORS 时如何设置 cookie。

使用您的原始代码,您需要将其更改为将withCredentials 设置为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 标志:

https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials

然后,您将在控制台中收到 CORS 错误。如果您解决这些错误,您应该能够使其正常工作。这些错误将包括...

首先,服务器需要返回标头Access-Control-Allow-Credentials: true。这也适用于预检 OPTIONS 请求。

其次,您当前使用的是Access-Control-Allow-Origin: *,但对于使用凭据的请求,这是不允许的。那需要是Access-Control-Allow-Origin: http://www.example.com。预检也是如此。

此时应该在大多数浏览器中设置 cookie(稍后将详细介绍浏览器怪癖)。

要将 cookie 添加到后续请求中,您还需要在这些请求上设置 withCredentials: true。上面列出的其他标题更改也需要应用。 cookie 不会包含在预检请求中,仅包含在主请求中。

在浏览器的开发人员工具中查看 cookie 并不完全简单。通常,这些工具只显示与当前页面相同来源的 cookie 设置,其中不包括通过 CORS 请求设置的 cookie。要查看这些 cookie,您需要打开另一个浏览器选项卡并将 URL 设置为以与 CORS 请求相同的来源开头的内容。不过要小心,您需要选择一个不会设置 cookie 本身的 URL,否则这并不能证明任何事情。

检查设置了哪些 cookie 的另一种方法是发送另一个请求(使用 withCredentials)并查看发送了哪些 cookie。

然后我们遇到了特定于浏览器的问题...

在 Safari 中,很难通过 CORS 设置 cookie。如果 Safari 是您的目标浏览器,那么我建议您对此进行进一步研究。

其次,Chrome 80 将显着改变 CORS cookie 的规则。见:

https://www.chromium.org/updates/same-site

您已经在使用旧版 Chrome 的控制台中看到有关此问题的警告。简而言之,CORS cookie 需要通过 https 提供服务并设置指令 SecureSameSite=None

更新:

自从写下这个答案以来,我已经为常见的 CORS 问题创建了一个常见问题解答。可以在https://cors-errors.info/faq#cdc8 找到解释使用 cookie 的部分。

【讨论】:

这是有史以来最好的答案!但是有什么理由使用 CORS 吗?或者我不会摆脱这个问题,这是一个标准?还是 CORS 是最佳实践?... @profes-s-rr 避免 CORS 的唯一方法是将所有请求发送到同一来源。根据您要实现的目标,这很可能是一种选择。可以使用反向代理将多个服务器组合成一个服务器。例如,如果您使用的是 Vue CLI,您可以在 cli.vuejs.org/config/#devserver-proxy 阅读有关在开发期间配置代理的更多信息。 这是一个非常好的问题。在我看来,它会解决我的问题。感谢分享【参考方案2】:

如果您使用 Axios 或 Fetch - 默认情况下它们不会发送 cookie。 如果你去检查 dav 工具 - (应用程序选项卡 -> cookie),你应该会看到它们。 这不是 chromes 问题,而是您尝试将它们发回的方式,在获取时您需要将 credentials: 'include', 添加到选项对象中,在 axios 中它是 withCredentials: true

【讨论】:

【参考方案3】:

如果您使用的是 express,可能会为您的会话 cookie 使用 express-session。

来自官方文档:https://www.npmjs.com/package/express-session


如果您使用cross-site origin and https 安全连接:

session: 
   cookie: 
      maxAge: 1000*60*60*1,
      httpOnly: true, //set false if you want to change the cookie using JavaScipt
      secure: true,
      sameSite: 'none'
  

别忘了设置: app.set('trust proxy', 1) 使用 express js


否则在localhost你可以使用

session: 
   cookie: 
      maxAge: 1000*60*60*1,
      httpOnly: true, //set false if you want to change the cookie using JavaScipt
      secure: false,
      sameSite: 'lax'
  

【讨论】:

以上是关于为啥 Chrome 不能设置 cookie的主要内容,如果未能解决你的问题,请参考以下文章

为啥谷歌chrome不能设置成默认浏览器

为啥不能再在 Chrome 中移动 DOM 节点?

为啥谷歌浏览器插件SangforECPlugin安装了以后还是不能用?

为啥滚动仅在 Chrome 中不起作用?

为啥Chrome安装不能触发Windows Defender等

为啥 MutationObserver 代码不能在 Chrome 30 上运行?