为何每一次服务器请求都会向数据库存储一个Session?
问题:Express应用每次请求都创建无
passport信息的Session 问题现象
- 用户登录功能正常,
passport信息能保存到MongoDB会话并保持登录状态 - 数据库中出现大量不包含
passport字段的Session,几乎每一次服务器请求都会创建新的这类空Session
服务器核心代码
const express = require("express"); const bodyParser = require("body-parser"); const passport = require("passport"); const flash = require("express-flash"); var session = require('express-session'); const mongoose = require('mongoose'); const uri = process.env.MONGODB_URI; const MongoDBStore = require('connect-mongodb-session')(session); const initializePassport = require(__dirname+"/passport-config.js"); const methodOverride = require("method-override"); const PORT=process.env.PORT || 3000; const app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.use(express.static("public")); initializePassport(passport); app.use( session({ secret: process.env.SESSION_SECRET, resave: false, cookie: { maxAge: 1000 * 60 * 60 * 24 * 7 // 1 week }, saveUninitialized: false, store: new MongoDBStore({ uri: process.env.MONGODB_URI, collection: "sessions", mongooseConnection: mongoose.connection, }), }) ); app.use(flash()); app.use(passport.initialize()); app.use(passport.session()); app.use(methodOverride("_method")); app.use((req,res,next)=>{ user=req.user; next(); }) //routes ... /// mongoose.connection.once("open", () => { app.listen(PORT, () => { console.log(`Server started on port ${PORT}`); }); }); mongoose.connection.on("error", (err) => { console.error("MongoDB connection error:", err); });
数据库中的Session数据示例
正常登录后的Session(包含passport信息)
{"_id":"RaJ_m6eXHCD4favlq6E15tljWv-OiCjc","expires":{"$date":{"$numberLong":"1691015547496"}},"session":{"cookie":{"originalMaxAge":null,"expires":null,"secure":null,"httpOnly":true,"domain":null,"path":"/","sameSite":null},"flash":{},"passport":{"user":"646ef8adfa61e6eac440db91"}}}
无效Session(无passport信息,每次请求生成)
{"_id":"QvqtxeS1UCQPy9v-xsCThbqM8XPC5o4q","expires":{"$date":{"$numberLong":"1691015549321"}},"session":{"cookie":{"originalMaxAge":null,"expires":null,"secure":null,"httpOnly":true,"domain":null,"path":"/","sameSite":null},"flash":{}}}
已尝试的无效方案
- 调整
app.use(express.static("public"))和initializePassport(passport)的加载顺序,无效果 - 自定义
genid函数尝试复用已有SessionID,但导致用户passport信息无法保存,登录失败:
app.use(session({ secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: false, store: new MongoDBStore({ uri: process.env.MONGODB_URI, collection: "sessions", mongooseConnection: mongoose.connection, }), genid: function (req) { return req.sessionID || null; } }));
passport-config代码
const LocalStrategy = require("passport-local").Strategy; const bcrypt = require("bcrypt"); const db=require(__dirname+"/db.js"); function initialize(passport){ const authenticateUser = async (username,password,done) =>{ const user = await db.findUserByUsername(username.toLowerCase()); if(user==null){ return done(null, false, {message: "No user with that username"}) } try{ if (await bcrypt.compare(password, user.password)){ return done(null, user) } else{ return done(null, false, {message: "password incorrect"}) } } catch (e){ return done(e) } } passport.use(new LocalStrategy({usernameField: "username"},authenticateUser)); passport.serializeUser((user, done) => done(null, user.id)) passport.deserializeUser(async (id, done) => { const user=await db.findUserByID(id) done(null, user); }) } module.exports = initialize;
解决方案
原因分析
核心问题是静态资源请求触发了Session创建:虽然设置了saveUninitialized: false,但express-flash中间件会在请求时修改req.session.flash(哪怕只是初始化空对象),导致Session从"未初始化"变为"已初始化",进而被保存到数据库。此外,中间件顺序不合理也会让不必要的请求经过Session相关中间件。
具体修复步骤
- 调整中间件顺序,让静态资源请求绕开Session中间件
把express.static移到Session和Flash中间件之前,这样静态资源请求不会触发Session创建:
const app = express(); app.use(bodyParser.urlencoded({ extended: true })); // 先处理静态资源,不经过session/flash等中间件 app.use(express.static("public")); initializePassport(passport); // 后续再加载session相关中间件 app.use( session({ secret: process.env.SESSION_SECRET, resave: false, cookie: { maxAge: 1000 * 60 * 60 * 24 * 7, secure: process.env.NODE_ENV === "production", // 生产环境开启HTTPS Cookie sameSite: "lax", // 优化Cookie跨域携带逻辑 httpOnly: true }, saveUninitialized: false, store: new MongoDBStore({ uri: process.env.MONGODB_URI, collection: "sessions", mongooseConnection: mongoose.connection, }), }) ); app.use(flash()); // ... 其他中间件
- 规范全局变量赋值,避免意外修改
将全局user变量改为res.locals.user,避免污染全局作用域,同时不会触发Session修改:
app.use((req,res,next)=>{ res.locals.user = req.user; // 把用户信息挂载到响应局部变量 next(); })
- 验证Cookie配置
确保前端能正确接收并携带Session Cookie:生产环境开启secure: true,设置sameSite: "lax"保证Cookie在合法场景下正常发送,避免因Cookie无法携带导致重复创建Session。
验证方法
修改后重启服务,观察MongoDB的sessions集合:只有登录用户会生成包含passport字段的Session,静态资源和未登录页面请求不会再生成空Session。
内容的提问来源于stack exchange,提问作者ashtxn
相关产品推荐
相关产品推荐

