You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何每一次服务器请求都会向数据库存储一个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相关中间件。

具体修复步骤

  1. 调整中间件顺序,让静态资源请求绕开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());
// ... 其他中间件
  1. 规范全局变量赋值,避免意外修改
    将全局user变量改为res.locals.user,避免污染全局作用域,同时不会触发Session修改:
app.use((req,res,next)=>{
  res.locals.user = req.user; // 把用户信息挂载到响应局部变量
  next();
})
  1. 验证Cookie配置
    确保前端能正确接收并携带Session Cookie:生产环境开启secure: true,设置sameSite: "lax"保证Cookie在合法场景下正常发送,避免因Cookie无法携带导致重复创建Session。

验证方法

修改后重启服务,观察MongoDB的sessions集合:只有登录用户会生成包含passport字段的Session,静态资源和未登录页面请求不会再生成空Session。

内容的提问来源于stack exchange,提问作者ashtxn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 15:24:51