简体   繁体   English

如何在 NestJs 中仅在特定情况下使 guard 可选?

[英]How to make guard optional only in particular case in NestJs?

I have the below two guards in NestJS(one for api key based authentication and another for token based authentication).The ApiKeyGuard is the top priority.I want to implement a system where if anyone has a key it will not check the other guard.Is there any way I can make the AuthGuard optional based on whether the first Guard passed in cases where there is a ApiKeyGuard?我在 NestJS 中有以下两个守卫(一个用于基于 api 密钥的身份验证,另一个用于基于令牌的身份验证)。ApiKeyGuard 是重中之重。我想实现一个系统,如果有人有钥匙,它不会检查另一个守卫。在有 ApiKeyGuard 的情况下,有什么方法可以根据第一个 Guard 是否通过来使 AuthGuard 可选?

// Can be accessed with token within app as well as third party users
    @UseGuards(ApiKeyGuard, AuthGuard)
    @Get('/get-products')
      async getProducts(): Promise<any> {
        try {
          return this.moduleRef
            .get(`appService`, { strict: false })
            .getProducts();
        } catch (error) {
          throw new InternalServerErrorException(error.message, error.status);
        }
    
      }

// Only to be accessed with token within app
    @UseGuards(AuthGuard)
    @Get('/get-users')
      async getUsers(): Promise<any> {
        try {
          return this.moduleRef
            .get(`appService`, { strict: false })
            .getUsers();
        } catch (error) {
          throw new InternalServerErrorException(error.message, error.status);
        }
    
      }

The below guard is used to check for api key based authentication下面的守卫用于检查基于 api 密钥的身份验证

api-key.guard.ts

@Injectable()
export class ApiKeyGuard implements CanActivate {
  constructor(private readonly apiKeyService: ApiKeyService) {} 

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const req = context.switchToHttp().getRequest();
    const key = req.headers['X-API-KEY'] ?? req.query.api_key;
    return this.apiKeyService.isKeyValid(key);
  }

The below guard is used to check for token based authentication下面的守卫用于检查基于令牌的身份验证

        authentication.guard.ts
        
        @Injectable()
        
        export class AuthGuard implements CanActivate, OnModuleInit {
          constructor(private readonly moduleRef: ModuleRef) {}
          onModuleInit() {}
          async canActivate(context: ExecutionContext): Promise<boolean> {
        
            try {
        
              // Get request data and validate token
        
              const request = context.switchToHttp().getRequest();
        
              if (request.headers.authorization) {
                const token = request.headers.authorization.split(' ')[1];
                const response = await this.checkToken(token);
           
                if (response) {
                  return response;
                } else {
                  throw new UnauthorizedException();
                }
              } else {
                throw new UnauthorizedException();
              }
        
            } catch (error) {
              throw new UnauthorizedException();
            }
        
          }
        
        }

What I did was using @nestjs/passport and using the AuthGuard and making custom PassportJS strategies.我所做的是使用@nestjs/passport并使用AuthGuard并制定自定义 PassportJS 策略。 I had a similar issue, and looked for a way to accomplish this without using some "magic".我有一个类似的问题,并寻找一种方法来完成这个而不使用一些“魔法”。 The documentation can be found here .可以在此处找到文档

In the AuthGuard , you can add multiple guards.AuthGuard中,您可以添加多个守卫。 It's a bit hidden away in the documentation, although it is very powerful.它有点隐藏在文档中,尽管它非常强大。 Take a look here , especially the last line of the section, it states: 看看这里,尤其是该部分的最后一行,它指出:

In addition to extending the default error handling and authentication logic, we can allow authentication to go through a chain of strategies.除了扩展默认的错误处理和认证逻辑外,我们还可以通过一系列策略允许对 go 进行认证。 The first strategy to succeed, redirect, or error will halt the chain.第一个成功、重定向或错误的策略将停止链。 Authentication failures will proceed through each strategy in series, ultimately failing if all strategies fail.身份验证失败将依次通过每个策略,如果所有策略都失败,则最终失败。

Which can be done like so:可以这样做:

export class JwtAuthGuard extends AuthGuard(['strategy_jwt_1', 'strategy_jwt_2', '...']) { ... }

Now, back to your example, you've to create 2 custom strategies, one for the API key and one for the authorization header, and both these guards should be activated.现在,回到您的示例,您必须创建 2 个自定义策略,一个用于 API 密钥,一个用于授权 header,这两个守卫都应该被激活。

So for the API strategy (as example):所以对于 API 策略(例如):

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

@Injectable()
export class ApiStrategy extends PassportStrategy(Strategy, 'api-strategy') {
  constructor(private readonly apiKeyService: ApiKeyService) {} 

  async validate(req: Request): Promise<User> {
    const key = req.headers['X-API-KEY'] ?? req.query.api_key;
    if ((await this.apiKeyService.isKeyValid(key)) === false) {
      throw new UnauthorizedException();
    }

    return this.getUser();
  }
}

Do something similar for your other way of authenticating, and then use the Passport guard as follows:为您的其他身份验证方式做类似的事情,然后按如下方式使用 Passport guard:

@UseGuard(AuthGuard(['api-strategy', 'other-strategy'])

This way, the guard will try all strategies (in order) and when all of them fail, your authentication has failed.这样,守卫将尝试所有策略(按顺序),当所有策略都失败时,您的身份验证失败。 If one of them succeeds, you're authenticated!如果其中之一成功,则您已通过身份验证!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM