NestJS Guards

paduck·2024년 6월 12일
0

NestJS

목록 보기
9/24

@Injectable() 데코레이터가 달린 클래스
CanActivate 인터페이스를 구현

단일 책임 원칙
인증

  • 특정 조건(권한, 역할, ACL 등)에 따라 주어진 요청이 라우트 핸들러에 의한 처리 여부를 결정

인증/권한은 전통적인 Express 애플리케이션에서 종종 미들웨어에 의해 처리

  • 토큰 유효성 검사나 request 객체에 속성을 첨부하는 작업은 특정 라우트 컨텍스트(및 메타데이터)와 강하게 연결되지 않기 때문
  • 미들웨어는 본질적으로 어떤 핸들러가 next() 함수 호출 후에 실행될지 알 수 없음
    반면, GuardExecutionContext 인스턴스에 접근하여 이후 실행 대상 확인 가능

예외 필터, 파이프, 인터셉터와 마찬가지로 요청/응답 사이클의 정확한 지점에서 처리 로직을 삽입하도록 설계, 선언적으로 수행 가능

가드는 모든 미들웨어가 실행된 , 인터셉터나 파이프가 실행되기 전에 실행

Authorization guard

충분한 권한을 가지고 있을 때만 사용하는 경우

  • 예시로, 토큰 추출, 유효성 검사, 요청이 진행될 수 있는지 여부를 결정
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    return validateRequest(request);
  }
}
  • validateRequest 는 커스텀 함수

모든 가드는 canActivate() 함수를 구현해야 함

  • 현재 요청이 허용되는지 여부를 나타내는 boolean 값 반환

  • 이는 동기적으로 또는 비동기적으로(Promise 또는 Observable을 통해) 응답 반환

    • true를 반환하면 요청이 처리
    • false를 반환하면 Nest는 요청을 거부

Execution context

canActivate() 함수는 단일 인수로 ExecutionContext 인스턴스 받음ExecutionContextArgumentsHost를 상속

ArgumentsHost를 확장해 ExecutionContext는 현재 실행 프로세스에 대한 추가 세부 정보를 제공하는 여러 새로운 헬퍼 메서드를 추가

  • 다양한 컨트롤러, 메서드 및 실행 컨텍스트에서 동작하게 함

여기

Role-based authentication

모든 역할 허용에서 특정 역할 허용 예시:

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class RolesGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    return true;
  }
}

Binding guards

컨트롤러 범위, 메서드 범위 또는 전역 범위로 설정 가능

  • @UseGuards() 데코레이터를 사용하여 컨트롤러 범위 가드를 설정
  • 단일-복수 인수 목록을 받음
@Controller('cats')
@UseGuards(RolesGuard)
export class CatsController {}

인스턴스가 아닌 클래스로 전달하여 프레임워크가 인스턴스화를 책임지도록 하고 의존성 주입을 활성화:

@Controller('cats')
@UseGuards(new RolesGuard())
export class CatsController {}

단일 메서드에 적용시 메서드 수준에서 @UseGuards() 데코레이터를 적용

전역 가드는 Nest 애플리케이션 인스턴스의 useGlobalGuards() 메서드를 사용:

const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new RolesGuard());

의존성 주입 측면에서 전역 가드는 모듈 외부에서 등록될 경우(위 예제에서처럼 useGlobalGuards()를 사용하여) 의존성을 주입할 수 없음

모듈 내부에서 직접 전역 가드를 설정:

import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';

@Module({
  providers: [
    {
      provide: APP_GUARD,
      useClass: RolesGuard,
    },
  ],
})
export class AppModule {}

Setting roles per handler

역할이나 각 핸들러에 대해 허용되는 역할에 대해 알지 못하는 단순한 상태
커스텀 메타데이터로 해결

Reflector.createDecorator 정적 메서드,
생성된 데코레이터나 내장된 @SetMetadata() 데코레이터를 통해,
라우트 핸들러에 커스텀 메타데이터를 첨부 가능

  • Reflector는 프레임워크에 의해 제공되며 @nestjs/core 패키지
import { Reflector } from '@nestjs/core';

export const Roles = Reflector.createDecorator<string[]>();
  • Roles 데코레이터는 string[] 타입의 단일 인수를 받는 함수

이제 이 데코레이터를 사용하려면 핸들러에 주석을 추가하기만 하면 됩니다:

@Post()
@Roles(['admin'])
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto

);
}

Reflector.createDecorator 메서드를 사용하는 대신 내장된 @SetMetadata() 데코레이터를 사용할 수 있습니다. 자세한 내용은 여기를 참조하세요.

Putting it all together

현재 처리 중인 라우트의 실제 역할과 현재 사용자에게 할당된 역할을 비교하여 조건부 처리:

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Roles } from './roles.decorator';

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

  canActivate(context: ExecutionContext): boolean {
    const roles = this.reflector.get(Roles, context.getHandler());
    if (!roles) {
      return true;
    }
    const request = context.switchToHttp().getRequest();
    const user = request.user;
    return matchRoles(roles, user.roles);
  }
}

Reflection and metadata 섹션에서 Reflector를 컨텍스트에 맞게 사용하는 방법에 대해 자세히 알아보세요.

권한이 부족한 경우라면:

{
  "statusCode": 403,
  "message": "Forbidden resource",
  "error": "Forbidden"
}
  • false를 반환하면 프레임워크는 ForbiddenException 을 기본 반환

특정 예외:

throw new UnauthorizedException();

가드에서 발생한 모든 예외는 예외 레이어에 의해 처리

실제 예제에 대해 자세히 알아보려면 여기를 참조하세요.

profile
학습 velog

0개의 댓글