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
相关产品推荐
相关产品推荐

