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

时间:2017-03-10 04:08:29

标签: javascript node.js express cookies session-cookies

我正在使用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。

enter image description here

但是,当我使用fetch API来执行等效操作时,它不会设置cookie。

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

为什么?

2 个答案:

答案 0 :(得分:3)

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

问题在于此fetch调用

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

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

credentialssame-origin我可以在Set-Cookie响应标头中看到来自服务器的Cookie,但浏览器中没有存储任何内容。一个奇怪的事情是,无论我在服务器上的HttpOnly设置如何,这个响应总是在cookie字符串之后标记{httpOnly : true/false}。如果手动使用浏览器到页面执行GET请求,HttpOnly将照常受到尊重,并且cookie已设置完毕。

因此解决方案是将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'

答案 1 :(得分:1)

否则5argon的解决方案对我来说很棒,但是我不得不将origin的显式变量设置为true。所以在后端:

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

并且正在获取:

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