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

Apollo Express Server请求未通过认证中间件的问题及解决

Apollo Server + Express 授权中间件失效问题解决

问题描述

使用Apollo Express Server和GraphQL开发应用时,将Apollo Server的context配置为校验用户登录状态的authMiddleware函数,但请求并未经过该函数。在Apollo Studio发送请求时,出现错误:Context creation failed: Cannot read properties of undefined (reading 'token')。另一应用配置几乎相同,仅authMiddleware响应内容不同,且已在Studio中正确设置请求头,却无法定位问题。

问题原因与解决方法

最终排查出两个核心问题:

  • 参数解构错误:authMiddleware的参数需解构为({ req })而非(req, res, next)。Apollo Server传给context函数的是一个包含req、res等属性的对象,而非直接传递三个独立参数。
  • 多余的next()调用:不存在后续中间件,调用next()会导致服务器崩溃。context函数无需调用next(),而是需要返回处理后的上下文对象(如包含用户信息的对象)供Resolvers使用。

修正后的代码

utils/auth.js

const jwt = require('jsonwebtoken');
require('dotenv').config();

const secret = process.env.JWT_SECRET;
const expiration = '2h';

module.exports = {
  // 修正参数解构逻辑,移除next()调用
  authMiddleware: function ({ req }) {
    // 支持从请求体、查询参数、请求头获取token
    let token = req.body.token || req.query.token || req.headers.authorization;

    // 处理Bearer格式的token
    if (req.headers.authorization) {
      token = token.split(' ').pop().trim();
    }

    if (!token) {
      // 返回空用户对象,也可根据需求抛出错误
      return { user: null };
    }

    // 验证token并解析用户数据
    try {
      const { data } = jwt.verify(token, secret, { maxAge: expiration });
      return { user: data };
    } catch {
      console.log('无效的token');
      return { user: null };
    }
  },
  signToken: function ({ username, email, _id }) {
    const payload = { username, email, _id };
    return jwt.sign({ data: payload }, secret, { expiresIn: expiration });
  },
};

server.js(原配置无需修改)

const express = require('express');
const {ApolloServer} = require('apollo-server-express');
const path = require('path');

const {typeDefs, resolvers} = require('./schemas');
const {authMiddleware} = require('./utils/auth');
const db = require('./config/connection');

const PORT = process.env.PORT || 3001;

const server = new ApolloServer({
    typeDefs,
    resolvers,
    context: authMiddleware
});
const app = express();

app.use(express.urlencoded({ extended: true }));
app.use(express.json());

const startApolloServer = async (typeDefs, resolvers) => {
    await server.start();
    server.applyMiddleware({app});
}

if (process.env.NODE_ENV === 'production') {
  app.use(express.static(path.join(__dirname, '../client/build')));
};

// frontend isn't set up yet
// app.get('*', (req, res) => {
//      res.sendFile(path.join(__dirname, '../client/build// /index.html'));
// });

db.once('open', () => {
    app.listen(PORT, () => {
        console.log(`API server running on port ${PORT}!`);
        console.log(
            `Use GraphQL at http://localhost:${PORT}${server.graphqlPath}`
        );
    });
});

startApolloServer(typeDefs, resolvers);

补充说明

原authMiddleware采用了Express中间件的写法(接收req、res、next),但Apollo Server的context函数逻辑不同:它接收上下文对象,需解构获取req;同时不需要通过next()传递请求,而是返回包含用户信息的对象,让Resolvers能通过context.user获取当前用户数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 08:50:28