Node.js passport认证failureRedirect登录失败不跳转无限加载问题
问题现象
- 正确账号密码登录流程运行正常
- 输入正确邮箱、错误密码提交登录时,页面无限加载,未按照
failureRedirect配置跳转回登录页
原有代码参考
server.js
require('dotenv').config(); const express = require('express'); const app = express(); const passport = require('passport'); const initializePassport = require('./passport-config'); const session = require('express-session'); const methodOverride = require('method-override'); initializePassport( passport, async email=>{ try{ let bookshelfUser = await createBookshelfOf("User"); return await new bookshelfUser().where("email", email).fetch().then((data)=>{ return data.attributes; }); } catch (e) { console.log(e); return null; }}, async id=>{ try{ let bookshelfUser = await createBookshelfOf("User"); return await new bookshelfUser().where("id", id).fetch().then((data)=>{ return data.attributes; }); } catch (e) { return null; }}); app.post('/login', passport.authenticate('local', { successRedirect : '/', failureRedirect : '/login', }) );
passport-config.js
const LocalStrategy = require('passport-local').Strategy; const bcrypt = require('bcrypt'); function initialize(passport, getUserByEmail, getUserById) { console.log("Passport initialized"); const authenticateUser = async (email, password, done) => { const user = await getUserByEmail(email); if (user == null) { console.log("No user with that email"); return done(null, false, { message: 'No user with that email' }); } try { await bcrypt.compare(password, user.geslo, (err, result)=>{ if (err){ console.log("Password incorrect"); return done(null, false, { message: 'Password incorrect' }); } if(result){ console.log("User logged in successfully: " + user.username); return done(null, user); } }); } catch (e) { console.log(e); return done(e) } } passport.use(new LocalStrategy({ usernameField: 'email' }, authenticateUser)); passport.serializeUser((user, done) => done(null, user.id)); passport.deserializeUser(async(id, done) => { return done(null, await getUserById(id)); }) } module.exports = initialize
问题根因
核心问题出在passport-config.js的密码比对逻辑:
- 分支覆盖不全:
bcrypt.compare的回调里仅处理了比对报错、比对成功两个场景,当密码错误result === false时,没有调用Passport要求的done()回调,Passport始终收不到认证结果,请求会一直挂起,最终表现为页面无限加载 - 逻辑理解错误:
bcrypt.compare回调中的err参数仅代表比对过程出现系统异常(比如哈希值格式非法),不是密码错误的标识,原代码把系统异常直接判定为密码错误,会掩盖真实的运行报错 - 写法不规范:混用
async/await和回调函数写法,容易出现逻辑遗漏
修复方案
修改passport-config.js中的authenticateUser方法,统一使用async/await写法,补全所有分支的done()调用:
const authenticateUser = async (email, password, done) => { const user = await getUserByEmail(email); if (user == null) { console.log("No user with that email"); return done(null, false, { message: 'No user with that email' }); } try { // 直接await获取比对结果,不传入回调 const isPasswordMatch = await bcrypt.compare(password, user.geslo); if (isPasswordMatch) { console.log("User logged in successfully: " + user.username); return done(null, user); } // 补全密码不匹配的分支,必须调用done返回认证失败结果 console.log("Password incorrect"); return done(null, false, { message: 'Password incorrect' }); } catch (e) { console.log(e); return done(e); } }
补充配置检查
你贴出的server.js缺少几个Express和Passport的必要中间件,如果实际代码中也没配置,后续可能出现参数解析失败、会话不生效等问题,请在路由定义前补充以下代码:
// 解析POST表单参数 app.use(express.urlencoded({ extended: false })); // 配置session(secret请替换为你自己的环境变量值) app.use(session({ secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: false })); // 初始化Passport app.use(passport.initialize()); app.use(passport.session()); // 注册methodOverride中间件 app.use(methodOverride('_method'));
内容的提问来源于stack exchange,提问作者Jan Zajc
相关产品推荐
相关产品推荐

