NestJS:使用 JWT 向 AuthGuard 添加验证选项
Posted
技术标签:
【中文标题】NestJS:使用 JWT 向 AuthGuard 添加验证选项【英文标题】:NestJS: Adding verification options to AuthGuard with JWT 【发布时间】:2019-01-26 16:34:34 【问题描述】:我正在尝试使用 AuthGuard
装饰器和护照 JWT 策略,遵循 documentation。
文档中的所有内容都很好用。但我现在想保护 JWT 中包含范围的路由。所以这是我的应用程序生成的基本 jwt 有效负载:
"user":
"id": "20189c4f-1183-4216-8b48-333ddb825de8",
"username": "user.test@gmail.com"
,
"scope": [
"manage_server"
],
"iat": 1534766258,
"exp": 1534771258,
"iss": "15f2463d-8810-44f9-a908-801872ded159",
"sub": "20189c4f-1183-4216-8b48-333ddb825de8",
"jti": "078047bc-fc1f-4c35-8abe-72834f7bcc44"
这是由AuthGuard
装饰器保护的基本保护路由:
@Get('protected')
@UseGuards(AuthGuard('jwt'))
async protected(): Promise<string>
return 'Hello Protected World';
我想添加选项并将该路由的访问权限限制为具有manager_server
范围的人进入他们的 JWT。所以在阅读了一点AuthGuard
的代码之后,我认为我可以写出类似的东西:
@Get('protected')
@UseGuards(AuthGuard('jwt',
scope: 'manage_server'
))
async protected(): Promise<string>
return 'Hello Protected World';
但是,我在文档中看不到可以使用此选项的位置。
我认为向JWTStrategy
的validate
函数添加一个选项参数可能会成功,但事实并非如此。这是我的validate
函数(包含在jwt.strategy.ts
文件中):
async validate(payload: JwtPayload, done: ((err: any, value: any) => void))
const user = await this.authService.validateUser(payload);
if (!user)
return done(new UnauthorizedException(), false);
done(null, user);
非常感谢您的帮助,如果您需要,请随时在 cmets 中向我询问更多信息。
【问题讨论】:
【参考方案1】:当您查看 AuthGuard 的 code 时,似乎 options.callback
函数是唯一可能的自定义。
我认为与其编写自己的支持范围检查的AuthGuard
,不如使用带有自己的装饰器的ScopesGuard
(或RolesGuard
),例如@Scopes('manage_server')
。为此,您可以按照docs 中的RolesGuard
示例进行操作,该示例还只是检查请求中user
属性下的JWT 有效负载的一个属性。
基本步骤
创建一个@Scopes()
装饰器:
export const Scopes = (...scopes: string[]) => SetMetadata('scopes', scopes);
创建ScopesGuard
:
@Injectable()
export class ScopesGuard implements CanActivate
constructor(private readonly reflector: Reflector)
canActivate(context: ExecutionContext): boolean
const scopes = this.reflector.get<string[]>('scopes', context.getHandler());
if (!scopes)
return true;
const request = context.switchToHttp().getRequest();
const user = request.user;
const hasScope = () => user.scopes.some((scope) => scopes.includes(scope));
return user && user.scopes && hasScope();
使用 ScopesGuard 作为所有路由的全局保护(在没有给出范围时返回 true):
@Module(
providers: [
provide: APP_GUARD,
useClass: ScopesGuard,
,
],
)
export class ApplicationModule
然后在端点上使用它:
@Get('protected')
@UseGuards(AuthGuard('jwt'))
@Scopes('manage_server')
async protected(): Promise<string>
return 'Hello Protected World';
【讨论】:
太好了,非常感谢,自从我发布问题以来,我就提供了使用自定义防护的解决方案,但是您使用自定义装饰器的解决方案是我非常喜欢的。感谢您的宝贵时间! 检查范围的好课 Kim!我确实在该类中发现了几个小错字: 1. 您正在传播范围参数,因此@Scopes(['manage_server'])
将引发编译时警告。我将(...scopes: string[])
更改为(scopes: string[])
2. 确保范围包括变量“范围”,而不是“范围”。 (scope => scopes.includes(scope));
再次感谢!
@leokwanbt14 谢谢! :-) 我已经用你的输入更新了答案。我已经离开了,因为这样你就可以将装饰器用作@Scopes('manage_server')
而不是@Scopes(['manage_server'])
,这对我来说感觉更自然。
我做的完全一样,但是 context.switchToHttp().getRequest();对我来说总是未定义@KimKern【参考方案2】:
我尝试了一种稍微不同的方法,即扩展 AuthGuard 保护。我想保持使用不同 Passport Strategies 的能力,所以我包含了一个 mixin。非常感谢您的反馈。
在您的 Jwt 策略中,您可以简单地返回 JwtPaylozd,以便用户具有范围属性。那么自定义的 AuthGuard 如下所示:
import UnauthorizedException, mixin from "@nestjs/common";
import AuthGuard from "@nestjs/passport";
export function AuthScopes(scopes: string[], type?: string | string[])
return mixin(class ScopesAuth extends AuthGuard(type)
protected readonly scopes = scopes;
handleRequest(err, user, info, context)
if (err || !user)
throw err || new UnauthorizedException();
if(!this.scopes.some(s => user.scopes.split(' ').includes(s)))
throw new UnauthorizedException(`JWT does not possess one of the required scopes ($this.scopes.join(','))`);
return user;
);
然后你可以像这样使用这个守卫:
@Get('protected')
@UseGuards(AuthScopes(['secret:read'], 'jwt'))
async protected(): Promise<string>
return 'Hello Protected World';
'jwt' 代表策略。
【讨论】:
它给出了以下错误TypeError: Cannot read property 'split' of undefined
@MohamedSaleh 我猜你的 JWT 令牌中没有包含任何范围?
是的,我认为是问题所在:)以上是关于NestJS:使用 JWT 向 AuthGuard 添加验证选项的主要内容,如果未能解决你的问题,请参考以下文章
NestJS:如何在从 JWT AuthGuard 扩展的 GraphQL 自定义 Guard 中从请求中获取用户
我在我的 nestJS 应用程序(使用 authGuard)中使用了 passport-jwt 身份验证策略,如何访问我的控制器中的令牌有效负载?
在没有 AuthGuard 的路由上获取 nestjs 中的当前用户