Apollo Federation网关如何实现子图动态身份认证与凭证传递
Apollo Gateway 子图动态身份认证最佳实践
完全不需要访问私有serviceMap属性,基于Apollo官方公开的扩展点即可实现,同时兼容手动配置子图和Apollo托管联邦两种部署模式:
实现核心逻辑
- 利用
buildService入参自带的子图name属性标识每个子图数据源,避免硬编码URL匹配 - 网关上下文提前拉取当前用户所有子图的访问凭证,通过上下文传递给数据源钩子
- 在
RemoteGraphQLDataSource的willSendRequest钩子中匹配当前子图标识,注入对应凭证
完整代码实现
import { ApolloServer } from '@apollo/server'; import { ApolloGateway, RemoteGraphQLDataSource } from '@apollo/gateway'; import { GraphQLRequest } from 'apollo-server-types'; // 自定义数据源,绑定子图名称 class AuthenticatedDataSource extends RemoteGraphQLDataSource { // 接收子图name作为初始化参数 constructor(config: { url: string, serviceName: string }) { super({ url: config.url }); this.serviceName = config.serviceName; } private serviceName: string; async willSendRequest({ request, context }: { request: GraphQLRequest, context: any }) { // 直接从上下文取当前子图对应的凭证,不需要匹配URL const userServiceToken = context.userServiceTokens?.[this.serviceName]; if (userServiceToken) { request.http?.headers.set('Authorization', `Bearer ${userServiceToken}`); } } } const main = async () => { const gateway = new ApolloGateway({ // 不管是手动配置serviceList还是托管联邦模式,buildService的入参都会携带子图的name和url buildService: ({ name, url }) => { // 给每个子图数据源绑定对应的子图名称 return new AuthenticatedDataSource({ url, serviceName: name }); }, // 也支持托管联邦模式,不需要手动写serviceList // serviceList: [ // { name: 'service1', url: 'http://localhost:3001' }, // { name: 'service2', url: 'http://localhost:3002' }, // ] }); const server = new ApolloServer({ gateway }); await server.listen({ port: 3000 }, async ({ url }) => { console.log(`Apollo Gateway ready at ${url}`); }); }
上下文用户凭证拉取示例
在Apollo Server的上下文构造函数中统一拉取当前用户的所有子图凭证,可以根据需要加缓存降低数据库压力:
const server = new ApolloServer({ gateway, // 上下文构造钩子,每个请求执行一次 context: async ({ req }) => { // 先做网关层的用户身份校验 const userId = verifyUserToken(req.headers.authorization); if (!userId) throw new Error('身份认证失败'); // 批量拉取当前用户所有子图的访问凭证 const userServiceTokens = await getUserAllServiceTokens(userId); return { userServiceTokens }; } });
额外优化建议
- 可以给用户的子图凭证加本地缓存,缓存key绑定用户ID,过期时间和凭证有效期对齐,避免每次请求查库
- 不需要传递给子图的敏感信息不要存入上下文,避免日志泄露
- 如果子图的认证规则不同,可以在
AuthenticatedDataSource中加自定义配置项单独处理
内容的提问来源于stack exchange,提问作者Random
相关产品推荐
相关产品推荐

