Express项目中如何用中间件保护GraphQL查询与变更?
为Express中的GraphQL查询与变更配置权限保护
1. 全局Token验证复用原有中间件
你可以直接把auth.verifyToken中间件加到GraphQL的路由上,让所有GraphQL请求先经过Token校验,和原来REST接口的用法逻辑一致:
const { ApolloServer } = require('@apollo/server'); const { expressMiddleware } = require('@apollo/server/express4'); const express = require('express'); const app = express(); // 将Token验证中间件挂载到GraphQL路由 app.use( '/graphql', auth.verifyToken, express.json(), expressMiddleware(server, { context: async ({ req }) => { // 把验证后的用户信息存入context,供后续解析器使用 return { user: req.user }; }, }) );
这样所有访问/graphql的请求都会先完成Token验证,不通过的请求会直接被拦截。
2. 解析器层级的角色权限校验
针对不同的查询(Query)和变更(Mutation),可以复用你的auth.checkRole逻辑,有两种常用方式:
方式一:直接在解析器内调用校验
如果你的auth.checkRole是Express风格的中间件(接收req, res, next参数),可以直接在解析器中调用;如果是同步校验函数,直接判断即可:
const resolvers = { Query: { getAllThings: async (parent, args, context) => { // 调用原有角色校验中间件,不通过则抛出错误 auth.checkRole(["role1", "role2"])(context.req, context.res, () => {}); // 或者改造checkRole为接收user参数的同步函数: // if (!auth.checkRole(["role1", "role2"], context.user)) { // throw new Error('无权限访问该资源'); // } return someController.getAllThings(); }, }, Mutation: { createThing: async (parent, args, context) => { auth.checkRole(["role1"])(context.req, context.res, () => {}); return someController.createThing(args); }, }, };
方式二:封装成可复用的解析器包装函数
把角色校验逻辑封装成高阶函数,避免重复代码:
// 封装角色校验包装器 const requireRoles = (roles) => (resolver) => { return async (parent, args, context) => { // 从context中获取全局验证后的用户信息 if (!auth.checkRole(roles, context.user)) { throw new Error('权限不足,无法访问该资源'); } // 执行原解析器逻辑 return resolver(parent, args, context); }; }; // 使用包装器简化解析器 const resolvers = { Query: { getAllThings: requireRoles(["role1", "role2"])(async () => { return someController.getAllThings(); }), }, Mutation: { createThing: requireRoles(["role1"])(async (parent, args) => { return someController.createThing(args); }), }, };
3. 声明式权限配置(Apollo Server专属)
如果使用Apollo Server,可以通过Schema Directives实现更优雅的声明式权限控制:
第一步:定义权限指令
const { GraphQLDirective, defaultFieldResolver } = require('graphql'); const { SchemaDirectiveVisitor } = require('@apollo/server'); class HasRoleDirective extends SchemaDirectiveVisitor { visitFieldDefinition(field) { const { resolve = defaultFieldResolver } = field; const { roles } = this.args; field.resolve = async function (...args) { const context = args[2]; if (!auth.checkRole(roles, context.user)) { throw new Error('无权限访问该字段'); } return resolve.apply(this, args); }; } }
第二步:在GraphQL Schema中使用指令
directive @hasRole(roles: [String!]!) on FIELD_DEFINITION type Query { getAllThings: [Thing!]! @hasRole(roles: ["role1", "role2"]) } type Mutation { createThing(input: ThingInput!): Thing! @hasRole(roles: ["role1"]) }
第三步:在Apollo Server中注册指令
const server = new ApolloServer({ typeDefs, resolvers, schemaDirectives: { hasRole: HasRoleDirective, }, });
内容的提问来源于stack exchange,提问作者coderNoob
相关产品推荐
相关产品推荐

