如何在Apollo Server中配置GraphQL默认拒绝访问并对接外部授权服务
GraphQL 默认锁定所有操作并通过指令开放权限的实现方案
核心思路
默认拒绝所有查询(queries)和变更(mutations),仅对标记了自定义授权指令的字段,触发外部授权服务校验,校验通过后才允许访问。
步骤1:定义自定义授权指令
在GraphQL Schema中声明@authorize指令,用于标记需要开放权限的字段:
# 定义授权指令,作用于字段定义 directive @authorize on FIELD_DEFINITION # 示例Schema:只有标记@authorize的字段可被访问 type Query { # 该查询默认被锁定 restrictedQuery: String # 该查询需通过授权校验才能访问 allowedQuery: String @authorize } type Mutation { restrictedMutation: String allowedMutation: String @authorize }
步骤2:实现指令校验与全局默认拦截
以Apollo Server(Node.js)为例,通过Schema指令和全局插件实现逻辑:
1. 实现@authorize指令的授权逻辑
const { SchemaDirectiveVisitor } = require('apollo-server'); const { defaultFieldResolver } = require('graphql'); class AuthorizeDirective extends SchemaDirectiveVisitor { visitFieldDefinition(field) { const originalResolve = field.resolve || defaultFieldResolver; // 重写字段解析器,先调用外部授权服务 field.resolve = async (...args) => { // 从上下文或参数中获取用户身份等信息,传入外部授权服务 const { context } = args[2]; const isAuthorized = await callExternalAuthorizeService(context.user.id, field.name); if (!isAuthorized) { throw new Error('权限校验失败,无法访问该操作'); } // 授权通过,执行原解析逻辑 return originalResolve.apply(this, args); }; } } // 模拟外部授权服务调用 async function callExternalAuthorizeService(userId, operationName) { // 替换为实际的外部服务调用逻辑 return await fetch('https://your-authorize-service.com/check', { method: 'POST', body: JSON.stringify({ userId, operationName }) }).then(res => res.json()); }
2. 全局拦截无授权指令的字段
通过Apollo插件实现全局校验,所有未标记@authorize的字段直接拒绝访问:
const { ApolloServer } = require('apollo-server'); const typeDefs = require('./your-schema'); const resolvers = require('./your-resolvers'); const server = new ApolloServer({ typeDefs, resolvers, // 注册自定义指令 schemaDirectives: { authorize: AuthorizeDirective, }, plugins: [ { async requestDidStart() { return { async executionDidStart() { return { async willResolveField({ field }) { // 检查当前字段是否带有@authorize指令 const hasAuthorizeDirective = field.astNode.directives?.some( dir => dir.name.value === 'authorize' ); // 无指令则直接抛出权限错误 if (!hasAuthorizeDirective) { throw new Error('该操作未开放权限,请联系管理员'); } }, }; }, }; }, }, ], }); server.listen().then(({ url }) => { console.log(`🚀 Server ready at ${url}`); });
关键注意事项
- 性能优化:对外部授权服务的结果做缓存(如Redis),避免重复调用影响接口响应速度
- 灵活授权:可扩展指令参数,比如
@authorize(roles: ["admin", "editor"]),让授权逻辑更精细化 - 嵌套字段处理:根据业务需求,可调整全局拦截逻辑,比如允许已授权父字段的子字段自动继承权限
内容的提问来源于stack exchange,提问作者Laura
相关产品推荐
相关产品推荐

