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

NestJS中如何向Passport Bearer Strategy父类传入动态配置

多Azure AD应用的动态API请求验证方案

问题背景

我正在用passport-azure-ad编写验证器类,用来验证API的所有请求。已经继承了PassportStrategy类并配置成全局守卫,运行正常。但现在需要验证来自不同域名的多个UI应用的请求,每个应用的clientId和tenantId都不一样,得对应验证token。

每个请求头里都带有appname参数,需要根据这个参数从数据库获取对应应用的Passport配置,想知道有没有更优的实现方式。

现有代码

import { AuthGuard, PassportStrategy } from '@nestjs/passport';
import { BearerStrategy } from 'passport-azure-ad';
import { Injectable } from '@nestjs/common';


@Injectable()
export class AzureADStrategy extends PassportStrategy(BearerStrategy, 'azure-ad-bearer') {
  constructor(dbService: DbService) {
  //const appName = request.headers['appname']
  //const metaData = await dbService.getTokenMetaData(appName)
    super({
      identityMetadata: metaData.identity,
      jwtIssuer: metaData.issuer,
      clientID: metaData.clientId,
      validateIssuer: true,
      passReqToCallback: false,
      loggingLevel: 'info',
      loggingNoPII: false
    });
  }

  async validate(payload: any): Promise<any> {
    return payload;
  }
}

export const AzureADGuard = AuthGuard('azure-ad-bearer');

疑问点

  1. 如何在构造函数中获取请求对象?
  2. 因super()必须是构造函数的首个调用,怎么先调用dbService从数据库获取数据?

解决方案

构造函数是在应用启动阶段执行的,此时还没有具体的请求上下文,所以没法在构造函数里获取请求对象或动态加载应用配置。正确的做法是利用passport-azure-ad的passReqToCallback特性,把请求对象传递到验证流程中,在验证阶段动态获取配置并完成token校验。

具体实现

1. 调整策略基础配置

开启passReqToCallback: true,让请求对象能传递到验证回调中:

constructor(private readonly dbService: DbService) {
  super({
    validateIssuer: true,
    passReqToCallback: true, // 关键:传递请求对象到回调
    loggingLevel: 'info',
    loggingNoPII: false
  } as IBearerStrategyOptionWithRequest);
}

2. 自定义verify回调,动态校验token

重写verify方法,先从请求头拿appname,再拉取对应配置,用临时策略实例验证token:

async verify(req: Request, token: string, done: VerifyCallback): Promise<void> {
  try {
    // 提取appname请求头
    const appName = req.headers['appname'] as string;
    if (!appName) {
      throw new UnauthorizedException('缺少appname请求头');
    }

    // 从数据库获取应用配置
    const metaData = await this.dbService.getTokenMetaData(appName);
    if (!metaData) {
      throw new UnauthorizedException('无效的appname');
    }

    // 用动态配置创建临时验证策略
    const tempStrategy = new BearerStrategy({
      identityMetadata: metaData.identity,
      jwtIssuer: metaData.issuer,
      clientID: metaData.clientId,
      validateIssuer: true
    }, (payload, done) => done(null, payload));

    // 执行token验证
    tempStrategy.verify(req, token, done);
  } catch (err) {
    done(err, false);
  }
}

3. 性能优化:缓存配置

如果数据库查询频繁,可以引入Redis等缓存工具,把应用配置缓存起来,设置合适的过期时间,减少DB访问次数。

疑问解答

  1. 构造函数中无法获取请求对象:构造函数在应用初始化时运行,此时没有请求上下文,请求对象只有在处理具体请求时才存在,必须在verify或validate这类请求阶段的方法中获取。
  2. super()必须先调用的问题:不需要在构造函数中加载动态配置,先初始化基础的策略配置,后续在验证回调中动态加载应用配置并完成校验,完美避开构造函数的限制。

内容的提问来源于stack exchange,提问作者Abhilash Shajan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 07:43:22