Passport.js无法登出且无法删除Cookie问题求助
问题描述
我用Node.js开发了一个示例应用,基于Passport.js实现用户注册、登录和登出功能。注册和登录流程都能正常运行,但执行登出操作时,终端仅输出反序列化函数的日志,浏览器中的Cookie没有变化,调用logout函数后仍然能获取到req.user,用户并未真正登出。
相关代码
中间件配置
//middleware app.use(session({ secret: "secret", resave: true, saveUninitialized: true, cookie: {maxAge:60*60*1000}//1hour })); app.use(express.urlencoded({ extended: false })); app.use(express.json()); app.use(cors({ origin: "http://localhost:3000", credentials: true, })); app.use(cookieParser("secret")); app.use(passport.initialize()); app.use(passport.session());
Passport序列化/反序列化函数
passport.serializeUser((user, cb) => { console.log("serializing user"); cb(null, user.id); }); passport.deserializeUser((id, cb) => { User.findById(id).then((err, user) => { if(err) throw err; console.log("id: " + id); console.log("userid: " + user); const userInformation = { username: user.email, }; cb(err, user); }).catch((err) => {console.error(err)}); });
LocalStrategy配置
passport.use( new localStrategy(async (username, password, done) => { try{ const user = await User.findOne({username: username}); if(!user) { console.log("no user"); return done(null, false); } bcrypt.compare(password, user.password, (err, result) => { if(err) throw err; if(result === true) { return done(null, user); } else { return done(null, false); } }); } catch(err){ console.log("err inside localStrategy: " + err); throw err; } }) );
路由处理
登录路由
app.post("/login", (req, res) => { console.log("inside login"); passport.authenticate("local", (err, user, info) => { console.log("inside authenticate, user: " + user); if(err) { console.log("err: " + err); throw err; } if(!user) { console.log("No user"); res.status(400); res.send("No user exists"); } else { console.log("inside else"); req.logIn(user, (err) => { if(err) throw err; console.log("req.user: " + req.user); console.log("successfully authenticated"); res.status(200).json({message: "successfully authenticated here"}); }); } })(req, res); });
注册路由
app.post("/register", async (req, res) => { try { console.log("username: " + req.body.username); const existingUser = await User.findOne({username: req.body.username}); if(existingUser) { console.log("duplicate user"); return res.status(400).json({message: "User already exists"}); } console.log("password to be hashed: " + req.body.password); //hash password const newUser = new User({ username: req.body.username, password: req.body.password }); bcrypt.genSalt(10, (err,salt)=> bcrypt.hash(newUser.password, salt, (err, hash) =>{ if(err) throw err; newUser.password = hash; newUser.save() .then((value)=> { console.log("value: " + value); console.log("registered: " + newUser.username); return res.status(200).json({message: "User successfully registered"}); }) .catch(err => console.log(err)); } ) ); } catch (err) { console.log("error registering: " + err); res.status(500).json({message: "Internal srever error"}); } });
登出路由
app.get("/logout", function(req, res){ req.logout(); req.session.destroy(function(err){ if(err){ console.log("error: " + err); res.status(500).json({message: "Error destroying session"}); } else { console.log("destroying session"); res.clearCookie('mcookie'); res.status(200).json({message: "Logged out successfully"}); } }); });
问题分析与解决方案
核心问题点
- 反序列化函数写法错误:Mongoose的
findById返回的Promise,成功回调的第一个参数是查询结果而非错误。当前代码把用户数据当成错误传入cb(err, user),导致Passport无法正确处理会话状态,还会触发不必要的反序列化日志。 - 登出逻辑不完整:
- 新版本Passport的
req.logout()是异步操作,未等待完成就销毁会话会导致清理不彻底。 - 清理Cookie时使用了错误的名称:express-session默认会话Cookie名称是
connect.sid,不是mcookie,导致Cookie未被清除。 - 未手动清除
req.user,导致后续请求仍能读取到用户信息。
- 新版本Passport的
修复步骤
1. 修正反序列化函数
passport.deserializeUser((id, cb) => { User.findById(id) .then(user => { if (!user) { return cb(new Error("User not found")); } console.log("id: " + id); console.log("user: " + user); cb(null, user); }) .catch(err => { console.error(err); cb(err); }); });
2. 修正登出路由
app.get("/logout", async function(req, res){ // 异步执行logout,确保会话状态清理完成 await new Promise((resolve, reject) => { req.logout(err => { if (err) reject(err); else resolve(); }); }); req.session.destroy(function(err){ if(err){ console.log("error: " + err); return res.status(500).json({message: "Error destroying session"}); } // 清理默认的会话Cookie,匹配session配置的参数 res.clearCookie('connect.sid', { path: '/', httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax' }); // 手动清除req.user req.user = null; console.log("destroying session"); res.status(200).json({message: "Logged out successfully"}); }); });
3. 优化Session配置(推荐)
app.use(session({ secret: "your-strong-secret-here", // 替换为安全的随机密钥 resave: false, // 避免不必要的会话存储 saveUninitialized: false, // 不存储未初始化的会话,符合隐私规范 cookie: { maxAge: 60*60*1000, httpOnly: true, // 防止XSS攻击 secure: process.env.NODE_ENV === 'production', // 生产环境启用HTTPS sameSite: 'lax' // 防范CSRF攻击 } }));
额外注意事项
- 前端发起登出请求时,需设置
credentials: true,否则浏览器不会传递Cookie,导致清理失败。 - 生产环境下,Session密钥不要硬编码,改用环境变量管理。
内容的提问来源于stack exchange,提问作者m0j1
相关产品推荐
相关产品推荐

