개발이야기

Nestjs로 구현하는 Authentication & Authorization(4)

adoreje 2021. 4. 11. 16:13

이 포스트는 NestJS를 사용하여 로그인 API를 구현하는 방법을 정리한 글이다. NestJS 의 공식문서를 참고하였으며 passport, typeorm 을 사용한다. 아래와 같이 4개의 글로 나누어 작성하였으며 이 글에서는 네번째로 passport를 사용하여 권한처리를 하는 방법을 정리하였다.

 

전체 코드는 github에 업로드 되어 있다.

1. User Controller 수정

권한처리를 위해 User Controller를 다음과 같이 수정한다.

  @Get()
  @Role("admin")
  @UseGuards(RolesGuard)
  @UseGuards(JwtAuthGuard)
  findAll(): Promise<User[]> {
    return this.userService.findAll();
  }

권한처리를 적용할 함수 위에 권한처리를 위한 설정을 한다. 테스트를 위해 모든 사용자 정보를 반환하는 findAll 함수를 관리자 권한을 가진 사람만이 요청할 수 있도록 설정한다.

2. 권한처리

2.1 Role Decorator

// roles.decorator.ts

export const ROLES_KEY = 'roles';
export const Role = (role: string) => SetMetadata(ROLES_KEY, role);

decorator를 사용하여 입력받은 role을 메타데이터에 저장한다.

2.2 Roles Guard

// roles.guard.ts

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

  canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
    const requiredRole = this.reflector.getAllAndOverride<string>(ROLES_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    console.log(requiredRole);
    if (!requiredRole) {
      return true;
    }

    const { user } = context.switchToHttp().getRequest();
    console.log(user);
    return requiredRole === user.role;
  }
}

RolesGuard는 CanActivate를 구현하는 Guard class이다. 생성자에서 Reflrector를 먼저 선언한다. Reflector는 helper class로 reflrector를 사용하면 metadata의 정보를 읽어올 수 있다. Role("admin") 데코레이터를 사용하여 "admin"을 metadata에 이미 저장하였다. 이 값을 확인하여 user객체에 있는 권한과 일치하면 findAll 함수를 실행하고 일치하지 않으면 예외를 던진다.

 

그렇다면 user객체는 어디에서 읽어오는가?

2.3 JWT Auth Guard

Nestjs에서 Guard는 역순으로 실행된다. 즉, 데코레이터를 사용하여 선언할 때 아래쪽에 선언된 Guard가 먼저 실행된다.

GET http://localhost:3000/user/
Authorization: "Bearer " + {accessTokenAdmin}

따라서 위와 같은 요청이 들어오면 먼저 JwtAuthGuard에서 토큰을 검증한 후 토큰에 저장된 정보를 반환한다. 이는 JwtStrategy의 validate 함수에서 이루어진다. 반환된 정보는 request 객체 안에 user라는 이름으로 들어가고 이후 RolesGuard에서 user객체 안의 role값을 읽어서 권한을 확인한다.