express-session设置req.session.isAuth后丢失问题求助
会话认证失效问题排查与修复建议
问题现象
- 登录时在
/login路由给req.session添加isAuth: true字段,控制台能看到会话对象包含该字段,但登录后访问首页时:- MongoDB会话存储中找不到
isAuth字段 - Chrome开发者工具看不到对应会话Cookie
/user路由的isAuth中间件中req.session.isAuth返回undefined
- MongoDB会话存储中找不到
- 已尝试的无效操作:
- 配置后端CORS选项
- 给登录请求添加
credentials: 'include'触发错误 - 调用
res.end()或req.session.save()无法保存会话
关键日志对比
/login路由中console.log(req.session)输出:
Session { cookie: { path: '/', _expires: blahblahblah, originalMaxAge: blahblahblah, httpOnly: true, secure: true }, isAuth: true, _id: new ObjectId('blahblahblah') }
/user路由中console.log(req.session)输出:
Session { cookie: { path: '/', _expires: blahblahblah, originalMaxAge: blahblahblah, httpOnly: true, secure: true } }
相关代码片段
后端index.js核心代码
import 'dotenv/config' const PORT = 8080; import cors from 'cors'; import mongoose from 'mongoose'; import express from "express"; import session from 'express-session'; import MongoStore from 'connect-mongo'; import bcrypt from 'bcrypt'; import User from './models/userModel.js' import { checkLogin } from './validation.js'; import { validationResult } from 'express-validator'; const app = express(); app.use(express.urlencoded({ extended: true })); app.use(express.json()); app.use(cors({ origin: "http://localhost:3000", credentials: true, methods: ["GET", "PATCH", "POST", "DELETE"], allowedHeaders: ["Content-Type", "Authorization"], optionsSuccessStatus: 200 })); const clientP = mongoose.connect( mongoUrl, { dbName: 'dbName' } ) .then(m => m.connection.getClient()) .then(() => { console.log('MongoDB Connected') app.listen(PORT, () => { console.log(`- - - Listening on PORT ${PORT} - - -`) }) }) .catch(err => console.log(err)); app.use(session({ secret: secret, resave: false, saveUninitialized: false, store: MongoStore.create({ mongoUrl: mongoUrl, dbName: "dbName", collectionName: 'the_collection_of_sessions', clientPromise: clientP, autoRemove: 'native', autoRemoveInterval: 15, crypto: { secret: secret }, ttl: 14 * 24 * 60 * 60, }), cookie: { secure: true, maxAge: 14 * 24 * 60 * 60, }, })); const isAuth = (req, res, next) => { console.log(req.session.isAuth); // 返回undefined if (req.session.isAuth) { console.log('is auth is true!') next(); } else { console.log('is auth is false!') return res.status(400).send({ message: "Authentication Failed" }); } } app.post("/login", checkLogin(), async (req, res) => { try { validationResult(req); const { username, email, password } = req.body; if (username === undefined) { console.log('set up email verification!') } const user = await User.find({ 'user.username': username }) if (user === null) { console.log('user does not exist') return res.status(400).send({ message: "Authentication Failed" }); } if (await bcrypt.compare(password, user[0].user.password)) { req.session._id = user[0]._id req.session.isAuth = true; req.session.save() return res.status(201).end("Authentication Successful"); } else { return res.status(400).end("Authentication Failed"); } } catch (error) { console.log(error.message) res.status(500).send({ message: error.message }) } }); app.get("/user", isAuth, async (req, res) => { const user = await User.find({ _id: req.session._id }) console.log(user) if (user === null) { return res.status(400).send({ message: "Authentication Failed" }); } return res.status(201).json(user); });
前端登录请求代码
const res = await fetch('http://localhost:8080/login', { method: 'POST', headers: {"Content-Type": "application/json"}, body: JSON.stringify(user), // credentials: "include", }) if (res.status === 201) { router.refresh(); router.push('/'); } else { router.push('/not-found'); }
前端/user请求代码
async function getUser() { const user = await fetch("http://localhost:8080/user", { method: 'GET', credentials: 'include' }) return user; } export default async function Home() { const user = await getUser() ... }
修复建议
修正Cookie配置
本地开发用http协议时,cookie.secure: true会导致浏览器拒绝保存Cookie,同时maxAge单位是毫秒,之前的写法是秒会导致Cookie立即过期,改成:cookie: { secure: process.env.NODE_ENV === 'production', maxAge: 14 * 24 * 60 * 60 * 1000, sameSite: 'none', // 跨域场景必须设置,配合credentials使用 httpOnly: true },等待会话保存完成再返回响应
req.session.save()是异步操作,直接返回响应会导致会话未保存就结束请求,添加await:await req.session.save(); return res.status(201).send("Authentication Successful");前端登录请求必须携带
credentials: 'include'
之前触发错误是因为Cookie配置和CORS不匹配,修正前端代码:const res = await fetch('http://localhost:8080/login', { method: 'POST', headers: {"Content-Type": "application/json"}, body: JSON.stringify(user), credentials: "include", })修正MongoDB会话存储连接顺序
当前session中间件初始化时,MongoDB可能还未完成连接,改成先等待连接完成再挂载中间件:clientP.then(client => { app.use(session({ secret: secret, resave: false, saveUninitialized: false, store: MongoStore.create({ client: client, dbName: "dbName", collectionName: 'the_collection_of_sessions', autoRemove: 'native', autoRemoveInterval: 15, crypto: { secret: secret }, ttl: 14 * 24 * 60 * 60, }), cookie: { secure: process.env.NODE_ENV === 'production', maxAge: 14 * 24 * 60 * 60 * 1000, sameSite: 'none', httpOnly: true }, })); // 挂载路由 app.post("/login", ...); app.get("/user", ...); app.listen(PORT, () => { console.log(`- - - Listening on PORT ${PORT} - - -`) }) })修正用户查询的判断逻辑
User.find()返回数组,永远不会等于null,改成判断数组长度:const user = await User.find({ 'user.username': username }) if (user.length === 0) { console.log('user does not exist') return res.status(400).send({ message: "Authentication Failed" }); }
内容的提问来源于stack exchange,提问作者Felt_Notebook
相关产品推荐
相关产品推荐

