NestJS 기초 (12) 가드를 사용한 인증과 인가

2022. 10. 17. 21:19·개발/NestJS

인증 및 인가의 기본 로직은 1. 접근 여부를 파악하고 2. 신원 정보를 확인하고 3. 일치하면 true 아니면 false를 리턴하는 것입니다. 과정을 통과하지 못하는 경우 보통 권한 없음(401) 또는 제한됨(403) 에러를 반환합니다.

 

가드란?

가드는 CanActivate 인터페이스를 구현하며 싱글 리포지토리를 갖습니다. 가드는 요청을 조건에 따라 라우트 핸들러에서 처리 여부를 결정합니다. 이를 보통 인증이라 합니다.

 

Express 애플리케이션에서 인증은 보통 미들웨어에서 처리했습니다. 그러나 미들웨어는 next()를 호출한 다음 어떠한 핸들러가 실행되는지 알지 못합니다. 반면, 가드는 ExecutionContext 인스턴스에 접근할 수 있으며, 다음에 실행될 것을 분명히 알고 있습니다.

가드를 통한 인증 구현

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

모든 가드는 canActivate() 함수를 구현해야 합니다.이는 불리언을 리턴해야 하며, 이를 통해 현재 요청을 허용할 것인지 말 것인지를 결정해야 합니다. 응답을 동기 또는 비동기(Promise 또는 Observable을 통해)로 전달할 수 있습니다.

실행 컨텍스트

canActivate() 함수는 하나의 아규먼트인 ExecutionContext를 받습니다. ExecutionContext는 ArgumentsHost를 상속 받습니다. 앞서 예외 처리 필터에서 ArgumentHost를 살펴본 적이 있습니다. 위 예시에서 동일한 헬머 메소드를 사용하고 있으며, Request 객체의 레퍼런스를 받고 있습니다.

 

ArgumentsHost를 확장하여 ExecutionContext 또한 현재 실행 과정에 추가 정보를 제공하는 새로운 헬퍼들을 추가할 수 있습니다. http를 사용하고 있다면 switchToHttp() 함수를 사용하여 요청을 판단하고 validateRequest 함수가 true 또는 false를 리턴합니다.

실제 가드 구현

이제 보다 실용적인 가드를 구현해보겠습니다. 기본적인 가드 템플릿에서 시작해보겠습니다.

// roles.guard.ts

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

가드 바인딩

파이프와 예외 필터와 마찬가지로 가드는 컨트롤러 스코프, 메소드 스포크, 글로벌 스코프를 가질 수 있습니다. 아래 예시는 UseGuards()를 사용하여 컨트롤러 스코프 가드를 적용한 예시입니다.

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

핸들러에 역할 설정하기

우리의 RolesGuard는 작동하지만 아직 그리 똑똑하지는 않습니다. 가드의 가장 중요한 기능 중 하나인 실행 컨텍스트의 장점을 사용하고 있지 않기 때문인데요. 현재까지는 역할에 대해 알지 못하며, 각 핸들러가 어떤 역할을 허용하는지도 모릅니다.

 

CatsController의 경우 각 라우터에 대해 서로 다른 권한 스키마를 가질 수 있습니다. 어떤 것은 관리자에게 다른 어떤 것은 모두에게 개방되어 있을 것입니다. 이러한 역할을 라우터에 매칭시키는 유연하고 재사용가능한 방법은 무엇일까요?

 

사용자 메타데이터를 활용하면 됩니다. Nest는 @SetMetadata()를 통해 라우트 핸들러에 사용자 메타데이터를 첨부할 수 있게 합니다.

// cats.controller.ts

@Post()
@SetMetadata('roles', ['admin'])
async create(@Body() createCatDto: CreateCatDto) {
  this.catsService.create(createCatDto);
}

이제 우리는 roles 메타데이터터(roles이 키이며 ['adamin']은 값입니다) create 메소드에 첨부했습니다. 작동은 하겠지만 @SetMetadata()를 라우트에 직접 사용하는 것은 좋은 생각은 아닙니다. 다음과 같이 데코레이터를 만들어 사용하는 것이 더 좋습니다.

// roles.decorator.ts

import { SetMetadata } from '@nestjs/common';

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

데코레이터를 활용하는 방법이 더 깔끔하고 읽기에 편합니다. 이제 우리는 커스텀 @Roles 데코레이터를 갖게 되었으며 이를 다음과 같이 사용할 수 있습니다.

// cats.controller.ts

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

최종 정리

이제 RolesGuard와 관련된 내용을 모두 정리해보도록 하겠습니다. 현재 이는 모든 경우에 true를 리턴하며 모든 요청을 처리합니다.

현재 사용자에게 할당된 역할에 따라 조건을 비교하여 요청 처리를 조건화하고자 한다면, Reflector 헬퍼 클래스를 사용할 수 있습니다.

// roles.guards.ts

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

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

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

참고 자료

  • https://docs.nestjs.com/guards
  • https://wikidocs.net/158627

'개발 > NestJS' 카테고리의 다른 글

NestJS 기초 (13) JWT를 사용한 인증 인가 처리와 데코레이터 구현  (0) 2022.11.01
NestJS 기초 (11) API 문서 작성하기 (스웨거)  (0) 2022.10.10
NestJS 기초 (10) 파이프와 유효성 검사  (0) 2022.10.09
'개발/NestJS' 카테고리의 다른 글
  • NestJS 기초 (14) 이미지 파일 업로드하기
  • NestJS 기초 (13) JWT를 사용한 인증 인가 처리와 데코레이터 구현
  • NestJS 기초 (11) API 문서 작성하기 (스웨거)
  • NestJS 기초 (10) 파이프와 유효성 검사
휘Hwi
휘Hwi
여행, 사진, 개발, 책 이야기를 기록하는 여행자 휘의 블로그 𓂍
Klook.com
  • 휘Hwi
    휘: 끝나지 않은 이야기
    휘Hwi
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 기록
        • 일상 에세이
        • 인사이트
        • 투자와 재테크
        • 코인 이야기
        • 아카이빙
        • 집무실 레터
        • 사랑에 대하여
        • 번역 이야기(完)
        • 프리랜서 일지(完)
      • 여행
        • 🌎 세계 여행기 S1 (完)
        • 🌊 삼삼한 여행기 (完)
        • 🚶 온더로드
        • 🇯🇵 일본
        • 🏝️ 발리
        • 🇻🇳 베트남
        • 🇱🇰 스리랑카
        • 🇮🇳 인도
        • 🇹🇭 태국
        • 🇸🇬 싱가포르
        • 🇦🇺 호주
        • 🇭🇰 홍콩
        • 🇰🇷 한국
        • 🍚 여행자의 한 끼
        • ℹ️ 여행 정보
      • 사진
        • 사진가
        • 사진 이론과 생각
        • 사진 관련 정보
      • 영상
        • 파이널컷 모션 공부
        • 고프로 GoPro
        • 영상 관련 정보
      • 책
        • 책 읽고 쓰기
      • 개발
        • 티스토리
        • Internet
        • HTML
        • CSS
        • JavaScript
        • Typescript
        • React
        • Node.js
        • Express
        • NestJS
        • Python
        • Django
        • MySQL
        • MongoDB
        • AWS
        • Deployment
        • Terminal
        • Git
        • Glossaries
        • Articles
        • Projects
        • TIL;
      • 미분류
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
휘Hwi
NestJS 기초 (12) 가드를 사용한 인증과 인가
상단으로

티스토리툴바