GraphQL联邦中集中式授权的实现方案咨询
集中式GraphQL联邦授权实现方案
针对你的场景,要实现supergraph层面的集中授权、让subgraph与权限逻辑完全解耦,可按以下步骤落地:
核心原则
- supergraph作为唯一的权限校验入口,所有请求必须先经过supergraph的权限校验,再转发到对应subgraph
- subgraph仅做IP白名单防护,只允许supergraph的IP访问,完全不处理业务权限逻辑
- 权限规则集中管理,新增subgraph时只需扩展规则,无需修改subgraph代码
具体实现步骤
1. 在supergraph网关层添加全局权限拦截
以Apollo Gateway为例,利用网关的生命周期钩子在请求执行前完成权限校验。核心是解析用户身份(比如从请求头的Token中提取角色、用户ID),然后根据请求的字段/类型匹配权限规则。
代码示例:
const { ApolloGateway } = require('@apollo/gateway'); const { extractRequestedFields } = require('./utils/fieldExtractor'); // 自定义工具函数 const { getPermissionRule, checkUserPermission } = require('./utils/permissionChecker'); // 自定义工具函数 const gateway = new ApolloGateway({ serviceList: [ { name: 'posts', url: 'http://posts-subgraph:4001/graphql' }, { name: 'users', url: 'http://users-subgraph:4002/graphql' }, { name: 'products', url: 'http://products-subgraph:4003/graphql' } ], // 传递用户身份给subgraph(可选,subgraph无需用,但留作扩展) buildService({ url }) { return new RemoteGraphQLDataSource({ url, willSendRequest({ request, context }) { request.http.headers.set('x-user-id', context.userId); request.http.headers.set('x-user-role', context.userRole); } }); }, // 权限校验核心逻辑 async didResolveOperation({ request, context }) { // 1. 提取当前请求的所有字段路径(如Query.users, User.email, Query.products等) const requestedFields = extractRequestedFields(request); // 2. 遍历字段逐一校验权限 for (const fieldPath of requestedFields) { const rule = getPermissionRule(fieldPath); if (!rule) { throw new Error(`未配置${fieldPath}的权限规则`); } switch (rule) { case 'allow-all': // posts公开访问,直接跳过 break; case 'admin-or-authorized': // users数据:管理员或有权限用户可访问 if (!context.userRole.includes('admin') && !await checkUserPermission(context.userId, fieldPath)) { throw new Error('无权访问用户数据'); } break; case 'product-role-based': // products数据:匹配对应角色(如product_viewer、product_editor) if (!context.userRole.some(role => role.startsWith('product_'))) { throw new Error('无权访问产品数据'); } break; case 'admin-or-self': // 用户自身数据:管理员或用户本人可访问 const targetUserId = extractTargetUserId(request.variables); // 从请求参数提取目标用户ID if (!context.userRole.includes('admin') && context.userId !== targetUserId) { throw new Error('无权访问该用户信息'); } break; } } } });
2. 集中管理权限规则
把权限规则做成可配置的结构(可以存在JSON文件、数据库或配置中心),方便新增subgraph时快速扩展:
{ "Query.posts": "allow-all", "Query.users": "admin-or-authorized", "User.*": "admin-or-self", "Query.products": "product-role-based", "Product.*": "product-role-based" }
3. subgraph层面的IP防护
在每个subgraph的服务入口添加IP白名单中间件,只允许supergraph的IP访问,彻底阻断外部直接请求:
const express = require('express'); const app = express(); // IP白名单校验中间件 app.use((req, res, next) => { const allowedIPs = ['192.168.1.100']; // 替换为supergraph的实际IP const clientIP = req.ip || req.connection.remoteAddress.replace(/^.*:/, ''); // 处理IPv6格式 if (!allowedIPs.includes(clientIP)) { return res.status(403).send('禁止直接访问'); } next(); }); // 挂载Apollo Server const { ApolloServer } = require('apollo-server-express'); const server = new ApolloServer({ schema: yourSubgraphSchema }); await server.start(); server.applyMiddleware({ app }); app.listen({ port: 4001 }, () => { console.log('Posts subgraph running'); });
4. 解决原指令失效问题
你之前在subgraph用指令失效,是因为GraphQL联邦合并supergraph schema时,默认不会保留subgraph的自定义指令。改用上述网关钩子的方式,不需要依赖schema指令,直接在请求执行前拦截校验,更适配联邦架构的集中授权需求。
扩展性说明
新增subgraph时,只需两步即可完成权限配置:
- 在supergraph的serviceList中添加新subgraph地址
- 在权限规则配置中新增对应字段的权限规则,无需修改任何subgraph代码
内容的提问来源于stack exchange,提问作者reza erami
相关产品推荐
相关产品推荐

