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

tvz2xvvm  于 2022-12-12  发布在  Node.js
关注(0)|答案(1)|浏览(184)

我在NestJS中有以下两个保护(一个用于基于API密钥的身份验证,另一个用于基于令牌的身份验证)。ApiKeyGuard是最优先的。我想实现一个系统,如果任何人有一个密钥,它不会检查其他保护。有没有什么方法可以使AuthGuard可选的基础上,第一个保护是否通过的情况下,有一个ApiKeyGuard?

// 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);
        }
    
      }

下面的保护用于检查基于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);
  }

下面的保护用于检查基于令牌的身份验证

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();
            }
        
          }
        
        }
nimxete2

nimxete21#

我所做的是使用@nestjs/passport和使用AuthGuard并定制PassportJS策略。我遇到了类似的问题,并寻找一种方法来完成这一点,而不使用一些“魔术”。documentation can be found here
AuthGuard中,你可以添加多个保护。虽然它非常强大,但在文档中有点隐藏。请看这里,特别是该部分的最后一行,它声明:
除了扩展默认的错误处理和身份验证逻辑之外,我们还可以允许身份验证通过一个策略链。第一个成功的策略、重定向或错误将中止该链。身份验证失败将依次通过每个策略,如果所有策略都失败,则最终失败。
可以这样做:

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

现在,回到您的示例,您必须创建2个自定义策略,一个用于API密钥,一个用于授权头,并且这两个保护都应该被激活。
因此,对于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();
  }
}

对其他身份验证方法执行类似的操作,然后按如下所示使用Passport防护:

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

这样,守卫会尝试所有的策略(按顺序),当所有策略都失败时,你的身份验证失败。如果其中一个成功,你就通过了身份验证!

相关问题