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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 16:13:44