Passport登录后Session无passport字段、req.user缺失问题排查
Passport本地登录Session丢失问题排查与解决
问题描述
使用Passport进行本地登录时,登录流程无报错,但session无法持久保存passport字段,请求对象中不存在req.user;登录后访问受保护的/welcome路由,返回「not authorization user」提示。
相关代码
初始化配置的initExpress函数
export default function initExpress(express, app) { app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(cors()); setDbSessions(app, process.env.DB_URL); // 数据库与Session配置 // 调试中间件 app.use((req, res, next) => { console.log(" ----------------------------------- "); console.log('Session:', req.session); console.log(" ----------------------------------- "); next(); }); app.get("/welcome", isAuth, (req, res) => { console.log(req.session); res.send("<h1> welcome boss </h1>"); }); app.post("/pass-login", passport.authenticate("local"), (req, res) => { console.log(req.session); res.send("<h1> welcome from auth password login </h1>"); }); }
数据库与Session配置函数
function setDbSessions(app, url) { mongoose.connect(url,{}).catch(error => console.log("App.js mongoose.connect error",error)); let db = mongoose.connection; db.on('error', console.error); db.once('open', function(){ console.log("App is connected to DB", db.name) }); app.use(session({ secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: false, store: MongoStore.create({ client: mongoose.connection.getClient() }), cookie: { maxAge: 1000 * 60 * 60 * 24 }, })); app.use(passport.initialize()); app.use(passport.session()); passport.use(localStratage); }
Passport策略文件
import { Strategy } from "passport-local"; import { comparePassword } from "./bcrypt.mjs"; import { User } from "../dbScheme/UserScheme.mjs"; import passport from "passport"; const customFields = { username: 'username', password: 'password', }; const checkValidFields = (username, password, done) => { User.findOne({ username: username }) .then((user) => { if (!user) return done(null, false); const match = comparePassword(password, user.password); if (match) { return done(null, user); }else { return done(null, false); } }) .catch((err) => { console.log(err); done(err); }); }; passport.serializeUser((user, done) => { done(null, user.id); }); passport.deserializeUser((userId, done) => { User.findById(userId) .then((user) => { done(null, user); }) .catch(err => done(err)) }); const localStratage = new Strategy(customFields, checkValidFields); export default localStratage;
身份验证中间件isAuth
export const isAuth = (req, res, done) => { console.log(req.session); if (req.isAuthenticated()) { done(); } else { res.status(404).send("<h1> not authorzation user </h1>"); } };
问题现象
- 登录
/pass-login端点后输出的Session:
Session { cookie: { path: '/', _expires: 2024-07-14T11:27:14.100Z, originalMaxAge: 86400000, httpOnly: true }, passport: { user: '66925ade46279d6d21a6491e' } }
- 访问
/welcome时调试中间件输出的Session:
Session { cookie: { path: '/', _expires: 2024-07-14T11:36:32.934Z, originalMaxAge: 86400000, httpOnly: true } }
原因分析
- MongoStore初始化时机错误:Session和Passport中间件在MongoDB连接建立前就已初始化,导致MongoStore无法正常连接数据库,Session无法持久化存储,每次请求都会生成新的Session,
passport字段自然丢失。 - 密码校验异步处理错误:
comparePassword(通常基于bcrypt,返回Promise)被同步赋值,导致match是Promise对象而非布尔值,可能引发逻辑错误(即使密码不匹配也会通过验证)。 - Session未手动保存:
saveUninitialized: false配置下,Passport修改Session后可能未触发自动保存机制,导致Session变更未写入数据库。 - 身份验证中间件参数错误:
isAuth中间件将第三个参数写为done,但Express中间件应使用next,导致认证通过后无法继续执行后续路由逻辑。
解决方案
1. 修正MongoStore初始化时机
将Session和Passport的初始化逻辑移至MongoDB连接成功后的回调中,确保数据库连接就绪:
function setDbSessions(app, url) { mongoose.connect(url,{}).catch(error => console.log("App.js mongoose.connect error",error)); let db = mongoose.connection; db.on('error', console.error); db.once('open', function(){ console.log("App is connected to DB", db.name); // 连接建立后再初始化Session和Passport app.use(session({ secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: false, store: MongoStore.create({ client: db.getClient() }), cookie: { maxAge: 1000 * 60 * 60 * 24 }, })); app.use(passport.initialize()); app.use(passport.session()); passport.use(localStratage); }); }
2. 正确处理密码校验的异步逻辑
将checkValidFields改为异步函数,使用await处理密码比对:
const checkValidFields = async (username, password, done) => { try { const user = await User.findOne({ username: username }); if (!user) return done(null, false); // 等待密码比对结果 const match = await comparePassword(password, user.password); if (match) { done(null, user); } else { done(null, false); } } catch (err) { done(err); } };
3. 登录成功后手动保存Session
在登录回调中调用req.session.save(),确保Session变更被持久化:
app.post("/pass-login", passport.authenticate("local"), (req, res) => { req.session.save((err) => { if (err) console.error("Session保存失败:", err); console.log(req.session); res.send("<h1> welcome from auth password login </h1>"); }); });
4. 修正身份验证中间件参数
将isAuth的第三个参数改为next,并使用正确的HTTP状态码:
export const isAuth = (req, res, next) => { console.log(req.session); if (req.isAuthenticated()) { next(); // 调用next进入后续路由 } else { res.status(401).send("<h1> not authorization user </h1>"); // 401表示未授权,而非404 } };
内容的提问来源于stack exchange,提问作者Yossef Sabry
相关产品推荐
相关产品推荐

