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

Express中req.session.userId参数丢失问题求助

会话无法保留userId参数的问题排查与修复

问题描述

在/login路由中设置req.session.userId后,控制台打印显示该参数已存在于req.session中,但访问/check-auth路由时,无法获取到包含userId的会话数据,导致无法通过会话判断用户登录状态。

问题根源与修复方案

1. MongoDB连接字符串语法错误

原代码中mongoose的连接字符串末尾多了一个],这会导致数据库连接异常,进而影响会话的持久化存储。
修复:

// 移除末尾多余的]
const mongoDB = "mongodb://127.0.0.1:27017/project";

2. 未定义server变量导致退出时报错

process.on('SIGINT')中调用server.close(),但server变量未通过app.listen()的返回值定义,会导致进程退出时无法正确关闭服务器,可能影响会话数据的保存。
修复:

// 将app.listen的返回值赋值给server
const server = app.listen(port, () => {
    console.log(`Example app listening on port ${port}`);
});

3. 会话保存时机问题

直接在设置req.session.userId后发送响应,可能会因为会话异步保存未完成,导致数据未写入MongoDB。
修复:
在req.session.save()的回调中再发送响应,确保会话数据持久化完成:

app.post("/login", async (req, res) => {
    try {
        let { username, password } = req.body;
        let user = await users.findOne({ username: username });

        if (user && (await bcrypt.compare(password, user.passwordHash))) {
            req.session.userId = user._id;
            // 等待会话保存完成后返回响应
            req.session.save((err) => {
                if (err) {
                    console.error("Session save error:", err);
                    return res.status(500).json("Internal server error");
                }
                console.log("Session:", req.session);
                res.status(200).send("Login successful!");
            });
        } else {
            res.status(401).json({ error: "Invalid credentials" });
        }
    } catch (error) {
        console.error("Internal server error", error.message);
        res.status(500).json("Internal server error");
    }
});

4. 前端请求未携带凭证

如果前端未配置携带会话凭证,浏览器不会自动发送会话Cookie,后端自然无法识别用户会话。
前端配置示例:

  • Axios:
axios.get('http://localhost:8000/check-auth', { withCredentials: true })
  • Fetch:
fetch('http://localhost:8000/check-auth', { credentials: 'include' })

5. 可选:调整Cookie的domain配置

部分浏览器对localhost域名的Cookie处理存在差异,可尝试注释掉domain配置,让Express自动适配:

cookie: {
    maxAge: 24 * 60 * 60 * 1000,
    // domain: "localhost", // 注释掉该配置
    secure: false,
    httpOnly: true,
}

修复后的完整代码

const express = require("express");
const session = require("express-session");
const MongoStore = require("connect-mongo");
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const cors = require("cors");

const users = require("./models/user");

const app = express();
const port = 8000;
const saltRounds = 10;

// 修正MongoDB连接字符串
const mongoDB = "mongodb://127.0.0.1:27017/project";
mongoose
    .connect(mongoDB, { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => console.log("Connected to database"))
    .catch((error) => console.error("Error connecting to database:", error));
const db = mongoose.connection;

db.on("error", console.error.bind(console, "MongoDB connection error:"));

db.on("connected", function () {
    console.log("Connected to database");
});

app.use(
    session({
        secret: "Super secret secret",
        cookie: {
            maxAge: 24 * 60 * 60 * 1000,
            // domain: "localhost", // 可选:注释掉适配部分浏览器
            secure: false,
            httpOnly: true,
        },
        resave: false,
        saveUninitialized: false,
        store: MongoStore.create({
            mongoUrl: "mongodb://127.0.0.1:27017/project",
        }),
    })
);

app.use(
    cors({
        origin: "http://localhost:3000",
        methods: ["POST", "PUT", "GET", "OPTIONS", "HEAD"],
        credentials: true,
    })
);

app.use(express.json());

// 定义server变量
const server = app.listen(port, () => {
    console.log(`Example app listening on port ${port}`);
});

process.on("SIGINT", () => {
    console.log("Server closed. Database instance disconnected");
    server.close(() => {
        db.close(() => {
            process.exit(0);
        });
    });
});

app.post("/login", async (req, res) => {
    try {
        let { username, password } = req.body;
        let user = await users.findOne({ username: username });

        if (user && (await bcrypt.compare(password, user.passwordHash))) {
            req.session.userId = user._id;
            // 等待会话保存完成后响应
            req.session.save((err) => {
                if (err) {
                    console.error("Session save error:", err);
                    return res.status(500).json("Internal server error");
                }
                console.log("Session:", req.session);
                res.status(200).send("Login successful!");
            });
        } else {
            res.status(401).json({ error: "Invalid credentials" });
        }
    } catch (error) {
        console.error("Internal server error", error.message);
        res.status(500).json("Internal server error");
    }
});

app.get("/check-auth", async (req, res) => {
    try {
        console.log("Session in check-auth:", req.session);
        console.log("Session User:", req.session.userId);
        if (req.session && req.session.userId) {
            let user = await users.findById(req.session.userId);
            console.log("User Details:", user);
            if (user) {
                res.status(200).json({ authenticated: true, user });
            } else {
                res.status(401).json({ authenticated: false });
            }
        } else {
            res.status(200).json({ authenticated: false });
        }
    } catch (error) {
        console.error("Error checking authentication: ", error);
        res.status(500).json({
            authenticated: false,
            error: "Internal server error",
        });
    }
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 13:44:54