Passport.js+Express+Apollo-Server中req.user始终为undefined求助
解决req.user在Apollo Server中始终为undefined的问题
你已经完成了认证流程(数据库里能看到会话记录),但会话没有正确附加到GraphQL请求的req.user上,这大概率是中间件顺序、会话配置或者Passport序列化/反序列化逻辑的问题。下面是针对性的修复方案:
1. 完善express-session配置,适配无服务器架构
你的express-session只配置了secret,默认用内存存储会话(无服务器架构下内存存储不可靠,重启就丢),而且缺少跨域场景下的cookie配置。需要改用Sequelize存储会话,并调整cookie参数:
import SessionStore from 'connect-session-sequelize'; // 初始化Sequelize会话存储 const SequelizeStore = SessionStore(session.Store); const sessionStore = new SequelizeStore({ db: orm.sequelize, // 传入你的Sequelize实例 }); // 替换原有的session配置 app.use(session({ secret: 'test', resave: false, // 避免重复存储未修改的会话 saveUninitialized: false, // 不存储未初始化的会话 store: sessionStore, // 用Sequelize存储会话 cookie: { secure: process.env.NODE_ENV === 'production', // 生产环境强制HTTPS sameSite: 'none', // 跨域场景下必须设置为none,配合credentials: true httpOnly: true, // 防止前端JS访问cookie,提升安全性 maxAge: 24 * 60 * 60 * 1000 // 会话有效期1天 } })); // 自动创建会话表(第一次运行时执行) sessionStore.sync();
2. 调整中间件顺序,让Apollo Server正确处理会话
你手动给/graphql路由加了passport.authenticate('session'),但Apollo Server的applyMiddleware会接管路由处理,导致这个中间件可能没生效。建议移除手动添加的认证中间件,同时给Apollo Server配置跨域凭证:
// 移除这一行:app.use('/graphql', passport.authenticate('session', { session: true })); // 修改Apollo Server的applyMiddleware配置 server.applyMiddleware({ app, path: '/graphql', cors: { origin: "*", // 生产环境建议指定具体域名,不要用* credentials: true, methods: "GET,POST", preflightContinue: false, optionsSuccessStatus: 204, } });
这样Passport的passport.session()中间件会自动处理所有请求的会话恢复,Apollo Server也能正确接收跨域请求的凭证。
3. 修复Passport的反序列化逻辑
你的deserializeUser用raw: true查询关联用户,这会让Sequelize返回扁平化的对象,可能破坏关联查询的结果。另外,建议通过UserSession反向关联查询User,逻辑更清晰:
passport.deserializeUser(async (hash, done) => { console.log({ hash }); try { // 通过会话hash找到对应的UserSession,关联查询用户 const userSession = await orm.UserSession.findOne({ where: { hash }, include: [{ model: orm.User }] }); if (!userSession) { return done(null, null); // 会话不存在,返回null } done(null, userSession.User); // 返回关联的用户对象 } catch (err) { done(err); // 捕获错误,传递给Passport } });
同时要确保你的User和UserSession模型已经正确定义关联:
// User模型里添加关联 User.hasMany(UserSession, { foreignKey: 'internalId', sourceKey: 'id' }); // UserSession模型里添加关联 UserSession.belongsTo(User, { foreignKey: 'internalId', targetKey: 'id' });
4. 确保客户端请求携带凭证
如果你的前端是跨域请求GraphQL,必须在请求时携带凭证:
- 用Apollo Client的话,设置
credentials: 'include' - 用原生fetch的话,设置
credentials: 'include'
示例Apollo Client配置:
const client = new ApolloClient({ uri: 'http://localhost:3000/graphql', credentials: 'include', // 其他配置... });
测试验证步骤
- 重启服务器,确认
sessions表(由connect-session-sequelize自动创建)和UserSession表都有记录 - 重新登录GitHub,检查会话记录是否正确生成
- 发起GraphQL请求,查看
context里的user是否被正确填充
内容的提问来源于stack exchange,提问作者Eugene
相关产品推荐
相关产品推荐

