Express+React项目中req.user未定义且passport.deserializeUser未触发问题排查
问题描述
我查遍了所有类似问题,但没找到适配我情况的解决方案。我在做一个简单的Todo应用,想集成passport.js。当前技术栈:
- 独立运行的Express应用(端口3001)和React应用(端口3000)
- Typescript
- express-session
- passport-google-oauth20
谷歌策略登录流程看起来没问题:用户会被存入数据库(不存在的话),跳转首页也正常,但后续请求里打印req.user显示undefined,而且passport.deserializeUser函数里的日志根本没输出。
Passport配置
app.use(passport.initialize()); app.use(passport.session()); passport.use( new GoogleAuth.Strategy( { clientID:"HIDDEN", clientSecret: "HIDDEN", callbackURL: "/auth/google/callback", }, async ( accessToken: string, refreshToken: string, profile: GoogleAuth.Profile, done: GoogleAuth.VerifyCallback ) => { let user = await getGoogleUser(profile.id); if (user) done(null, user); else { const userObj: any = { google: { id: profile.id, name: profile.displayName, email: profile._json.email, }, }; const newUser = await createGoogleUser(userObj); if (newUser) done(null, newUser); else done(new Error("error in google auth")); } }));
认证路由
router.get("/google", passport.authenticate("google", { scope: ["profile", "email"] })); router.get("/google/callback", passport.authenticate("google", { successRedirect: "http://localhost:3000", }) );
Serialize和Deserialize User
passport.serializeUser((user: any, done) => { done(null, user.id); }); passport.deserializeUser(async (id: string, done) => { const user = await getUser(id); console.log("deserializeUser"); done(null, user); });
Session配置
app.use( session({ secret: "testasdasdcode", saveUninitialized: false, resave: false, cookie: { maxAge: 1000 * 60 }, }) );
前端登录按钮处理
window.location.href = "http://localhost:3001/auth/google";
(我知道这是不良实践,目前仅用于测试)
可能的问题及解决方案
1. Session中间件顺序错误
Express中间件按执行顺序生效,session必须在passport.initialize()和passport.session()之前加载。如果你的代码里session配置在passport初始化之后,直接调整顺序:
// 先加载session app.use( session({ secret: "testasdasdcode", saveUninitialized: false, resave: false, cookie: { maxAge: 1000 * 60 }, }) ); // 再初始化passport app.use(passport.initialize()); app.use(passport.session());
Passport依赖session中间件读取会话信息,顺序错误会导致无法识别会话,自然不会触发deserializeUser。
2. 跨域Cookie问题
前后端分属不同端口,属于跨域场景,浏览器默认不会在跨域请求中携带Cookie,导致后端无法识别登录状态。需要做两处调整:
后端配置
修改session的Cookie设置,同时配置CORS允许携带凭证:
// 安装cors依赖后配置 const cors = require('cors'); app.use(cors({ origin: "http://localhost:3000", // 允许的前端域名 credentials: true // 允许跨域携带Cookie })); // 调整session配置 app.use( session({ secret: "testasdasdcode", saveUninitialized: false, resave: false, cookie: { maxAge: 1000 * 60, sameSite: "none", // 跨域场景必须设置 secure: process.env.NODE_ENV === "production", // 生产环境强制HTTPS,开发环境可设为false httpOnly: true // 建议开启,防范XSS攻击 }, }) );
前端配置
发送请求时明确携带凭证:
- 用fetch:
fetch('http://localhost:3001/api/your-endpoint', { credentials: 'include' });
- 用axios:
axios.get('http://localhost:3001/api/your-endpoint', { withCredentials: true });
3. Serialize/Deserialize逻辑异常
检查serializeUser存入的user.id是否有效,以及deserializeUser中的getUser(id)是否能正确查询到用户:
- 在
serializeUser中添加日志,确认存入的ID合法:
passport.serializeUser((user: any, done) => { console.log("序列化用户ID:", user.id); done(null, user.id); });
- 给
deserializeUser添加异常捕获,避免异步错误导致done不执行:
passport.deserializeUser(async (id: string, done) => { try { const user = await getUser(id); console.log("反序列化找到用户:", user); if (!user) return done(new Error("用户不存在")); done(null, user); } catch (err) { done(err); } });
4. 会话存储问题(次要)
默认express-session用内存存储,开发环境暂时可用,但如果服务器重启或会话量大会丢失数据。如果前面的问题都排查完仍有异常,可以换成持久化存储如connect-mongo,但当前问题大概率和这个无关。
内容的提问来源于stack exchange,提问作者Laxus
相关产品推荐
相关产品推荐

