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

微服务架构中的授权:网关向服务传递Token的最佳实践

微服务中Gateway向服务传递授权Token的最佳实践

针对你用NestJS+RabbitMQ构建微服务时遇到的授权Token传递问题,以下几种方案无需将Token嵌入DTO,符合每个服务自行处理认证的需求:

1. 利用RabbitMQ消息Headers传递原始Token

NestJS微服务的消息传递支持附加元数据(Headers),可以将Token放在Headers中发送,服务端通过RPC上下文获取后自行校验。

网关端修改:发送消息时附带Token Header

// 网关的sendMicroserviceRequest方法实现
async sendMicroserviceRequest(pattern: string, payload: any, req: Request) {
  // 从HTTP请求头中提取Token
  const authHeader = req.headers.authorization;
  if (!authHeader) {
    throw new UnauthorizedException('No token provided');
  }
  
  // 携带Token到微服务消息的headers中
  return this.clientProxy.send(pattern, payload, {
    headers: { authorization: authHeader }
  }).toPromise();
}

// 在AccountController的me方法中传入Request对象
@Get(':id')
async me(@Param('id', ParseIntPipe) id: number, @Req() req: Request): Promise<any> {
  const dto: FindOneDTO = {id};
  return this.sendMicroserviceRequest("user/me", dto, req);
}

服务端修改:AuthGuard从RPC上下文读取Token校验

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private readonly authService: AuthService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    // 获取RPC上下文的元数据
    const rpcContext = context.switchToRpc();
    const msgMetadata = rpcContext.getContext();
    const authHeader = msgMetadata?.headers?.authorization;

    if (!authHeader) {
      throw new RpcException('Unauthorized: No token found');
    }

    // 提取Token并调用Auth Service校验
    const token = authHeader.split(' ')[1];
    const isValid = await this.authService.validateToken(token);
    
    if (!isValid) {
      throw new RpcException('Unauthorized: Invalid token');
    }

    // 将用户信息附加到上下文,供接口使用
    const user = await this.authService.getUserFromToken(token);
    rpcContext.getData().user = user;
    return true;
  }
}

2. 网关预验证Token,传递身份凭证而非原始Token

由网关先完成Token的初步验证,提取用户身份信息(如userId、角色)后传递给服务端,服务端仅需校验身份信息的合法性,无需处理原始Token。

网关端修改:预验证Token并传递身份信息

@Get(':id')
async me(@Param('id', ParseIntPipe) id: number, @Req() req: Request): Promise<any> {
  const dto: FindOneDTO = {id};
  const authHeader = req.headers.authorization;
  if (!authHeader) {
    throw new UnauthorizedException('No token provided');
  }

  // 网关调用Auth Service验证Token并获取用户信息
  const token = authHeader.split(' ')[1];
  const user = await this.authService.validateTokenAndGetUser(token);
  
  // 将用户身份信息放入消息headers
  return this.sendMicroserviceRequest("user/me", dto, {
    headers: { userId: user.id, roles: user.roles.join(',') }
  });
}

服务端修改:AuthGuard校验身份信息

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private readonly authService: AuthService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const rpcContext = context.switchToRpc();
    const msgMetadata = rpcContext.getContext();
    const userId = msgMetadata?.headers?.userId;

    if (!userId) {
      throw new RpcException('Unauthorized: No user identity provided');
    }

    // 调用Auth Service确认用户身份合法性
    const user = await this.authService.getUserById(Number(userId));
    if (!user) {
      throw new RpcException('Unauthorized: Invalid user identity');
    }

    rpcContext.getData().user = user;
    return true;
  }
}

3. JWT无状态验证(服务端自行校验Token签名)

如果使用JWT作为授权Token,每个服务可以通过公钥直接验证Token的签名和有效期,无需调用Auth Service,实现无状态认证。

服务端AuthGuard实现JWT校验

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(
    @Inject(JWT_MODULE_OPTIONS) private readonly jwtOptions: JwtModuleOptions
  ) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const rpcContext = context.switchToRpc();
    const msgMetadata = rpcContext.getContext();
    const authHeader = msgMetadata?.headers?.authorization;

    if (!authHeader) {
      throw new RpcException('Unauthorized: No token provided');
    }

    const token = authHeader.split(' ')[1];
    try {
      // 直接验证JWT签名和过期时间
      const payload = await verify(token, this.jwtOptions.secretOrPublicKey);
      rpcContext.getData().user = payload;
      return true;
    } catch (err) {
      throw new RpcException('Unauthorized: Invalid or expired token');
    }
  }
}

注意事项

  • 确保RabbitMQ启用TLS加密,防止Token或身份信息在传输过程中被窃取。
  • 所有服务的AuthGuard都要适配RPC上下文,不能依赖HTTP Request对象。
  • 避免将敏感信息放入消息Payload,始终通过Headers传递身份相关数据。

内容的提问来源于stack exchange,提问作者Emir Kutlugün

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 03:12:39