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

如何通过PassportJS实现Apollo GraphQL Server的接口认证?

在Apollo Server中结合PassportJS实现/graphql端点前置认证

我之前也踩过这个坑,核心是要理清Express中间件的执行顺序,以及如何把Passport认证后的用户信息传递给Apollo的上下文。下面一步步给你拆解:

1. 中间件的正确顺序

首先必须明确:bodyParser要在Passport认证中间件之前执行。原因有两个:

  • Apollo Server处理GraphQL请求需要解析JSON格式的请求体,bodyParser是基础依赖;
  • 哪怕你用的是Header传递认证凭证(比如JWT的Authorization头),先执行bodyParser也不会有问题,反而能保证请求体被正确解析,不影响后续流程。如果是需要读取请求体里的凭证(比如表单提交的token),Passport更是必须在bodyParser之后才能拿到解析后的数据。

2. 全局路由级别的认证(推荐方案)

如果想让所有访问/graphql的请求都先经过认证,最直接的方式是在Apollo Server挂载前,给/graphql路由添加Passport的认证中间件。给你一个完整的可运行示例:

const express = require('express');
const { ApolloServer, gql } = require('apollo-server-express');
const passport = require('passport');
const bodyParser = require('body-parser');
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;

const app = express();

// 第一步:解析请求体,必须放在最前面
app.use(bodyParser.json());

// 第二步:初始化Passport并配置认证策略(这里以JWT为例)
const jwtOptions = {
  jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
  secretOrKey: 'your-strong-secret-key'
};
passport.use(new JwtStrategy(jwtOptions, (jwtPayload, done) => {
  // 这里写你的用户查询逻辑,比如从数据库根据payload里的id查找用户
  User.findById(jwtPayload.id, (err, user) => {
    if (err) return done(err, false);
    if (user) return done(null, user);
    return done(null, false, { message: '用户不存在' });
  });
}));
app.use(passport.initialize());

// 第三步:给/graphql路由添加Passport认证中间件
// session: false 因为我们用的是无状态认证(比如JWT),不需要维护会话
app.use('/graphql', passport.authenticate('jwt', { session: false }));

// 第四步:配置Apollo Server并挂载到Express
const typeDefs = gql`
  type Query {
    currentUser: User
  }
  type User {
    id: ID!
    email: String!
    username: String!
  }
`;

const resolvers = {
  Query: {
    currentUser: (_, __, context) => {
      // 这里的context.user就是Passport认证后挂载到req上的用户对象
      return context.user;
    }
  }
};

const apolloServer = new ApolloServer({
  typeDefs,
  resolvers,
  // 把Express的req对象传递到上下文,从而拿到认证后的user
  context: ({ req }) => ({ user: req.user })
});

apolloServer.applyMiddleware({ app });

app.listen(4000, () => {
  console.log(`🚀 Server running at http://localhost:4000${apolloServer.graphqlPath}`);
});

3. 关键细节说明

  • Passport的认证结果传递:当passport.authenticate执行成功后,会自动把认证后的用户对象挂载到req.user上,我们只需要在Apollo的context配置里把这个req.user传递给解析器即可。
  • 自定义认证失败响应:默认情况下,Passport认证失败会直接返回401状态码。如果你想让失败信息以GraphQL错误的形式返回,可以自定义认证回调:
    app.use('/graphql', (req, res, next) => {
      passport.authenticate('jwt', { session: false }, (err, user, info) => {
        if (err) return next(err);
        if (!user) {
          // 把错误信息挂载到req上,后续在上下文里处理
          req.authError = info?.message || '认证失败,请检查凭证';
        } else {
          req.user = user;
        }
        // 继续执行后续中间件(Apollo的处理逻辑)
        next();
      })(req, res, next);
    });
    
    然后在Apollo的上下文里抛出错误:
    context: ({ req }) => {
      if (req.authError) {
        throw new Error(req.authError);
      }
      return { user: req.user };
    }
    

4. 局部认证(仅部分查询/突变需要认证)

如果不需要全局认证,只是某些解析器需要用户信息,可以不用在路由级别加passport.authenticate,而是在Apollo的context里手动调用Passport的认证逻辑,或者在解析器里检查req.user(前提是你已经初始化了Passport)。不过这种方式不如全局路由认证高效,因为每个请求都会进入Apollo的处理流程后才做认证检查。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 08:10:44