fetch() 无法设置从服务器接收到的 cookie?

Posted

技术标签:

【中文标题】fetch() 无法设置从服务器接收到的 cookie?【英文标题】:fetch() cannot set cookies received from the server? 【发布时间】:2017-07-31 07:21:39 【问题描述】:

我正在使用 Express.js 服务器。使用cookie-parser 我已经打开了这个端点

app.get("/s", (req,res) => 
    res.cookie("bsaSession", req.session.id)
    res.send("set cookie ok")
)

当我手动使用浏览器到http://localhost:5555/s 运行网站时,浏览器调试控制台显示cookie 已被应用。

但是当我使用fetch API 做等效时,它并没有设置cookie。

  async trySetCookie()
  
    await fetch("http://localhost:5555/s",
       method: 'GET',
       credentials: 'same-origin'
    )
  

为什么?

【问题讨论】:

"它没有设置 cookie" --- 你怎么知道的?浏览器是否在请求标头中发送了 cookie? 我查看了 Safari 调试控制台 > 存储 > Cookies。当我单击调用trySetCookie() 的按钮时,列表不会改变。但是当我直接进入该页面时,列表会立即添加新的 cookie。 那么,您在响应标头中看到Set-Cookie 了吗?该脚本是否在同一个端口上运行? 这可能只是 Safari 调试控制台在 Ajax 调用完成后没有立即更新其显示的情况。我建议你用代码测试 cookie 的存在。获取完成后,只需执行console.log(document.cookie) 我又找到了一条线索,在这两种情况下Set-Cookie 肯定会显示要设置的cookie,但在fetch 情况下,它总是在cookie 字符串后面有HttpOnly。这可能是阻止浏览器设置 cookie 的原因?我尝试在服务器端添加httpOnly : false 选项,但fetch 仍然有HttpOnly 无论如何。 【参考方案1】:

我找到了解决方案。这个问题的核心是我触发fetch的按钮在http://localhost:3000/上。服务器在http://localhost:5555/(我在自己的机器上模拟真实环境)

问题是这个fetch调用

  async trySetCookie()
  
    await fetch("http://localhost:5555/s",
       method: 'GET',
       credentials: 'same-origin'
    )
  

没有credentials,浏览器无法通过fetch(https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials)发送或接收cookies

使用credentials 作为same-origin,我可以在Set-Cookie 响应标头中看到来自服务器的cookie,但浏览器中没有存储任何内容。一件奇怪的事情是,无论我在服务器上的httpOnly : true/false 设置如何,这个响应总是在cookie 字符串之后标记HttpOnly。在手动使用浏览器到页面做GET请求的情况下,HttpOnly照常被尊重,并设置cookies。

所以解决方法是将credentials设置为include,以允许跨域cookie发送。

  async trySetCookie()
  
    await fetch("http://localhost:5555/s",
       method: 'GET',
       credentials: 'include'
    )
  

此外,在服务器端,您需要手动允许使用新标头的特定来源:

app.get("/s", (req,res) => 
    res.cookie("bsaSession", req.session.id, httpOnly:false)
    res.header('Access-Control-Allow-Origin', 'http://localhost:3000')
    res.header('Access-Control-Allow-Credentials','true'
    res.send("set")
)

不这样做会导致

XMLHttpRequest cannot load http://localhost:5555/s. Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true.

但是无论这个错误如何,cookie 都会被设置。仍然很高兴包含该标题以消除错误。

如果您将cors 中间件用于Express,那就更容易了。您可以使用这些选项

var corsOptions = 
  origin: 'http://localhost:3000',
  credentials:  true


app.use(cors(corsOptions))

当然credentials: 'include' 在客户端仍然是必需的。

【讨论】:

对于 CORS 情况下的第一次身份验证,还需要参数 credentials: 'include'。然后浏览器会自动设置服务器返回的cookie。 布拉赫,你是真正的MVP。已经为此苦苦挣扎了一整天。你应该有一个假期来庆祝你的技能。【参考方案2】:

5argon 的解决方案对我来说非常棒,但我不得不将 express cors 中的 origin 设置为 true。所以在后端:

app.use(
  cors(
    origin: true,
    credentials: true,
  )
);

在获取中:

fetch("http://localhost:5555/s", 
    method: 'GET',
    credentials: 'include'
)

【讨论】:

警告使用 NextJS 的人:它显然不喜欢 "origin: true" 和 "origin: '*' " 被设置。不过,约翰的答案应该适用于各种用例

以上是关于fetch() 无法设置从服务器接收到的 cookie?的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 2.1 MVC - 无法设置从 Web api 响应接收到的 cookie

反应等待异步还是重新渲染?

“无法解析的日期:1302828677828”试图用 Gson 反序列化从服务器接收到的毫秒格式日期

解析ajax服务请求——客户端的数据配置解析——服务端node的接收数据的解析——其他状态——fetch——ajax封装fetch

无法从 python 服务器将图像上传到 Firebase 存储

ant design 中,使用dva/fetch 设置导致无法从后台导出excel的问题