Authorization + AuthDecorator
1. AuthDecorator 구현
목적
- 미들웨어에서 사용자 정보를 req 객체에 넣었다.
- controller 에서 req객체를 뒤적거리어서 사용자 객체를 얻을 수 있다.
- 위 반복적인 코드를 데코레이터로 만들고자 한다.
로직
- createParamDecorator을 구현하여 ExecutionContext 을 가져온다.
- req에서 사용자 정보를 가져와서 리턴하도록 한다.
구현
import {
createParamDecorator,
ExecutionContext,
SetMetadata,
} from '@nestjs/common';
import { MemberInfo } from 'src/member/entities';
export const AuthUser = createParamDecorator(
(data: unknown, context: ExecutionContext): 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 함수마다 접근 권한을 명시해주어
- 사용자가 가진 권한과 비교하여 통과여부를 결정해 보자.
로직
- CanActivate 함수를 구현하는 provide 클래스를 작성한다.
- reflector를 주입받아서 핸들러 함수에 대한 메타 정보(권한)를 회득
- req의 사용자 정보에서 권한을 획득
- 이 둘을 비교하여 통과 여부를 결정
코드
export type AllowRoles = keyof typeof UserRole | 'Any';
export const Roles = (roles: AllowRoles[]) => SetMetadata('roles', roles);
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { MemberInfo } from 'src/member/entities';
import { AllowRoles } from './auth.decorator';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest<Request>();
const roles = this.reflector.get<AllowRoles>('roles', context.getHandler());
if (!roles) {
return true;
} else {
const memberInfo: MemberInfo = request['memberInfo'];
if (!memberInfo) return false;
else if (roles.includes('Any')) return true;
else return roles.includes(memberInfo.role);
}
return true;
}
}
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { AuthGuard } from './auth.guard';
@Module({
providers: [
{
provide: APP_GUARD,
useClass: AuthGuard,
},
],
})
export class AuthModule {}
- App.module에 적용하기
- API 핸들러 함수에 적용하기
@Roles(['Any'])
@Get('getMyStrategyList')
async getMyStrategyList(@AuthUser() MemberInfo: MemberInfo) {
return this.strategyService.getMyStrategyList({
email_id: MemberInfo.email_id,
});
}