TIL

23/12/28 TIL __ nestJs 핸들러에 요청이 접근하지 못하고 에러가 날때. RolesGuard

GABOJOK 2023. 12. 29. 00:04

 

 

nest js 에서는 컨트롤러에서 요청을 처리할때

express의 미들웨어 처럼 해당 사용자에 대해 원하는 조건에 해당하는 사용자만 접근할 수있도록 거를 수 있는 장치가 있는데,

그중 하나가 rolesguard 이다. 

 

따로 rolesguard 라는 파일을 만들어 두고 그 파일을 이용해서 접근하는데, 

접근은 이런식으로 한다. 

@UseGuards(RolesGuard)
@Controller('concert'){

}

 

 

먼저 rolesguard 라는 파일은 아래와 같이 사용했다. 

 

import {
  CanActivate,
  ExecutionContext,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { Role } from 'src/user/types/userRole.type';

@Injectable()
export class RolesGuard extends AuthGuard('jwt') implements CanActivate {
  constructor(private reflector: Reflector) {
    super();
  }

  async canActivate(context: ExecutionContext) {
    //로그인이 되어 있는 유저인가 확인.(req가 있는지)
    const authenticated = await super.canActivate(context);
    if (!authenticated) {
      return false;
    }

    //핸들러에 걸려있는 권한 체크
    const requiredRoles = this.reflector.getAllAndOverride<Role[]>('roles', [
      context.getHandler(), //req, res객체 읽음.
      context.getClass(), //클래스에 설정된 데코레이터, 메타데이터를 읽음.
    ]);

    //권한이 안걸려 있더라고 볼 수 있도록 true? 통과시킬거라는말.
    if (!requiredRoles) {
      return true;
    }

    //req.user.role과 비교해서 true인 경우 이 미들웨어 통과.
    const { user } = context.switchToHttp().getRequest();
    const result = requiredRoles.some((role) => {
      return user.role === role;
    });
    if (!result) {
      throw new UnauthorizedException('권한이 없습니다.');
    } else {
      return result;
    }
  }
}

 

 

이 롤가드를 통과하고 해당 컨트롤러 경로로 접근하는 것이다. 

 

위에서 보다싶이 로그인 된 사용자던 그렇지 않던 해당 경로를 통과시키는데,  

로그인 된 사용자라면, 개발자가 요청한 권한과 일치하는 경우만 통과시키는 로직이다. 

 

 

 

그다음 가드를 또 걸어놨는데,  수정 요청이 들어오는 경로에 접근하기 이전에, req 객체를 받아 user라는 변수에 유저 정보를 저장하고,

해당 경로에 대한 요청은 admin인 사람과, performer인 사람만 접근이 가능하도록 하였다. 

 

@UseGuards(RolesGuard)
@Controller('concert')
export class ConcertController {
  private logger = new Logger(); //로그 찍기, boardsController아니여도 됨.
  constructor(private readonly concertService: ConcertService) {}

    //공연 수정하기
      @UseGuards(AuthGuard('jwt'))
      @Roles(Role.ADMIN, Role.PERFORMER)
      @Put(':id')
      async modifyConcert(
        @Param('id') id: number,
        @Body() modifyData: ConcertDto, //유저가 어떤걸 수정할지 모르니 ConcertDto로 하고, Put메소드 사용.
      ): Promise<Concert> {
        return await this.concertService.modifyConcert(id, modifyData);
      }
}

 

 

이때 제대로 한것 같은데 뭔가 문제가 있다면,

타입 또한 제대로 지정을 해줬는지 살펴보자.

타입에 해당 값이 존재하지 않는게 껴있다면,??

제대로 작동하지 않는다. !!!!!!!!!🫠

 

 

 

 

@Roles 함수는 아래  파일에서 가져오는건데, 

이것 또한 커스텀 데코레이터라고 볼 수 있다 .

 

import { SetMetadata } from '@nestjs/common';
import { Role } from 'src/user/types/userRole.type';

export const Roles = (...roles: Role[]) => {
  console.log('roles', roles);
  return SetMetadata('roles', roles);
};

 

SetMetadata 는 nestjs의 데코레이터

 

 

...roles를 사용하는 이유

 @Roles(Role.Admin, Role.User)와 같이 사용하면
'Admin' 또는 'User' 역할 중 하나라도 가지고 있는 사용자에게 접근을 허용할 수 있도록 하기 위해서 사용한다. 

 

 

 

 

추가로 

혹시 나는 제대로 한것 같은데 계속 원하는 롤가드가 제대로 작동하지 않는 경우,

순서를 바꿔보자. 

 

 

나의 경우 특정 요청은 롤가드를 걸고,

다른 요청은 롤가드를 걸지 않은 채로

하나의 컨트롤러 안에 넣어두었는데,

제대로 작동하지 않아 혹시나 싶어 순서를 바꿔보니 제대로 작동했다.

 

 

코드를 컴파일 순서가 다르게 되어 생기는 문제인가 싶었지만,

과제를 어서 완성해야 하기에 다음에 이부분은 찾아보겠다.