Guards
Last updated
Last updated
가드는 @Injectable()
데코레이터로 주석이 달린 클래스입니다. 가드는CanActivate
인터페이스를 구현해야합니다.
가드는 단독 책임이 있습니다. 런타임에 존재하는 특정 조건 (예: 권한, 역할, ACL 등)에 따라 지정된 요청이 라우트 핸들러에 의해 처리될 지 여부를 결정합니다. 이를 종종 권한이라고합니다. 인증 (및 일반적으로 공동 작업 인 인증)은 일반적으로 기존 Express 응용 프로그램의 미들웨어에 의해 처리되었습니다. 토큰 유효성 검사 및 속성을 요청(Request)
개체에 연결하는 것과 같은 것은 특정 경로 컨텍스트 (및 해당 메타 데이터)와 강력하게 연결되어 있지 않기 때문에 미들웨어는 인증에 적합한 선택입니다.
그러나 미들웨어는 본질적으로 바보입니다. next()
함수를 호출한 후 어떤 핸들러가 실행될지 알 수 없습니다. 반면에 Guards는 ExecutionContext
인스턴스에 액세스할 수 있으므로 다음에 무엇이 실행 될지 정확히 알 수 있습니다. 요청/응답 주기의 정확한 시점에 처리 로직을 삽입하고 선언적으로 처리할 수 있도록 예외 필터, 파이프 및 인터셉터와 같이 설계되었습니다. 이렇게 하면 코드를 건조하고 선언적으로 유지할 수 있습니다.
info 힌트 가드는 각 미들웨어 후에 실행되지만, 인터셉터 나 파이프는 전에 실행됩니다.
언급 한 바와 같이 권한은 발신자 (일반적으로 특정 인증된 사용자)에게 충분한 권한이 있는 경우에만 특정 경로를 사용할 수 있어야 하기 때문에 Guards의 훌륭한 사용 사례입니다. 우리가 구축 할 AuthGuard
는 이제 인증된 사용자를 가정합니다 (따라서 토큰이 요청 헤더에 첨부되어 있음). 토큰을 추출하고 유효성을 검사하고 추출된 정보를 사용하여 요청을 진행할 수 있는지 여부를 결정합니다.
validateRequest()
함수 내부의 논리는 필요한 만큼 간단하거나 정교할 수 있습니다. 이 예의 요점은 보호자가 요청/응답 주기에 어떻게 맞는지 보여줍니다.
모든 가드는 canActivate()
함수를 구현해야합니다. 이 함수는 현재 요청이 허용되는지 여부를 나타내는 조건값을 반환해야 합니다. 응답을 동기식 또는 비동기식으로 반환 할 수 있습니다 (Promise
또는 Observable
을 통해). Nest는 리턴 값을 사용하여 다음 조치를 제어합니다.
true를 반환하면 요청이 처리됩니다.
'false'를 반환하면 Nest는 요청을 거부합니다.
canActivate()
함수는 ExecutionContext
인스턴스라는 단일 인수를 취합니다. ExecutionContext
는 ArgumentsHost
에서 상속받습니다. 예외 필터 챕터에서 앞서 ArgumentsHost
를 보았습니다. 여기서는 original 핸들러로 전달된 인수를 감싸는 래퍼이며 응용 프로그램의 유형에 따라 다른 인수 배열을 포함합니다. 이 주제에 대한 자세한 내용은 예외 필터 챕터을 다시 참조하십시오.
ExecutionContext
는 ArgumentsHost
를 확장하여 현재 실행 프로세스에 대한 추가 정보를 제공합니다. 그 모습은 다음과 같습니다.
getHandler()
메소드는 호출될 핸들러에 대한 참조를 리턴합니다. getClass()
메소드는 이 특정 핸들러가 속하는 Controller
클래스의 유형을 리턴합니다. 예를 들어, 현재 처리 된 요청이 CatsController
에서 create()
메소드로 지정된 POST
요청 인 경우, getHandler()
는 create()
메소드에 대한 참조를 리턴하고 getClass()
는 CatsController
type (인스턴스 아님)을 반환합니다.
특정 역할을 가진 사용자만 액세스 할 수 있는 보다 기능적인 보호 기능을 구축해 보겠습니다. 기본 가드 템플릿으로 시작하여 다음 섹션에서 작성합니다. 현재로서는 모든 요청을 진행할 수 있습니다.
파이프 및 예외 필터와 마찬가지로 가드는 컨트롤러 범위(controller-scoped), 방법 범위(method-scoped) 또는 전역 범위(global-scoped) 일 수 있습니다. 아래는 @UseGuards()
데코레이터를 사용하여 컨트롤러 범위의 보호대를 설정했습니다. 이 데코레이터는 단일 인수 또는 쉼표로 구분된 인수 목록을 사용할 수 있습니다. 이를 통해 한 번의 선언으로 적절한 가드 세트를 쉽게 적용할 수 있습니다.
info 힌트
@UseGuards()
데코레이터는@nestjs/common
패키지에서 가져옵니다.
위에서 우리는 인스턴스 대신에 롤 가드 (RolesGuard) 유형을 전달하여 프레임 워크에 인스턴스화와 의존성 주입을 가능하게 했습니다. 파이프 및 예외 필터와 마찬가지로 내부 인스턴스를 전달할 수도 있습니다.
위의 구성은이 컨트롤러가 선언한 모든 핸들러에 가드를 연결합니다. 가드가 단일 메소드에만 적용되도록 하려면 메소드 수준에서 @UseGuards()
데코레이터를 적용합니다.
전역 가드를 설정하려면 Nest 애플리케이션 인스턴스의 useGlobalGuards()
메소드를 사용하십시오.
warning 알림 하이브리드 앱의 경우
useGlobalGuards()
메소드는 게이트웨이 및 마이크로 서비스에 대한 보호를 설정하지 않습니다. "표준"(하이브리드가 아닌) 마이크로 서비스 앱의 경우useGlobalGuards()
는 가드를 전역으로 마운트합니다.
전역 가드는 모든 컨트롤러와 모든 경로 처리기에 대해 전체 응용 프로그램에서 사용됩니다. 의존성 주입의 관점에서, 모듈 외부에서 등록된 전역 가드 (위의 예에서와 같이 useGlobalGuards()
로)는 의존성이 주입될 수 없습니다. 이는 모듈의 컨텍스트 밖에서 수행되기 때문입니다. 이 문제를 해결하기 위해 다음 구성을 사용하여 모든 모듈에서 직접 가드를 설정할 수 있습니다.
info 힌트 가드에 대한 의존성 주입을 수행하기 위해이 접근 방식을 사용할 때, 이 구성이 사용되는 모듈에 관계없이, 가드는 실제로 전역입니다. 어디에서 해야 합니까? 가드 (위 예에서
RolesGuard
)가 정의된 모듈을 선택하십시오. 또한 커스텀 프로 바이더 등록을 다루는 유일한 방법은useClass
가 아닙니다. 여기에 대해 자세히 알아보십시오.
우리의 RolesGuard
가 작동하고 있지만 아직 똑똑하지는 않습니다. 우리는 아직 가장 중요한 보호 기능인 실행 컨텍스트(execution context)를 활용하지 않습니다. 아직 역할이나 각 처리기에 허용되는 역할에 대해서는 아직 알지 못합니다. 예를 들어 CatsController
는 경로마다 다른 권한 체계를 가질 수 있습니다. 일부는 관리자만 사용할 수 있고 다른 일부는 모든 사람에게 공개될 수 있습니다. 유연하고 재사용 가능한 방식으로 역할과 경로를 어떻게 일치시킬 수 있습니까?
사용자 지정 메타 데이터가 사용되는 곳입니다. Nest는 @SetMetadata()
데코레이터를 통해 핸들러를 라우트하기 위해 커스텀 메타 데이터를 첨부하는 기능을 제공합니다. 이 메타 데이터는 스마트 가드가 결정을 내려야 하는 누락된 역할 데이터를 제공합니다. @SetMetadata()
를 사용하여 봅시다 :
info 힌트
@SetMetadata()
데코레이터는@nestjs/common
패키지에서 가져옵니다.
위의 구성에서 우리는 roles
메타 데이터 (roles
가 핵심이고['admin']
이 특정 값임)를 create()
메소드에 첨부했습니다. 이것이 작동하는 동안 경로에서 직접 @SetMetadata()
를 사용하는 것은 좋지 않습니다. 대신 아래와 같이 자신 만의 데코레이터를 만드십시오.
이 접근 방식은 훨씬 깨끗하고 읽기 쉽고 강력하게 입력됩니다. 이제 커스텀 @Roles()
데코레이터가 있으므로 이를 사용하여 create()
메소드를 장식 할 수 있습니다.
이제 돌아가서 이것을 RolesGuard
와 함께 묶어 봅시다. 현재는 모든 경우에 단순히 true를 반환하여 모든 요청을 진행할 수 있습니다. 현재 사용자에게 할당 된 역할을 처리중인 현재 경로에 필요한 실제 역할과 비교하여 반환 값을 조건부로 만들고 싶습니다. 경로의 역할 (커스텀 메타 데이터)에 액세스하기 위해 프레임 워크에 의해 제공되고@nestjs/core
패키지에서 제공되는 Reflector
헬퍼 클래스를 사용합니다.
info 힌트 node.js 세계에서는 권한이 있는 사용자를
request
오브젝트에 첨부하는 것이 일반적입니다. 따라서 위의 샘플 코드에서request.user
에 사용자 인스턴스와 허용된 역할이 포함되어 있다고 가정합니다. 앱에서 아마도 사용자 지정 인증 가드(authentication guard) (또는 미들웨어)에서 해당 연결을 만들 것입니다.
Reflector
클래스를 사용하면 지정된 key에 의해 메타 데이터에 쉽게 액세스할 수 있습니다 (이 경우 키는 roles
입니다. roles.decorator.ts
파일과 거기에서 만들어진 SetMetadata()
호출을 다시 참조하십시오). 위의 예에서 현재 처리 된 요청 메소드의 메타 데이터를 추출하기 위해 context.getHandler()
를 전달했습니다. getHandler()
는 경로 핸들러 함수에 참조를 제공한다는 것을 기억하십시오.
컨트롤러 메타 데이터를 추출하고 이를 사용하여 현재 사용자 역할을 결정함으로써 이 가드를 보다 일반적으로 만들 수 있습니다. 컨트롤러 메타 데이터를 추출하기 위해 우리는 context.getHandler()
대신 context.getClass()
를 전달합니다:
권한이 충분하지 않은 사용자가 엔드 포인트를 요청하면 Nest는 다음 응답을 자동으로 반환합니다.
뒤에서 가드가 false
를 반환하면 프레임 워크에서 ForbiddenException
이 발생합니다. 다른 오류 응답을 리턴하려면 고유한 예외를 처리해야 합니다. 예를 들면 다음과 같습니다.
가드에 의해 발생 된 예외는 예외 계층(전역 예외 필터 및 현재 컨텍스트에 적용되는 모든 예외 필터)에 의해 처리됩니다.