[NestJS] Authorization + AuthDecorator

도도·2021년 8월 1일
0

기술Velog

목록 보기
7/28

Authorization + AuthDecorator

1. AuthDecorator 구현

목적

  • 미들웨어에서 사용자 정보를 req 객체에 넣었다.
  • controller 에서 req객체를 뒤적거리어서 사용자 객체를 얻을 수 있다.
  • 위 반복적인 코드를 데코레이터로 만들고자 한다.

로직

  1. createParamDecorator을 구현하여 ExecutionContext 을 가져온다.
  2. req에서 사용자 정보를 가져와서 리턴하도록 한다.

구현

import {
  createParamDecorator,
  ExecutionContext,
  SetMetadata,
} from '@nestjs/common';
import { MemberInfo } from 'src/member/entities';

export const AuthUser = createParamDecorator( // createParamDecorator 가져ㅇ옴
  (data: unknown, context: ExecutionContext): MemberInfo => {
      // ExecutionContext 실행 컨테스트가 http 프로토콜을 이용하고
      //  Http 변환 후 request객체를 얻어, memberInfo 을 리턴 하도록 한다.
    if (
      context['contextType'] &&
      String(context['contextType']).startsWith('http')
    ) {
      const request = context.switchToHttp().getRequest();
      return request['memberInfo'];
    }
  },
);

2. Authorization 구현

목적

  • 어떤 REST API 는 누구나 접근해서 정보를 획득 할 수 있다.
  • 하지만 어떤 API는 회원가입이 된 사람만, 혹은 관리자만 접근할 수 있다.
  • 발급된 JWT 토큰을 보고 사용자 권한을 확인하여, API접근을 허용할지 결정해보자.

기존의 미들웨어와 AuthGuard의 다른 점

  • 보통의 미들웨어는 req의 정보를 처리하는 로직만 담당한다.
  • 나중에 있을 핸들러 함수에 대한 정보는 없다.
  • 멍청한 미들웨어 함수와 달리(단방향)
  • AuthGuard는 API 핸들러 함수에 대한 메타데이터를 볼 수 있다.(양방향)
  • controller 의 API 함수마다 접근 권한을 명시해주어
  • 사용자가 가진 권한과 비교하여 통과여부를 결정해 보자.

로직

  1. CanActivate 함수를 구현하는 provide 클래스를 작성한다.
  2. reflector를 주입받아서 핸들러 함수에 대한 메타 정보(권한)를 회득
  3. req의 사용자 정보에서 권한을 획득
  4. 이 둘을 비교하여 통과 여부를 결정

코드

    1. auth.decorator.ts
// enum UserRole 의 key값과 Any 타입을 추가 하자.
export type AllowRoles = keyof typeof UserRole | 'Any';
// SetMetadata는 API 핸들러 함수에 적용될 데코레이터 이다.
export const Roles = (roles: AllowRoles[]) => SetMetadata('roles', roles);
    1. auth.guard.ts
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { MemberInfo } from 'src/member/entities';
import { AllowRoles } from './auth.decorator';

@Injectable() // 반드시 Injectable 선언, Nest에서  Reflector 주입가능
export class AuthGuard implements CanActivate { // CanActivate 구현 
  constructor(private readonly reflector: Reflector) {} //  Reflector을 선언하면 알아서 주입된다.

  canActivate(context: ExecutionContext) {  
    const request = context.switchToHttp().getRequest<Request>(); // Http 로 context를 변환한다.
    // ( 물론 gqlContext 또한 변환 가능 )
    const roles = this.reflector.get<AllowRoles>('roles', context.getHandler());
    // CASE1. dont' Need Authentication
    // 누구나 토큰없이 API 접근 가능
    if (!roles) {
      return true;
      // CASE2. Need Authentication
    } else {
        // 사용자 정보 획득
      const memberInfo: MemberInfo = request['memberInfo'];
      if (!memberInfo) return false;
      // Any 라면 다 통과
      else if (roles.includes('Any')) return true;
      // 그렇지 않다면 접근 권한 비교
      else return roles.includes(memberInfo.role);
    }

    return true;
    // const request = context.switchToHttp().getRequest<Request>();
    // return request.originalUrl;
  }
}
    1. auth.module.ts
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { AuthGuard } from './auth.guard';

@Module({
  providers: [
    {
      provide: APP_GUARD, // 반드시 Nest의 예약어로 provide 설정
      useClass: AuthGuard,
    },
  ],
})
export class AuthModule {}
    1. App.module에 적용하기
    1. API 핸들러 함수에 적용하기
  // (GET) getMyStrategyListById(5) 나의 전략 조회(리스트)
  @Roles(['Any'])
  @Get('getMyStrategyList')
  async getMyStrategyList(@AuthUser() MemberInfo: MemberInfo) {
    return this.strategyService.getMyStrategyList({
      email_id: MemberInfo.email_id,
    });
  }
profile
프론트 주력의 JS 개발자 입니다.! Node.js, React, NestJS 에 관심이 많습니다.

0개의 댓글