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
相关产品推荐
相关产品推荐

