【问题标题】:Send an express-session generated session ID cookie to a different-origin React front end将 express-session 生成的会话 ID cookie 发送到不同来源的 React 前端
【发布时间】:2021-06-14 06:58:18
【问题描述】:

我正在制作一个网络应用程序。我的后端使用 Node.js、Express.js,具体来说,它使用模块 express-session 为session-based authentication 创建会话对象,以保持用户登录。我了解当我使用 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 不再发送到我的浏览器,即使我从前端到后端执行 HTTP POST(通过 JavaScript fetch())并且得到一个有效的回应。

简而言之,我的问题是:当我为前端使用单独的应用程序时,如何将我的快速会话会话 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.domaincookie.sameSite 属性?我需要弄乱 CORS 或 fetch() Accept 标头吗? res.json() 是不是用错了方法?处理真实IP而不是localhost更容易吗?我已经尝试了很多东西,但无论我做什么,我都无法在我的浏览器上获取那个该死的 express-session 生成的会话 ID cookie。

【问题讨论】:

  • 另外,我知道有人问过类似的问题,但我发布了这个问题,因为其他问题存在一些关键差异,并且通常没有好的(或任何)答案。这是一个问类似问题的问题,但使用与 express-session 不同的模块:stackoverflow.com/questions/62096877/…
  • 如果您根据需要从浏览器中的端口 3000 访问并且代码使 xhr 到 3001 它应该可以正常工作,您的图像显示您在端口 3001 上,尽管我不确定您为什么想要这个,当您上线时,您想要一个反向代理,您不会想要使用差异端口,最好只针对您的应用程序的单个入口点
  • @LawrenceCherone,你的评论并没有完全回答我的问题,但它让我指向了正确的方向,所以谢谢你。看我的回答。

标签: javascript reactjs express session-cookies mern


【解决方案1】:

原来问题出在前端,而不是后端,这是一个 CORS 问题。快速会话代码使 cookie 正常,但前端无法接受它,因为将后端托管在端口 3001 上使其成为跨域请求(您会记得前端位于端口 3000 上) ,即使前端和后端都在同一台机器上,localhost。

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

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

随着代理的更改,我还必须更改我的 React 代码中的 fetch(),如下:

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

到这里:

fetch('/gimmecookie', {
  ...

因为我的 proxy 字段欺骗了我的 React 项目,让我认为我在不同端口上的后端实际上是在同一个源上。

旁注:一旦我意识到这是一个前端 CORS 问题,我就尝试了一些其他解决方案(例如在 fetch() 初始化对象中使用 credentials: include),但很快很明显,在我找到代理解决方案之前,这些解决方案有很大的缺陷。翻转 CORS!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-09-17
    • 1970-01-01
    • 2020-08-14
    • 2020-10-13
    • 2015-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多