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');
疑问点
- 如何在构造函数中获取请求对象?
- 因
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访问次数。
疑问解答
- 构造函数中无法获取请求对象:构造函数在应用初始化时运行,此时没有请求上下文,请求对象只有在处理具体请求时才存在,必须在
verify或validate这类请求阶段的方法中获取。 - super()必须先调用的问题:不需要在构造函数中加载动态配置,先初始化基础的策略配置,后续在验证回调中动态加载应用配置并完成校验,完美避开构造函数的限制。
内容的提问来源于stack exchange,提问作者Abhilash Shajan
相关产品推荐
相关产品推荐

