Mongoose从回调改Promise后,Passport的done函数失效致登录认证异常
问题:Mongoose改用Promise后Passport认证无法传递用户至req.user
由于Mongoose不再支持回调,我正在重构代码。虽然findOne()能找到用户,但无法将用户传递到routes.js中,导致isLoggedIn函数无法正常工作(req.user为undefined)。
旧代码:routes.js(isLoggedIn函数异常)
app.post('/login', passport.authenticate('local-login', { successRedirect : '/profile', // 重定向到安全的个人资料页面 failureRedirect : '/login', // 出错时重定向回登录页 failureFlash : true // 允许使用flash消息 })); // 使用路由中间件验证用户是否登录(isLoggedIn函数) app.get('/profile', isLoggedIn, function(req, res) { res.render('pages/profile.ejs', { user : req.user // 从会话中取出用户并传递给模板 }); }); // 验证用户是否登录的路由中间件 function isLoggedIn(req, res, next) { console.log("这里应该打印req.user,但它是undefined"); console.log(req.user); // 如果用户已在会话中认证,继续执行 if (req.isAuthenticated()){ return next(); } // 未认证则重定向到首页 res.redirect('/'); }
旧代码:passport.js
passport.use('local-login', new LocalStrategy({ // 默认本地策略用用户名和密码,这里替换为邮箱 usernameField : 'email', passwordField : 'password', passReqToCallback : true // 允许将整个请求传递给回调函数 }, function(req, email, password, done) { // 表单传来的邮箱和密码作为回调参数 User.findOne({ 'local.email' : email }, function(err, user) { console.log("正在查找用户"); // 有错误先返回错误 if (err) return done(err); // 未找到用户,返回提示消息 if (!user) return done(null, false, req.flash('loginMessage', '未找到该用户。')); // req.flash是用connect-flash设置闪存数据的方式 // 用户存在但密码错误 if (!user.validPassword(password)) return done(null, false, req.flash('loginMessage', '哎呀!密码错误。')); // 创建loginMessage并存入会话闪存 // 一切正常,返回认证成功的用户 return done(null, user); }); }));
重构后的passport.js(done()无法正常执行,认证失败)
passport.use('local-login', new LocalStrategy({ // 默认本地策略用用户名和密码,这里替换为邮箱 usernameField : 'email', passwordField : 'password', passReqToCallback : true // 允许将整个请求传递给回调函数 }, function(req, email, password, done) { // 表单传来的邮箱和密码作为回调参数 console.log(email + " 尝试登录"); // 查找邮箱与表单输入一致的用户 // 检查尝试登录的用户是否已存在 User.findOne({ 'local.email' : email }) .then(user=>{ if (!user){ return done(null, false, req.flash('loginMessage', '未找到该用户。')); // req.flash是用connect-flash设置闪存数据的方式 } if (!user.validPassword(password)){ return done(null, false, req.flash('loginMessage', '哎呀!密码错误。')); // 创建loginMessage并存入会话闪存 } req.user= user; return user; }) .then(user=>done(null,user)) .catch((err)=>{ return done(null, false); req.flash('loginMessage', '未找到该用户。')); // req.flash是用connect-flash设置闪存数据的方式 }); }));
问题分析与修复方案
你的新代码存在三个关键问题:
- 重复调用
done():第一个then分支已经在用户不存在/密码错误时调用了done(),后续then又重复调用,导致Passport逻辑混乱。 - 错误处理语法错误+逻辑错误:
catch块存在语法错误(多余闭合括号),且错误发生时未将err传递给done(),无法正确触发Passport的错误处理流程。 - 手动赋值
req.user无效:Passport会在认证成功后自动处理req.user的赋值和会话存储,手动设置反而可能干扰流程。
修复后的passport.js代码
passport.use('local-login', new LocalStrategy({ usernameField : 'email', passwordField : 'password', passReqToCallback : true }, async function(req, email, password, done) { // 改用async/await简化逻辑 console.log(`${email} 尝试登录`); try { const user = await User.findOne({ 'local.email': email }); if (!user) { return done(null, false, req.flash('loginMessage', '未找到该用户。')); } if (!user.validPassword(password)) { return done(null, false, req.flash('loginMessage', '哎呀!密码错误。')); } // 认证成功,返回用户,Passport自动处理会话与req.user赋值 return done(null, user); } catch (err) { // 传递数据库错误给Passport return done(err); } }));
修复说明
- 用
async/await替代Promise链式调用,避免链式调用的嵌套陷阱,代码更易维护。 - 移除无效的
req.user = user赋值,交给Passport自动处理用户会话与req.user的绑定。 - 每个分支仅调用一次
done(),避免重复调用导致的认证逻辑冲突。 - 错误处理时直接传递
err给done(),让Passport正确捕获并处理数据库异常。
内容的提问来源于stack exchange,提问作者garson
相关产品推荐
相关产品推荐

