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

Express Session登录路由定义后,其他路由session变量为undefined求助

问题:express-session 会话无法跨路由读取,req.session.userid 始终为 undefined

我基于 Node.js+MongoDB 后端与 React 前端开发项目,需要存储用户会话并在客户端请求接口时获取会话信息。尝试过 express-session 和 client-sessions,均无效:用户登录时已将 user id 存入 session 对象,但在其他路由中访问该 session 对象时始终为 undefined。


后端核心代码

import express, { json, urlencoded } from 'express';
import cors from 'cors';
import dotenv from 'dotenv';
import connectToDB from './utils/database.js';
import MongoStore from 'connect-mongo';
import session from 'express-session';

import authRouter from './routes/auth.routes.js';
import attendanceRouter from './routes/attendance.routes.js';
import userRouter from './routes/user.routes.js';


dotenv.config();

const app = express();
app.disable('x-powered-by');
app.use(express.static('public'));
app.use(cors({
    origin: 'http://localhost:3000',  // Frontend URL
    credentials: true,
}));
connectToDB();
app.use(session({
    secret: process.env.SESSION_SECRET,
    resave: false,
    saveUninitialized: true,
    store: MongoStore.create({ mongoUrl: process.env.MONGO_URI, collectionName: 'sessions' }),
    cookie: {
        maxAge: 1000 * 60 * 60 * 24 // 1 day
    },
}));
app.use(json());
app.use(urlencoded({ extended: true }));

app.use('/', authRouter);
app.use('/attendance', attendanceRouter);
app.use('/user', userRouter);

app.listen(process.env.PORT || 5000, () => {
    console.log('Server is running on port 3000');
})

Auth 控制器代码

import { User } from "../models/user.model.js";
import bcrypt from 'bcryptjs';

const registerPost = async (req, res) => {
    const { username, password, isGuard, phoneNumber, lastSubmitted } = req.body;
    const hashedPassword = bcrypt.hashSync(password, 10);
    const alreadyExistingUser = await User.findOne({ username });

    if (alreadyExistingUser) {
        console.log("User already exists");
        return res.status(400).json({
            message: "User already exists"
        });
    }

    const user = new User({
        username,
        password: hashedPassword,
        isGuard,
        phoneNumber,
        lastSubmitted
    });

    console.log("Registration end point hit");

    try {
        await user.save();
        req.session.userid = user._id;
        res.status(201).send('User created successfully');
    } catch (error) {
        res.status(400).send(error.message);
    }
}

const loginPost = async (req, res) => {
    const { username, password } = req.body;
    const user = await User.findOne({ username });

    if (!user) {
        console.log("User not found");
        return res.status(404).send('User not found');
    }

    const isMatch = bcrypt.compareSync(password, user.password);
    if (!isMatch) {
        console.log("Invalid password");
        res.status(401).send('Invalid password');
    }

    req.session.userid = user._id;
    res.status(200).send('Logged in successfully');
}

const isSignedIn = async (req, res) => {
    if (req.session.userid) { // this is always undefined even after logging in
        const user = await User.findById(req.session.userid);

        return res.status(200).json({
            username: user.username,
            isGuard: user.isGuard,
            phoneNumber: user.phoneNumber
        });
    } else {
        return res.status(401).send('You are not logged in');
    }
}

export default {
    registerPost,
    loginPost,
    isSignedIn
}

前端核心代码

import React, { useEffect, useState } from 'react';
import Cookies from 'js-cookie';
import axios from 'axios';

const Dashboard = () => {

    // const [cookie, setCookie] = useState('');

    // useEffect(async () => {
    //     const cookieValue = await Cookies.get('userid');
    //     console.log(cookieValue)
    //     console.log(cookieValue.userid)
    //     setCookie(cookieValue);
    // }, [])

    const handleClick = async () => {
        const response = await axios.get('http://localhost:5000/issigned');
        console.log(response);
    }

    return (
        <div>
            <h1 onClick={handleClick}>Dashboard</h1>
        </div>
    );
}

export default Dashboard;

前后端 package.json

后端

{
  "name": "ideaboard",
  "version": "1.0.0",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "dev": "nodemon index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "bcryptjs": "^2.4.3",
    "client-sessions": "^0.8.0",
    "connect-mongo": "^5.1.0",
    "cors": "^2.8.5",
    "dotenv": "^16.4.5",
    "express": "^4.19.2",
    "express-session": "^1.18.0",
    "helmet": "^7.1.0",
    "moment": "^2.30.1",
    "mongoose": "^8.5.2",
    "multer": "^1.4.5-lts.1"
  },
  "devDependencies": {
    "nodemon": "^3.1.4"
  }
}

前端

{
  "name": "client",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "axios": "^1.7.3",
    "js-cookie": "^3.0.5",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-router-dom": "^6.26.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

问题排查与修复方案

1. 前端请求必须携带凭证

axios 默认不会跨域携带 Cookies,导致后端无法识别用户会话。修改所有请求(包括登录、注册、isSignedIn),添加 withCredentials: true:

// 示例:修改isSignedIn请求
const handleClick = async () => {
    const response = await axios.get('http://localhost:5000/issigned', {
        withCredentials: true
    });
    console.log(response);
}

// 登录请求也要修改
// axios.post('http://localhost:5000/login', {username, password}, {withCredentials: true})

2. 优化后端 Session 配置

补充 Cookie 的跨域属性,确保会话 Cookie 能在跨域场景下正常传递:

app.use(session({
    secret: process.env.SESSION_SECRET,
    resave: false,
    saveUninitialized: false, // 避免创建空会话,减少无效存储
    store: MongoStore.create({ mongoUrl: process.env.MONGO_URI, collectionName: 'sessions' }),
    cookie: {
        maxAge: 1000 * 60 * 60 * 24,
        sameSite: 'lax', // 允许跨域请求携带Cookie
        secure: process.env.NODE_ENV === 'production', // 生产环境开启(需HTTPS)
        httpOnly: true // 防止XSS攻击,禁止前端JS访问Cookie
    },
}));

3. 确保中间件顺序正确

session 中间件必须在 json()、urlencoded() 和路由挂载之前,你的当前代码顺序是正确的,保持即可。

4. 手动确保会话保存(可选)

在登录/注册的异步操作后,手动调用 req.session.save() 确保会话被持久化:

// 登录示例
req.session.userid = user._id;
req.session.save(err => {
    if (err) console.error('会话保存失败:', err);
    res.status(200).send('Logged in successfully');
});

5. 验证会话存储

登录后检查 MongoDB 的 sessions 集合,确认是否有新的会话文档生成。如果没有,检查:

  • MONGO_URI 是否正确,数据库连接是否成功
  • connect-mongo 与 express-session、mongoose 的版本兼容性(当前版本组合无问题)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 18:35:58