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

express-session设置req.session.isAuth后丢失问题求助

会话认证失效问题排查与修复建议

问题现象

  • 登录时在/login路由给req.session添加isAuth: true字段,控制台能看到会话对象包含该字段,但登录后访问首页时:
    • MongoDB会话存储中找不到isAuth字段
    • Chrome开发者工具看不到对应会话Cookie
    • /user路由的isAuth中间件中req.session.isAuth返回undefined
  • 已尝试的无效操作:
    • 配置后端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()
    ...
}

修复建议

  1. 修正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
    },
    
  2. 等待会话保存完成再返回响应
    req.session.save()是异步操作,直接返回响应会导致会话未保存就结束请求,添加await:

    await req.session.save();
    return res.status(201).send("Authentication Successful");
    
  3. 前端登录请求必须携带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",
    })
    
  4. 修正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} - - -`)
        })
    })
    
  5. 修正用户查询的判断逻辑
    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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 06:09:52