如何在nestjs中同时使用两个自定义的认证授权守卫

问题描述 投票:0回答:1

auth.guard.ts

import {
  CanActivate,
  ExecutionContext,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import { InjectModel } from '@nestjs/mongoose';
import { Request } from 'express';
import { Model } from 'mongoose';
import { User } from '../user/entities/user.entity';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(
    @InjectModel(User.name)
    private userModel: Model<User>,
    private jwtService: JwtService,
    private readonly configService: ConfigService,
  ) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const token = this.extractTokenFromHeader(request);
    if (!token) {
      throw new UnauthorizedException(
        'You are not authorized to access this resource.',
      );
    }
    try {
      const payload = await this.jwtService.verifyAsync(token, {
        secret: this.configService.get<string>('JWT_SECRET'),
      });
      const user = await this.userModel.findById(payload.id);

      if (!user) {
        throw new UnauthorizedException('User not found.');
      }
      request['user'] = user;
      console.log('request.user -->', request.user);
    } catch {
      throw new UnauthorizedException(
        'You are not authorized to access this resource.',
      );
    }
    return true;
  }

  private extractTokenFromHeader(request: Request): string | undefined {
    const [type, token] = request.headers.authorization?.split(' ') ?? [];
    return type === 'Bearer' ? token : undefined;
  }
}

角色.guard.ts

import {
  Injectable,
  CanActivate,
  ExecutionContext,
  UnauthorizedException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Role } from '../enums/role.enum';
import { ROLES_KEY } from '../decorators/roles.decorator';

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

  canActivate(context: ExecutionContext): boolean {
    const requiredRoles = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    if (!requiredRoles) {
      return true;
    }
    const { user } = context.switchToHttp().getRequest();
    const isAuthorozed = requiredRoles.some(
      (role) => user.role?.includes(role),
    );

    if (!isAuthorozed) {
      throw new UnauthorizedException({
        message: 'You are not authorized person to access this resource.',
        statusCode: 401,
      });
    }
  }
}

角色.decorator.ts

import { SetMetadata } from '@nestjs/common';
import { Role } from '../enums/role.enum';

export const ROLES_KEY = 'roles';
export const Roles = (...roles: Role[]) => SetMetadata(ROLES_KEY, roles);

book.controller.ts

  @UseGuards(AuthGuard)
  @Roles(Role.USER)
  @Get()
  async getAllBooks(
    @Req() req: Request,
    @Res() res: Response,
    @Query() query: any,
  ): Promise<any> {
    const books = await this.bookService.findAll(query);
    return res.status(HttpStatus.OK).json({
      data: books,
      statusCode: 200,
      message: 'Books fetched successfully.',
    });
  }

app.module.ts

@Module({
  imports: [
    MongooseModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => ({
        uri: configService.get<string>('MONGO_URI'),
        dbName: configService.get<string>('DATABASE_NAME'),
      }),
      inject: [ConfigService],
    }),
    ConfigModule.forRoot({
      envFilePath: '.env',
      isGlobal: true,
    }),
    BookModule,
    AuthModule,
    UserModule,
  ],
  controllers: [AppController],
  providers: [
    AppService,
    {
      provide: APP_GUARD,
      useClass: RolesGuard,
    },
  ],
})

在我的场景中,我使用了两个自定义防护,即身份验证和基于角色的授权。如果我在书籍控制器中使用它们(我在上面的代码中提到过),它会忽略

AuthGuard
并直接执行
RoleGuard

但我想先执行

AuthGuard
,因为我需要用户详细信息来检查
RoleGuard
中的用户角色。

提前致谢。

快乐编码!

node.js mongodb authentication nestjs authorization
1个回答
0
投票

由于

RolesGuard
是一个全局守卫,它会在
AuthGuard
之前预先执行,在我看来,
AuthGuard
应该在
AppModule
级别而不是控制器级别定义:

@Module({
  imports: [
    MongooseModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => ({
        uri: configService.get<string>('MONGO_URI'),
        dbName: configService.get<string>('DATABASE_NAME'),
      }),
      inject: [ConfigService],
    }),
    ConfigModule.forRoot({
      envFilePath: '.env',
      isGlobal: true,
    }),
    BookModule,
    AuthModule,
    UserModule,
  ],
  controllers: [AppController],
  providers: [
    AppService,
    {
      provide: APP_GUARD,
      useClass: AuthGuard,
    },
    {
      provide: APP_GUARD,
      useClass: RolesGuard,
    },
  ],
})

您还应该从

AuthGuard
中删除
book.controller.ts
,因为它将从应用程序模块执行。

© www.soinside.com 2019 - 2024. All rights reserved.