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

如何在@apollo/server中集成graphql-shield实现权限控制

在Apollo Server v4中使用graphql-shield实现权限控制

一、适配Apollo Server v4的实现方式

Apollo Server v4不会直接暴露自动生成的Schema,因此需要手动构建可执行Schema并应用graphql-shield中间件,具体步骤如下:

1. 导入必要工具

首先从@graphql-tools/schema导入makeExecutableSchema,若未安装该依赖,执行npm install @graphql-tools/schema添加。

2. 定义权限规则

先编写符合业务需求的shield规则,比如验证用户登录状态、角色权限等:

import { shield, rule, and } from "graphql-shield";

// 验证用户是否已登录的规则
const isAuthenticated = rule()(async (parent, args, context) => {
  // 替换为你的实际token解析/用户验证逻辑,比如JWT解析
  const user = await validateToken(context.token);
  return !!user;
});

// 验证用户是否为管理员的规则
const isAdmin = rule()(async (parent, args, context) => {
  const user = await validateToken(context.token);
  return user?.role === "ADMIN";
});

// 权限规则映射
const permissions = shield({
  Query: {
    // 仅登录用户可查看个人信息
    me: isAuthenticated,
    // 仅管理员可查看用户列表
    userList: isAdmin,
  },
  Mutation: {
    // 仅登录用户可更新个人资料
    updateProfile: isAuthenticated,
    // 仅管理员可创建用户
    createUser: isAdmin,
  },
});

3. 修改原代码,应用中间件

调整初始化流程,先构建基础Schema,再注入权限中间件,最后传给ApolloServer:

import * as dotenv from "dotenv";
import mongoose from "mongoose";
import typeDefs from "./graphql/typeDefs.js";
import resolvers from "./graphql/resolvers.js";
import { makeExecutableSchema } from "@graphql-tools/schema"; // 新增导入
import { ApolloServer } from "@apollo/server";
import { expressMiddleware } from "@apollo/server/express4";
import { ApolloServerPluginDrainHttpServer } from "@apollo/server/plugin/drainHttpServer";
import express from "express";
import http from "http";
import pkg from "body-parser";
import cors from "cors";
import { applyMiddleware } from "graphql-middleware";
import { shield, rule } from "graphql-shield";

dotenv.config();
const { json } = pkg;
const MONGODB = process.env.MONGODB;

// 1. 构建基础可执行Schema
const baseSchema = makeExecutableSchema({ typeDefs, resolvers });

// 2. 定义权限规则(替换为你的实际规则)
const isAuthenticated = rule()(async (parent, args, context) => {
  return context.token !== ""; // 示例:简单验证token存在,实际需解析验证
});

const permissions = shield({
  Query: {
    me: isAuthenticated,
  },
  Mutation: {
    updateUser: isAuthenticated,
  },
});

// 3. 将权限中间件应用到Schema
const schemaWithPermissions = applyMiddleware(baseSchema, permissions);

const app = express();
const httpServer = http.createServer(app);

// 4. 初始化ApolloServer时传入处理后的Schema
const server = new ApolloServer({
  schema: schemaWithPermissions, // 替换原有的typeDefs和resolvers参数
  plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});

await mongoose.connect(MONGODB, {
  useNewUrlParser: true,
});

await server.start();

app.use(
  "/graphql",
  cors({
    origin: ["https://studio.apollographql.com", process.env.FRONTEND_DEV_URL],
  }),
  json(),
  expressMiddleware(server, {
    context: ({ req }) => ({
      token: req.headers.authorization || "",
    }),
  })
);

await new Promise((resolve) =>
  httpServer.listen({ port: process.env.PORT || 4000 }, resolve)
);
console.log(`🚀 Server ready `);

二、官方文档「真实世界示例」中文翻译

真实世界示例

假设你有一个博客应用,需求如下:

  • 所有用户均可查看帖子和评论
  • 仅帖子作者可编辑/删除自己的帖子
  • 仅评论作者可删除自己的评论
  • 登录用户可创建帖子和评论

首先定义基础规则:

const isAuthenticated = rule()(async (parent, args, ctx) => {
  return ctx.user !== null;
});

const isPostAuthor = rule()(async (parent, { id }, ctx) => {
  const post = await ctx.prisma.post.findUnique({ where: { id } });
  return post.authorId === ctx.user.id;
});

const isCommentAuthor = rule()(async (parent, { id }, ctx) => {
  const comment = await ctx.prisma.comment.findUnique({ where: { id } });
  return comment.authorId === ctx.user.id;
});

然后定义权限映射:

const permissions = shield({
  Query: {
    posts: allow, // 允许所有用户访问
    post: allow,
    comments: allow,
  },
  Mutation: {
    createPost: isAuthenticated,
    createComment: isAuthenticated,
    updatePost: and(isAuthenticated, isPostAuthor),
    deletePost: and(isAuthenticated, isPostAuthor),
    deleteComment: and(isAuthenticated, isCommentAuthor),
  },
});

最后将中间件应用到Schema:

const schema = applyMiddleware(executableSchema, permissions);

三、替代方案推荐

若graphql-shield的适配成本过高,可考虑以下更灵活的权限控制方案:

1. Apollo Server原生权限控制

  • Resolver内验证:在需要权限的resolver开头添加验证逻辑,比如检查context中的用户身份和权限。
  • 自定义Directive:在typeDefs中用@auth等标记需要权限的字段,通过Apollo Server的directives选项实现验证逻辑。

2. Envelop权限插件

Envelop是The Guild推出的模块化GraphQL中间件框架,提供官方的@envelop/authorization权限插件,适配Apollo Server v4,支持更灵活的中间件组合,且维护更活跃。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 15:55:41