使用Expressjs实现多用户同时登录时独立展示对应发票咨询
问题根因
你用app.set()存储的Users和userInvoices是Express应用实例的全局属性,所有用户的请求共享该实例,因此后登录的用户会拿到前一个用户存入全局的数据,这是多用户串数据的核心原因。
1. express-session可以解决该问题
session是基于用户独立存储的服务端数据,每个用户对应唯一的会话标识,不会出现跨用户数据串用的问题。
具体实现步骤
第一步 安装依赖
npm install express-session connect-mongo
用connect-mongo将会话数据存在MongoDB中,适配你的技术栈,避免内存存储会话在生产环境丢失数据、无法多实例部署的问题。
第二步 配置会话中间件
在Express项目的入口文件(如app.js)中,路由注册前添加如下配置:
const session = require('express-session'); const MongoStore = require('connect-mongo'); app.use(session({ secret: process.env.SESSION_SECRET, // 加密密钥存在环境变量中,不要明文写在代码里 resave: false, saveUninitialized: false, store: MongoStore.create({ mongoUrl: process.env.MONGO_URI }), // 替换为你的MongoDB连接串 cookie: { maxAge: 7 * 24 * 60 * 60 * 1000, // 会话有效期7天 httpOnly: true, secure: process.env.NODE_ENV === 'production' // 生产环境开启HTTPS后设为true } }));
第三步 改造现有业务代码
将所有全局app.set/app.get的调用改为基于req.session的用户级存储,同时优化冗余逻辑:
- 改造
/saveUser接口:
userInvoices.post('/saveUser', async (req, res) => { try { const User = req.body; const usersFromDB = await fetchUsersFromDB(); findCommonUser(usersFromDB, User); // 把用户信息存在当前用户的会话中,不是全局 req.session.user = User; res.status(200).json({msg: '用户信息保存成功'}); } catch (error) { console.log(error); res.status(500).json({msg: '保存失败'}); } })
- 改造发票匹配逻辑,删除全局存储:
// 直接返回筛选后的发票数组,不需要存入全局 function matchUserAndInvoice(dbInvoices, loggedInUser) { return dbInvoices.filter(DBElement => DBElement.customer_email === loggedInUser.email); }
- 改造
/fetchUserInvoices接口:
userInvoices.get('/fetchUserInvoices', async (req,res) => { try { // 先校验用户登录状态 if(!req.session.user) { return res.status(401).json({msg: '未登录'}); } // 优化:直接在数据库查询时过滤对应用户的发票,不用查全表再过滤,性能更高 const userInvoices = await InvoiceModel.find({customer_email: req.session.user.email}); res.json(userInvoices); } catch (error) { console.log(error); res.status(500).json({msg: '查询失败'}); } });
2. 更优的解决方案:JWT校验
你已经用了Auth0做认证,JWT(JSON Web Token)方案更适配前后端分离的MERN栈,比session方案更适合生产环境:
- 前端登录Auth0后会拿到JWT令牌,每次请求接口时放在请求头
Authorization: Bearer <你的JWT令牌>中 - 后端添加Auth0的JWT校验中间件,自动校验令牌合法性,解析出用户信息存在
req.user中 - 不需要服务端存储会话,服务端无状态,更容易做分布式部署,也不会出现会话过期、存储同步的问题
- 后续查询发票直接用
req.user.email作为过滤条件查询数据库即可,完全不会出现用户数据串用的问题。
现有代码其他可优化点
matchUserAndInvoice中使用forEach包裹异步逻辑的写法有问题,异步任务不会等待执行完成,容易返回空数组saveUser中使用updateMany没必要,用户邮箱是唯一标识,用updateOne即可- 全局调用的
clientCall()无实际作用,可以直接删除
内容的提问来源于stack exchange,提问作者AS10
相关产品推荐
相关产品推荐

