在Set-Cookie头中的Cookie未被设置

13

我正在使用axios从reactjs客户端向node.js服务器发送请求,如下所示。

import axios from 'axios';

const apiClient = axios.create({
  withCredentials: true,
  baseURL: 'http://localhost:8080'
});

async function(payload) {
  try {
    debugger;
    let result = await apiClient.post('/auth/signup/', payload);
    debugger;
    return result;
  } catch (error) {
    debugger;
    throw error;
  }
}

如下所示,node.js端点在响应中设置了一个cookie。

const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser')
const cors = require('cors');
const jwt = require('jsonwebtoken');

router.use(bodyParser.json());
router.use(bodyParser.urlencoded({ extended: true }));
router.use(cors({ origin: 'http://localhost:3000', credentials: true, exposedHeaders: ['Set-Cookie', 'Date', 'ETag']} ));
router.use(cookieParser());


router.post('/signup', async (req, res, next) => {
  debugger;
  let database = req.app.locals.database;
  try {
    let user = await database.findByUsername(req.body.username);
    let token = await jwt.sign({username: user.username}, config.secret, {expiresIn: "15m"});
    res.cookie('jwt',  token, {
      maxAge: 900,
    });
  } catch (error) {
    debugger;
    return res.status(503).send({ auth: false, message: 'Database error.' });
  }
});


响应的Set-Cookie头包含了预期的cookie。

Set-Cookie Header

然而,Chrome似乎没有设置cookie,因为我在开发者控制台的应用程序窗口中看不到cookie。

Cookies

我已经查看了以下问题的答案,其中提到在axios配置中设置{withCredentials:true}并在node.js的cors中不使用通配符来源,但我已经两者都在做了。
Chrome中Set-Cookie header not setting cookie为跨域请求设置cookie
有什么想法,为什么cookie没有被设置,以及如何解决这个问题?

3
好问题。插入图片并点赞。 - marko-36
3个回答

4
以下是我在类似问题上的答案: https://dev59.com/BLHma4cB1Zd3GeqPQ8oC#62821342
在我的情况下,网络面板显示响应具有“Set-Cookie”标头,但在axios中,标头不会显示,并且cookie已设置。
对我来说,解决方法是设置Access-Control-Expose-Headers标头。
为了解释,从axios存储库中的一个问题的此评论,我被引导到这个人的笔记,这使我设置了Access-Control-Expose-Headers标头-现在cookie在客户端中正确地设置。
因此,在Express.js中,我必须将exposedHeaders选项添加到我的cors中间件中:
const corsOptions = {
  //To allow requests from client
  origin: [
    "http://localhost:3001",
    "http://127.0.0.1",
    "http://104.142.122.231",
  ],
  credentials: true,
  exposedHeaders: ["set-cookie"],
};

...

app.use("/", cors(corsOptions), router);

在使用axios请求需要包括cookie时,对于axios端而言,使用withCredentials配置也是非常重要的。

例如:

const { data } = await api.get("/workouts", { withCredentials: true });

在前端使用 Axios 时,exposedHeaders: ["set-cookie"] 似乎是至关重要的。 - Sam

4
尽管你将客户端和服务器都托管在相同的域名下,即http://localhost,但是你们使用的端口不同,因此同源策略未能起作用。你可以查看https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy了解详情。
所以,你正在发起CORS请求,请在当前浏览器的开发者工具的网络选项卡中检查,你可能会看到一个预检请求OPTIONS,在客户端发送POST请求之前。
服务器必须指定头部以接受来自http://localhost:8000 的下一个请求 - POST请求,并且方法为POST,你可以参考https://developer.mozilla.org/zh-CN/docs/Glossary/Preflight_request
HTTP/1.1 204 No Content
Connection: keep-alive
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: POST // Your next request will use POST method
Access-Control-Max-Age: 86400
Access-Control-Allow-Credentials: true // cookies accepted

新增:

Set-Cookie 中的 Max-Age 必须是非零数字。并且根据RFC文档,它将会被舍入为整数。对于 Express.js,cookie 的 maxAge 属性是毫秒级别的

解决方案是将 maxAge 属性设置为 second * 1000

    res.cookie('jwt',  token, {
      maxAge: 10000,
    });

只需检查Express的CORS中间件即可。它似乎可以在预检响应中完成设置CORS配置的所有工作。现在,我非常想知道响应会是什么样子。 - trmaphi
根据此来源:https://httptoolkit.tech/blog/chrome-79-doesnt-show-cors-preflight,预检请求现在无法在Chrome DevTools中查看。我也不太确定您在预检请求/响应中要寻找什么。您能否请澄清一下? - V. Nitu
从屏幕上看,你似乎没有问题,关于服务器响应的 HTTP cookie 值。那么,请检查你 Set-Cookie 中的 Max-Age,可能是问题所在,因为我记得该值必须是非零数字。 - trmaphi
1
谢谢,确实是Max-Age值为0的问题。我以为在node中使用res.cookie('jwt', token, {maxAge: 900});设置了Max-Age,但查看Set-Cookie头时发现Max-Age为0。虽然在node中cookie选项中的maxAge属性是以毫秒为单位的,但Set-Cookie头中的Max-Age是以秒为单位的,因此我的900毫秒值被四舍五入为0秒。将您的评论添加到答案中,我会接受它。 - V. Nitu

0
如果有人遇到相同的问题,这可能会有所帮助。
前端:如果使用axios,则添加:withCredentials: true。对于fetch(),添加:credentials: 'include'
后端:如果你在http://localhost:....上,可以在你的cookie中添加sameSite:'lax',只要确保你在http://localhost上不要添加Secure,因为它只适用于https而不是http,否则你的cookie将无法添加到浏览器中。在生产环境中,添加sameSite: 'none'Secure是可以的。
我还添加了一些头部信息:
res.setHeader('Access-Control-Allow-Credentials', true);
res.setHeader('Access-Control-Allow-Headers','Origin, X-Requested-With, Content-Type, Accept, authorization')
res.setHeader('Content-Type', 'application/json');
res.setHeader('Access-Control-Allow-Origin','http://localhost:3000');
res.setHeader('Access-Control-Allow-METHODS',"GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH");

还添加了:
app.use(cors({
    origin: 'http://localhost:3000',
    credentials: true
}))

请确保在添加头部或跨域时,不要使用'*'。请按照上面的示例写:origin: 'http://localhost:3000'res.setHeader('Access-Control-Allow-Origin','http://localhost:3000'); 以上所有内容都对我的项目有所帮助,希望也能帮助其他人。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接