如何允许 express 后端 REST API 在使用 axios 的反应前端中设置 cookie?

How to allow express backend REST API to set cookie in a react frontend which is using axios?

后端

我正在尝试使基于 JWT cookie 的身份验证工作。我目前正在后端 API.

中执行以下 cookie 设置作为登录路由的一部分
res.cookie('authCookie', token, {maxAge: 900000, httpOnly: true});

稍后当我授权任何其他请求时,我正在读取此 cookie 并在 passport-jwt 策略中对其进行测试。

我在 postman 中完成了这项工作 - 当我执行登录并访问安全路由时 - 它工作得很好 + cookie 也在 postman 中设置。

前端

现在,我在前端执行以下调用堆栈只是为了测试工作,

axios.post("http://localhost:3001/login", logInParams, config)
    .then(result => {
        // User is logged in so push them to the  respective dashboard
        console.log(result);
        axios.get("http://localhost:3001/user/profile")
            .then(result => {
                console.log(result);
            })
            .catch(err => {
                console.log(err);
                return;
            })
    })
    .catch(err => {
        console.log(err)
        return;
    });

所以基本上,我让用户登录并且工作得很好 - 我得到了预期的 JSON 响应,但是调用应该设置一个 cookie,但它不是,因此下一个 axios.get 调用未成功返回。尽管出于某种原因,我看到 session cookie 和 CSRF cookie。只有这个 authCookie 或 jwt-cookie 没有设置。

一些额外的细节

我正在使用带有默认参数的 cors - 这可能是一个错误吗?或者我对 axios 有什么改变吗?我已经看到了一些答案,但作为 MERN 的新手,我并不真正理解它们。有没有人知道这个或者经历过并解决了?

我认为你应该在 res.cookie('authCookie', token, {maxAge: 900000, httpOnly: true});

您是否 运行 服务器来自与提供客户端的端口不同的端口(即:localhost:3000 上的 webpack-dev-server 运行 和 localhost:3001)?这看起来像是 same-site cookie 问题。在某些浏览器的最新版本中,例如 Chrome,当此浏览器来自不同的来源站点时,cookie 设置将被阻止。这是出于安全考虑;您可以了解有关同站点 cookie 的更多信息 here.

浏览器中所做的更改与它们为名为 same-site 的 cookie 策略 属性 提供的默认值有关。旧的解决方法是默认将来自不同来源的所有 cookie 视为 None,但去年它更改为将 same-site 策略视为 Lax,而没有 same-site 策略服务器未明确提供。这是浏览器的理想行为,因为它有助于防止第三方站点使用服务器提供的 cookie,但您可以通过不同的方式对其进行编辑:

  • 更改浏览器设置的默认同站点策略(有关同站点 cookie 的文章有解释)。
  • 从服务器发送 same-site:'None'Express has a way to do so explaind on its docs。请记住,当 cookie 未标记为 Secure 时,浏览器也有一个忽略 same-site:'None' 的新策略,这需要使用 HTTPS(我想可以在您的浏览器中编辑此行为如果您想在使用 HTTP) 时检查设置)。

显然,任何要求用户更改浏览器设置的策略都是行不通的,因此 运行 HTTPSSecure cookie 对 same-site:'None' 是强制性的.

你总是有使浏览器和客户端同源的方法,所以你不会有任何问题 same-site (即 Express 服务器返回生产的 index.html构建您的客户端作为其主要静态)。我还没有找到任何方法来将 CORS 模块配置为具有默认的相同站点 cookie 策略(或其唯一用作中间件来更改它),可能不是它的目的,但您可以尝试 adding a dynamic origin config.

据我所知,Postman does not support the same-site cookie property yet,这样就可以解释为什么它在 Postman 上工作,但在浏览器上不工作。

从外观上看 - 这似乎与 cors 的工作方式有关,我添加以下答案以帮助遇到它的其他人。稍后谢谢我:)

服务器端

您的服务器中将有一个如下所示的 cors,

app.use(cors());

您必须将 credentials 设置为 true 并设置 allowedHeadersorigin,如下所示,

app.use(cors({
    credentials: true,
    allowedHeaders: ['Content-Type', 'Authorization'],
    origin: ['http://localhost:3000']
}));

这是因为如果服务器端和客户端在同一个端口,一般情况下是不允许在浏览器中设置cookies的。要处理此问题,服务器端需要上述内容。

客户端

我们还必须在发送请求时传递 cookie,要使用 axios 执行此操作,只需在您的 React 应用程序的 index.js 中添加以下内容,

axios.defaults.withCredentials = true;