NestJS gRPC微服务RPC授权咨询:grpc.Metadata传递位置疑问
NestJS gRPC微服务RPC授权实现方案
我之前在做NestJS gRPC微服务授权的时候刚好踩过这个坑,给你详细讲下具体实现,重点把grpc.Metadata的传递位置和验证逻辑说清楚:
一、gRPC客户端附加Token(Metadata传递位置)
在NestJS里调用gRPC服务时,Metadata是作为调用方法的第二个参数传递的(如果你用的是@ClientGrpc生成的客户端),或者在ClientProxy的send方法的第三个参数里配置。
1. 使用@ClientGrpc生成的客户端
import { Injectable, OnModuleInit } from '@nestjs/common'; import { Client, ClientGrpc, Transport } from '@nestjs/microservices'; import { join } from 'path'; import { Metadata } from '@grpc/grpc-js'; // 定义gRPC服务的类型(根据你的proto文件生成) interface YourServiceClient { yourRpcMethod(data: any, metadata: Metadata): Observable<any>; } @Injectable() export class AppService implements OnModuleInit { @Client({ transport: Transport.GRPC, options: { package: 'user_service', // 对应proto里的package protoPath: join(__dirname, '../proto/user.proto'), // proto文件路径 }, }) private client: ClientGrpc; private yourService: YourServiceClient; onModuleInit() { // 获取gRPC服务实例 this.yourService = this.client.getService<YourServiceClient>('UserService'); } async fetchUser(userId: string) { // 1. 创建Metadata对象,添加Token const metadata = new Metadata(); metadata.add('authorization', `Bearer ${process.env.USER_SERVICE_TOKEN}`); // 2. 调用gRPC方法时,第二个参数就是Metadata return this.yourService.getUser({ id: userId }, metadata).toPromise(); } }
2. 使用ClientProxy客户端代理
如果是用ClientProxy来调用gRPC服务,需要在send方法的第三个参数里传入metadata:
import { Injectable } from '@nestjs/common'; import { ClientProxy, Client, Transport } from '@nestjs/microservices'; import { Metadata } from '@grpc/grpc-js'; import { join } from 'path'; @Injectable() export class AppService { @Client({ transport: Transport.GRPC, options: { package: 'user_service', protoPath: join(__dirname, '../proto/user.proto'), }, }) private clientProxy: ClientProxy; async fetchUser(userId: string) { const metadata = new Metadata(); metadata.add('authorization', `Bearer ${process.env.USER_SERVICE_TOKEN}`); // 第三个参数传入包含metadata的配置对象 return this.clientProxy.send('getUser', { id: userId }, { metadata }).toPromise(); } }
二、gRPC服务端验证Token
服务端我们可以通过**自定义守卫(Guard)**来拦截gRPC请求,从Metadata中取出Token并验证。
1. 实现AuthGuard
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common'; import { Metadata } from '@grpc/grpc-js'; import { verify } from 'jsonwebtoken'; @Injectable() export class GrpcAuthGuard implements CanActivate { canActivate(context: ExecutionContext): boolean { // 获取gRPC上下文 const grpcContext = context.switchToRpc().getContext(); const metadata: Metadata = grpcContext.metadata; // 从Metadata中取出Authorization头 const authHeader = metadata.get('authorization')?.[0]; if (!authHeader) { throw new UnauthorizedException('Authorization token missing'); } // 提取Token(去掉Bearer前缀) const token = authHeader.split(' ')[1]; if (!token) { throw new UnauthorizedException('Invalid authorization format'); } // 验证Token(这里用JWT示例,替换成你的实际验证逻辑) try { verify(token, process.env.JWT_SECRET); return true; } catch (err) { throw new UnauthorizedException('Invalid or expired token'); } } }
2. 在gRPC控制器中使用守卫
把守卫加到对应的gRPC方法或者整个控制器上:
import { Controller } from '@nestjs/common'; import { GrpcMethod, UseGuards } from '@nestjs/microservices'; import { GrpcAuthGuard } from './grpc-auth.guard'; @Controller() export class UserController { // 给单个方法加守卫 @UseGuards(GrpcAuthGuard) @GrpcMethod('UserService', 'getUser') async getUser(data: { id: string }) { // 你的业务逻辑 return { id: data.id, name: 'John Doe' }; } // 也可以给整个控制器加守卫 // @UseGuards(GrpcAuthGuard) // @GrpcMethod('UserService', 'updateUser') // async updateUser(data: any) { // // ... // } }
注意事项
- 确保客户端和服务端使用的Metadata键名一致(这里用的是
authorization,你也可以自定义); - Token验证逻辑可以抽成公共服务,方便多个微服务复用;
- 验证失败时要抛出合适的gRPC错误码(比如
UNAUTHENTICATED),NestJS会自动转换成对应的gRPC状态码。
内容的提问来源于stack exchange,提问作者user2534584
相关产品推荐
相关产品推荐

