MERN栈passport.js 0.5.3版本req.logout()登出功能失效问题
问题根因
req.logout()失效、登出路由内req.user为undefined是三个配置错误共同导致的:
- 序列化/反序列化逻辑不匹配:
serializeUser阶段将第三方返回的原始profile对象全量存入session,deserializeUser阶段却固定按GoogleID查询数据库,Facebook登录的用户不存在该字段,反序列化直接失败,自然拿不到req.user,登出逻辑没有操作对象自然不生效。 - Passport 0.5.x版本的
req.logout()是异步接口,当前代码同步调用后直接返回响应,登出逻辑还没执行完会话就已经结束。 - 登出后未主动清理session和对应cookie,即使passport清除了会话数据,旧cookie仍会被后续请求携带。
修复步骤
1. 修正passport序列化/反序列化逻辑
不要把整个第三方profile对象存进session,只存自有数据库的用户唯一主键,反序列化时统一按主键查询,兼容所有登录渠道的用户,修改passport.js对应代码:
// 序列化:仅存储MongoDB生成的用户主键_id,减少session存储体积 passport.serializeUser((user, done) => { done(null, user._id); }); // 反序列化:按用户主键查询,兼容Google、Facebook等所有登录方式的用户 passport.deserializeUser((userId, done) => { User.findById(userId) .then((user) => done(null, user)) .catch(done) });
同时修改两个社交登录策略的回调逻辑,不要把第三方原始profile传给done,要传自有数据库中查询/新建的用户对象,修复原代码中变量作用域导致新建用户无法传递的问题:
// Google策略回调修改 async function (accessToken, refreshToken, profile, done) { const id = profile.id; const name = profile.displayName; const email = profile.emails[0].value; const picture = profile.photos[0].value; // 用let声明,方便后续赋值 let user = await User.findOne({ GoogleID: id }); const userSameEmail = await User.findOne({ email: email }); if (!user && !userSameEmail) { user = new User({ GoogleID: id, name, email, picture, }); await user.save(); } // 传入自有库的用户对象,不要传第三方原始profile done(null, user || userSameEmail); } // Facebook策略回调修改 async function (accessToken, refreshToken, profile, done) { const id = profile.id; const name = profile.name; const email = profile.emails[0].value; let userSameEmail = await User.findOne({ email: email }); let user = await User.findOne({ fbID: id }); if (!user && !userSameEmail) { user = new User({ fbID: id, name, email, }); await user.save(); } done(null, user || userSameEmail); }
2. 修正登出路由逻辑
适配Passport 0.5.3的异步logout接口,等登出逻辑执行完成后主动清理session和cookie,修改auth.js中的登出路由:
routerLogin.post("/logout", (req, res) => { req.logout((err) => { if (err) return res.status(500).send(err); // 清除当前session数据 req.session = null; // 主动清除对应cookie,名称和cookieSession配置的name保持一致 res.clearCookie("SocialMedia-auth-session"); res.send({ success: true }); }); });
3. 补充前端请求配置
跨域场景下前端发登出请求必须携带cookie,否则后端无法识别对应会话,以axios为例需要加如下配置:
axios.post("http://localhost:8000/logout", {}, { withCredentials: true })
验证标准
修改完成后重启服务,重新走社交登录流程再触发登出:
- 登录成功后访问任意业务路由,
req.user可以正常打印出自有库的用户对象,不会出现undefined - 登出路由中可以正常获取到
req.user值 - 登出完成后再访问需要登录鉴权的路由,
req.user会变回undefined
内容的提问来源于stack exchange,提问作者CodeLearner
相关产品推荐
相关产品推荐

