You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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',
  // 其他配置...
});

测试验证步骤

  1. 重启服务器,确认sessions表(由connect-session-sequelize自动创建)和UserSession表都有记录
  2. 重新登录GitHub,检查会话记录是否正确生成
  3. 发起GraphQL请求,查看context里的user是否被正确填充

内容的提问来源于stack exchange,提问作者Eugene

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:14:28