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

NestJS:Passport策略中访问Execution Context报错‘context.getHandler is not a function’求解

问题:NestJS自定义RolesStrategy报错"context.getHandler is not a function"

在NestJS应用中使用Passport实现自定义RolesStrategy时,遇到错误:context.getHandler is not a function。原本预期Execution Context可以正常使用,需要排查问题原因及解决方法。

相关代码如下:

策略代码

import { Strategy } from 'passport-custom';
import { PassportStrategy } from '@nestjs/passport';
import { ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import { ModuleRef, Reflector } from '@nestjs/core';

@Injectable()
export class RolesStrategy extends PassportStrategy(Strategy, 'roles-strategy') {
  constructor(private readonly moduleRef: ModuleRef, private reflector: Reflector) {
    super();
  }

  async validate(req: Request, context: ExecutionContext): Promise<Object> {
    if(req.headers['authorization']) {
        const token = req.headers['authorization'];
        const roles = this.reflector.get<string[]>('roles', context.getHandler());
    } else {
       throw new UnauthorizedException();
    }
  }
}

API代码

@ApiSecurity('key')
@UseGuards(AuthGuard(['admin-strategy', 'roles-strategy']))
@Roles('admin')
@Get('/greet')
async sayHello(@Headers() headers): Promise<any> {
  try {
    return { message: 'Hi' } ;
  } catch (error) {
    throw new InternalServerErrorException(error.message, error.status);
  }
}

原因分析

passport-custom的validate方法默认仅接收**请求对象(req)**作为参数,你定义的第二个参数context并非NestJS的ExecutionContext实例,实际是undefined或无效值,因此调用context.getHandler()会抛出“not a function”错误。

Passport策略本身不支持直接接收NestJS的ExecutionContext,NestJS也不会自动将上下文对象传递给策略的validate方法。


解决方法

方法1:将角色校验逻辑移至自定义Guard(推荐)

将身份验证与角色校验分离:Passport策略负责验证token有效性并返回用户信息,自定义Guard负责通过Reflector获取路由元数据并校验角色。

  1. 自定义RolesGuard:
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    // 获取路由上的@Roles元数据
    const requiredRoles = this.reflector.get<string[]>('roles', context.getHandler());
    if (!requiredRoles) {
      return true; // 无角色要求时直接通过
    }
    // 从请求中获取已验证的用户信息(由前面的AuthStrategy挂载)
    const request = context.switchToHttp().getRequest();
    const user = request.user;
    // 校验用户角色是否包含所需角色
    return requiredRoles.some(role => user.roles?.includes(role));
  }
}
  1. 调整API路由,同时使用AuthGuard和RolesGuard:
@ApiSecurity('key')
@UseGuards(AuthGuard('admin-strategy'), RolesGuard) // 先验证身份,再校验角色
@Roles('admin')
@Get('/greet')
async sayHello(@Headers() headers): Promise<any> {
  try {
    return { message: 'Hi' };
  } catch (error) {
    throw new InternalServerErrorException(error.message, error.status);
  }
}
  1. 简化RolesStrategy,仅负责身份验证:
import { Strategy } from 'passport-custom';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';

@Injectable()
export class RolesStrategy extends PassportStrategy(Strategy, 'roles-strategy') {
  constructor() {
    super();
  }

  async validate(req: Request): Promise<Object> {
    if (!req.headers['authorization']) {
      throw new UnauthorizedException();
    }
    const token = req.headers['authorization'];
    // 这里添加token验证逻辑,返回包含角色信息的用户对象
    const user = { id: 1, roles: ['admin'] };
    return user;
  }
}

方法2:在自定义AuthGuard中传递上下文到策略

如果需要在Strategy中使用ExecutionContext,可以自定义AuthGuard,将上下文挂载到请求对象上,再在Strategy中获取:

  1. 自定义AuthGuard:
import { AuthGuard } from '@nestjs/passport';
import { ExecutionContext, Injectable } from '@nestjs/common';

@Injectable()
export class CustomAuthGuard extends AuthGuard(['admin-strategy', 'roles-strategy']) {
  getRequest(context: ExecutionContext) {
    const req = context.switchToHttp().getRequest();
    // 将上下文挂载到request对象
    req.context = context;
    return req;
  }
}
  1. 修改Strategy的validate方法:
async validate(req: Request & { context: ExecutionContext }): Promise<Object> {
  if(req.headers['authorization']) {
    const token = req.headers['authorization'];
    const roles = this.reflector.get<string[]>('roles', req.context.getHandler());
    // 这里添加角色校验逻辑
    return {};
  } else {
    throw new UnauthorizedException();
  }
}
  1. API路由使用自定义Guard:
@ApiSecurity('key')
@UseGuards(CustomAuthGuard)
@Roles('admin')
@Get('/greet')
async sayHello(@Headers() headers): Promise<any> {
  try {
    return { message: 'Hi' };
  } catch (error) {
    throw new InternalServerErrorException(error.message, error.status);
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 10:25:14