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

nest-keycloak-connect中@Resource()装饰器如何动态传入资源名称

nest-keycloak-connect @Resource()装饰器动态传参实现方案

原生能力边界

  • 官方提供的@Resource()装饰器本质是TS标准装饰器,执行时机为类加载/编译阶段,仅支持传入编译期就能确定值的静态参数,直接传入运行时才会赋值的变量、依赖请求上下文的函数返回值是无效的——装饰器不会在每次请求进来时重新执行计算参数。
  • 硬编码传值、模板字符串拼接编译期常量的写法是原生支持的,示例如下:
// 编译期常量、服务启动时就能读取到的环境变量可以直接传入
const BAG_RESOURCE = 'my bag';
@Controller('bag')
@Resource(`${BAG_RESOURCE}`) // 该写法原生完全兼容
export class BagController {}

注意:如果你的资源名需要根据请求参数、登录用户身份、数据库查询结果动态生成,上面的静态传参方案无法满足,需要做轻量自定义扩展。

运行时动态传参实现方案

如果需要请求触发时才确定资源名,不需要修改库源码,通过自定义装饰器+扩展原生守卫即可实现,步骤如下:

  • 第一步:定义自定义动态资源装饰器,用来挂载资源解析逻辑
import { SetMetadata } from '@nestjs/common';
export const DYNAMIC_RESOURCE_META = 'dynamic_resource_resolver';
// 装饰器接收一个解析函数,入参是请求对象,返回最终的资源名称,支持异步返回
export const DynamicResource = (resolver: (req: any) => string | Promise<string>) => 
  SetMetadata(DYNAMIC_RESOURCE_META, resolver);
  • 第二步:继承官方的ResourceGuard,在权限校验前先执行动态资源解析,把解析结果传给后续校验逻辑
import { Injectable, ExecutionContext } from '@nestjs/common';
import { ResourceGuard } from 'nest-keycloak-connect';
import { Reflector } from '@nestjs/core';
import { DYNAMIC_RESOURCE_META } from './dynamic-resource.decorator';

@Injectable()
export class CustomKeycloakResourceGuard extends ResourceGuard {
  constructor(protected reflector: Reflector) {
    super(reflector);
  }

  async canActivate(context: ExecutionContext): Promise<boolean> {
    // 优先读取方法上的解析器,不存在则读取类上的解析器
    const resolver = this.reflector.get<(req: any) => string | Promise<string>>(
      DYNAMIC_RESOURCE_META,
      context.getHandler()
    ) || this.reflector.get(DYNAMIC_RESOURCE_META, context.getClass());

    if (resolver) {
      const req = context.switchToHttp().getRequest();
      // 执行解析逻辑拿到真实资源名,支持异步查询/计算
      const resourceName = await resolver(req);
      // 将解析结果挂载到请求对象,供原生权限逻辑读取
      req.resource = resourceName;
    }

    return super.canActivate(context);
  }
}
  • 第三步:在模块中替换默认的资源守卫,即可在业务代码中使用动态资源配置
// 模块中注册自定义守卫
@Module({
  providers: [
    {
      provide: APP_GUARD,
      useClass: CustomKeycloakResourceGuard
    }
  ]
})
export class AuthModule {}

// 业务使用示例
@Controller('user/resource')
export class UserResourceController {
  // 示例:根据请求携带的用户角色、路径参数动态返回资源名
  @DynamicResource(async (req) => {
    const { resourceType } = req.params;
    return req.user.roles.includes('admin') ? `admin_${resourceType}` : `user_${resourceType}`;
  })
  @Get(':resourceType')
  getResource() {
    return [];
  }
}

注意事项

  • 不要直接在原生@Resource()中传入运行时才会赋值的变量,这类变量在类加载阶段值通常为undefined,会导致权限校验异常
  • 如果资源解析逻辑需要查库、调用第三方接口,直接在resolver函数里写异步逻辑即可,守卫已经做了异步兼容
  • 动态解析的资源名优先级高于原生@Resource()配置的静态值,两种装饰器不要在同一个路由上重复使用

内容的提问来源于stack exchange,提问作者Hamza Ali

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 04:06:51