-1

我正在制作一个网络应用程序。我的后端使用 Node.js、Express.js,具体来说,它使用模块 express-session 为基于会话的身份验证创建会话对象,以保持用户登录。我了解当我使用 express-session 创建会话时,会在后端创建一个带有会话 ID 的 cookie,并将其发送到前端的浏览器。我已验证当我使用浏览器并访问托管 Express 应用程序的页面(在我的情况下为localhost:3001)时,此 cookie 会无缝发送。

// My Express app's index.js file
// This code seamlessly sends a session ID cookie to the browser when I visit http://localhost:3001

const express = require('express');
const session = require('express-session');
const mongoDbStore = require('connect-mongodb-session')(session);
const app = express();
const store = new mongoDbStore({
    uri: 'mongodb://localhost:27017/GameDB',
    collection: 'UserSessions'
});

store.on('error', (err) => {
    console.log('Something exploded:' + err);
});

app.use(session({
    secret: 'I am stuck, help me please!',
    store: store,
    saveUninitialized: true,
    resave: false,
} ));

app.listen(3001, () => {
    console.log('server started on 3001');
})

我得到我的饼干就好了。这是我的 Chrome 开发者工具中的截图:

在此处输入图像描述

但是,我希望我的前端是一个单独的应用程序(我正在使用 React),并且我想使用来自前端的 API 请求来访问后端的所有内容。由于前端和后端是独立的应用程序,它们需要在不同的端口上运行。我的前端将在端口 3000 上运行,而我的后端将继续在端口 3001 上运行。考虑到这一点,我将localhost:3000在浏览器中运行而不是localhost:3001.

唯一的问题是,通过这些更改,express-session 生成的 cookie 不再发送到我的浏览器,即使fetch()我从前端到后端执行 HTTP POST(通过 JavaScript)并获得有效响应也是如此。

简而言之,我的问题是:当我为前端使用单独的应用程序时,如何将我的快速会话会话 ID cookie 保存到我的浏览器中?

这是我的前端 API 请求:

fetch('http://localhost:3001/gimmecookie', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
    },
    body: JSON.stringify({ data: "data" })
})
.then(response => response.json() )
.then(response => {
    console.log(response)
})
.catch((error) => {
    console.error(error);
});

下面是我的 Express 应用程序的 index.js 文件稍作修改:

// My Express app's index.js file that *should* create and 
//   send a session-id cookie to my React project

const express = require('express');
const cors = require('cors');
const session = require('express-session');
const mongoDbStore = require('connect-mongodb-session')(session);
const app = express();

const corsOptions = {
    origin: 'http://localhost:3000',
}

const store = new mongoDbStore({
    uri: 'mongodb://localhost:27017/GameDB',
    collection: 'UserSessions'
});

store.on('error', (err) => {
    console.log('Something exploded:' + err);
});

app.use(cors(corsOptions));

app.use(session({
    secret: 'I am stuck, help me please!',
    store: store,
    saveUninitialized: true,
    resave: false,
}));

app.post('/gimmecookie', (req, res) => {
    res.json({ sendsome: "data" })
});

app.listen(3001, () => {
    console.log('server started on 3001');
});

现在,我正在使用 hack 发送 cookie(在 JS 中创建一个 cookie 并手动发送),但是随着我的项目变得越来越大,hack 变得越来越烦人。当我只使用一个应用程序时,我需要在此代码中添加什么才能让 cookie 发送?express-session 不是为此设计的吗?(似乎是这样,我知道前端和后端都有一个单独的应用程序是非常普遍的。)

如果前端和后端有任何形式的握手,我是否应该期望 cookie 自动发送?我是否需要弄乱快速会话初始化对象中的cookie.domainorcookie.sameSite属性?我需要弄乱CORS或fetch() Accept标题吗?是不是res.json()用错了方法?处理真实IP更容易localhost吗?我已经尝试了很多东西,但无论我做什么,我都无法在我的浏览器上获得那个被爆破的 express-session 生成的会话 ID cookie。

4

1 回答 1

1

事实证明,问题出在前端,而不是后端,这是一个 CORS 问题。express-session 代码使 cookie 很好,但前端无法接受它,因为后端托管在端口 3001 上使其成为跨域请求(您会记得前端位于端口 3000 上) ,即使前端和后端都在同一台机器上,localhost。

我所要做的就是proxy在我的 React 项目中使用 package.json 文件中的字段,如下所示:

    ...
  },
  "proxy": "http://localhost:3001"
}

随着代理的改变,我也不得不改变fetch()我的 React 代码,从这里:

fetch('http://localhost:3001/gimmecookie', {
  ...

对此:

fetch('/gimmecookie', {
  ...

因为我的proxy领域正在欺骗我的 React 项目,让我认为我在不同端口上的后端实际上是同一个来源。

旁注:一旦我意识到这是一个前端 CORS 问题,我就尝试了一些其他解决方案(例如credentials: includefetch()init 对象中使用),但很快就发现这些解决方案存在重大缺陷,直到我找到代理解决方案。Flippn'CORS!

于 2021-03-20T04:31:51.007 回答