Guards
Guards
๊ฐ๋๋ @Injectable()
๋ฐ์ฝ๋ ์ดํฐ๋ก ์ฃผ์์ด ๋ฌ๋ฆฐ ํด๋์ค์
๋๋ค. ๊ฐ๋๋CanActivate
์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด์ผํฉ๋๋ค.

๊ฐ๋๋ ๋จ๋
์ฑ
์์ด ์์ต๋๋ค. ๋ฐํ์์ ์กด์ฌํ๋ ํน์ ์กฐ๊ฑด (์: ๊ถํ, ์ญํ , ACL ๋ฑ)์ ๋ฐ๋ผ ์ง์ ๋ ์์ฒญ์ด ๋ผ์ฐํธ ํธ๋ค๋ฌ์ ์ํด ์ฒ๋ฆฌ๋ ์ง ์ฌ๋ถ๋ฅผ ๊ฒฐ์ ํฉ๋๋ค. ์ด๋ฅผ ์ข
์ข
๊ถํ์ด๋ผ๊ณ ํฉ๋๋ค. ์ธ์ฆ (๋ฐ ์ผ๋ฐ์ ์ผ๋ก ๊ณต๋ ์์
์ธ ์ธ์ฆ)์ ์ผ๋ฐ์ ์ผ๋ก ๊ธฐ์กด Express ์์ฉ ํ๋ก๊ทธ๋จ์ ๋ฏธ๋ค์จ์ด์ ์ํด ์ฒ๋ฆฌ๋์์ต๋๋ค. ํ ํฐ ์ ํจ์ฑ ๊ฒ์ฌ ๋ฐ ์์ฑ์ ์์ฒญ(Request)
๊ฐ์ฒด์ ์ฐ๊ฒฐํ๋ ๊ฒ๊ณผ ๊ฐ์ ๊ฒ์ ํน์ ๊ฒฝ๋ก ์ปจํ
์คํธ (๋ฐ ํด๋น ๋ฉํ ๋ฐ์ดํฐ)์ ๊ฐ๋ ฅํ๊ฒ ์ฐ๊ฒฐ๋์ด ์์ง ์๊ธฐ ๋๋ฌธ์ ๋ฏธ๋ค์จ์ด๋ ์ธ์ฆ์ ์ ํฉํ ์ ํ์
๋๋ค.
๊ทธ๋ฌ๋ ๋ฏธ๋ค์จ์ด๋ ๋ณธ์ง์ ์ผ๋ก ๋ฐ๋ณด์
๋๋ค. next()
ํจ์๋ฅผ ํธ์ถํ ํ ์ด๋ค ํธ๋ค๋ฌ๊ฐ ์คํ๋ ์ง ์ ์ ์์ต๋๋ค. ๋ฐ๋ฉด์ Guards๋ ExecutionContext
์ธ์คํด์ค์ ์ก์ธ์คํ ์ ์์ผ๋ฏ๋ก ๋ค์์ ๋ฌด์์ด ์คํ ๋ ์ง ์ ํํ ์ ์ ์์ต๋๋ค. ์์ฒญ/์๋ต ์ฃผ๊ธฐ์ ์ ํํ ์์ ์ ์ฒ๋ฆฌ ๋ก์ง์ ์ฝ์
ํ๊ณ ์ ์ธ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์๋๋ก ์์ธ ํํฐ, ํ์ดํ ๋ฐ ์ธํฐ์
ํฐ์ ๊ฐ์ด ์ค๊ณ๋์์ต๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ์ฝ๋๋ฅผ ๊ฑด์กฐํ๊ณ ์ ์ธ์ ์ผ๋ก ์ ์งํ ์ ์์ต๋๋ค.
info ํํธ ๊ฐ๋๋ ๊ฐ ๋ฏธ๋ค์จ์ด ํ์ ์คํ๋์ง๋ง, ์ธํฐ์ ํฐ ๋ ํ์ดํ๋ ์ ์ ์คํ๋ฉ๋๋ค.
Authorization guard
์ธ๊ธ ํ ๋ฐ์ ๊ฐ์ด ๊ถํ์ ๋ฐ์ ์ (์ผ๋ฐ์ ์ผ๋ก ํน์ ์ธ์ฆ๋ ์ฌ์ฉ์)์๊ฒ ์ถฉ๋ถํ ๊ถํ์ด ์๋ ๊ฒฝ์ฐ์๋ง ํน์ ๊ฒฝ๋ก๋ฅผ ์ฌ์ฉํ ์ ์์ด์ผ ํ๊ธฐ ๋๋ฌธ์ Guards์ ํ๋ฅญํ ์ฌ์ฉ ์ฌ๋ก์
๋๋ค. ์ฐ๋ฆฌ๊ฐ ๊ตฌ์ถ ํ AuthGuard
๋ ์ด์ ์ธ์ฆ๋ ์ฌ์ฉ์๋ฅผ ๊ฐ์ ํฉ๋๋ค (๋ฐ๋ผ์ ํ ํฐ์ด ์์ฒญ ํค๋์ ์ฒจ๋ถ๋์ด ์์). ํ ํฐ์ ์ถ์ถํ๊ณ ์ ํจ์ฑ์ ๊ฒ์ฌํ๊ณ ์ถ์ถ๋ ์ ๋ณด๋ฅผ ์ฌ์ฉํ์ฌ ์์ฒญ์ ์งํํ ์ ์๋์ง ์ฌ๋ถ๋ฅผ ๊ฒฐ์ ํฉ๋๋ค.
@@filename(auth.guard)
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);
}
}
@@switch
import { Injectable } from '@nestjs/common';
@Injectable()
export class AuthGuard {
async canActivate(context) {
const request = context.switchToHttp().getRequest();
return await validateRequest(request);
}
}
validateRequest()
ํจ์ ๋ด๋ถ์ ๋
ผ๋ฆฌ๋ ํ์ํ ๋งํผ ๊ฐ๋จํ๊ฑฐ๋ ์ ๊ตํ ์ ์์ต๋๋ค. ์ด ์์ ์์ ์ ๋ณดํธ์๊ฐ ์์ฒญ/์๋ต ์ฃผ๊ธฐ์ ์ด๋ป๊ฒ ๋ง๋์ง ๋ณด์ฌ์ค๋๋ค.
๋ชจ๋ ๊ฐ๋๋ canActivate()
ํจ์๋ฅผ ๊ตฌํํด์ผํฉ๋๋ค. ์ด ํจ์๋ ํ์ฌ ์์ฒญ์ด ํ์ฉ๋๋์ง ์ฌ๋ถ๋ฅผ ๋ํ๋ด๋ ์กฐ๊ฑด๊ฐ์ ๋ฐํํด์ผ ํฉ๋๋ค. ์๋ต์ ๋๊ธฐ์ ๋๋ ๋น๋๊ธฐ์์ผ๋ก ๋ฐํ ํ ์ ์์ต๋๋ค (Promise
๋๋ Observable
์ ํตํด). Nest๋ ๋ฆฌํด ๊ฐ์ ์ฌ์ฉํ์ฌ ๋ค์ ์กฐ์น๋ฅผ ์ ์ดํฉ๋๋ค.
true๋ฅผ ๋ฐํํ๋ฉด ์์ฒญ์ด ์ฒ๋ฆฌ๋ฉ๋๋ค.
'false'๋ฅผ ๋ฐํํ๋ฉด Nest๋ ์์ฒญ์ ๊ฑฐ๋ถํฉ๋๋ค.
canActivate()
ํจ์๋ ExecutionContext
์ธ์คํด์ค๋ผ๋ ๋จ์ผ ์ธ์๋ฅผ ์ทจํฉ๋๋ค. ExecutionContext
๋ ArgumentsHost
์์ ์์๋ฐ์ต๋๋ค. ์์ธ ํํฐ ์ฑํฐ์์ ์์ ArgumentsHost
๋ฅผ ๋ณด์์ต๋๋ค. ์ฌ๊ธฐ์๋ original ํธ๋ค๋ฌ๋ก ์ ๋ฌ๋ ์ธ์๋ฅผ ๊ฐ์ธ๋ ๋ํผ์ด๋ฉฐ ์์ฉ ํ๋ก๊ทธ๋จ์ ์ ํ์ ๋ฐ๋ผ ๋ค๋ฅธ ์ธ์ ๋ฐฐ์ด์ ํฌํจํฉ๋๋ค. ์ด ์ฃผ์ ์ ๋ํ ์์ธํ ๋ด์ฉ์ ์์ธ ํํฐ ์ฑํฐ์ ๋ค์ ์ฐธ์กฐํ์ญ์์ค.
Execution context
ExecutionContext
๋ ArgumentsHost
๋ฅผ ํ์ฅํ์ฌ ํ์ฌ ์คํ ํ๋ก์ธ์ค์ ๋ํ ์ถ๊ฐ ์ ๋ณด๋ฅผ ์ ๊ณตํฉ๋๋ค. ๊ทธ ๋ชจ์ต์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
export interface ExecutionContext extends ArgumentsHost {
getClass<T = any>(): Type<T>;
getHandler(): Function;
}
getHandler()
๋ฉ์๋๋ ํธ์ถ๋ ํธ๋ค๋ฌ์ ๋ํ ์ฐธ์กฐ๋ฅผ ๋ฆฌํดํฉ๋๋ค. getClass()
๋ฉ์๋๋ ์ด ํน์ ํธ๋ค๋ฌ๊ฐ ์ํ๋ Controller
ํด๋์ค์ ์ ํ์ ๋ฆฌํดํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ํ์ฌ ์ฒ๋ฆฌ ๋ ์์ฒญ์ด CatsController
์์ create()
๋ฉ์๋๋ก ์ง์ ๋ POST
์์ฒญ ์ธ ๊ฒฝ์ฐ, getHandler()
๋ create()
๋ฉ์๋์ ๋ํ ์ฐธ์กฐ๋ฅผ ๋ฆฌํดํ๊ณ getClass()
๋ CatsController
type (์ธ์คํด์ค ์๋)์ ๋ฐํํฉ๋๋ค.
Role-based authentication
ํน์ ์ญํ ์ ๊ฐ์ง ์ฌ์ฉ์๋ง ์ก์ธ์ค ํ ์ ์๋ ๋ณด๋ค ๊ธฐ๋ฅ์ ์ธ ๋ณดํธ ๊ธฐ๋ฅ์ ๊ตฌ์ถํด ๋ณด๊ฒ ์ต๋๋ค. ๊ธฐ๋ณธ ๊ฐ๋ ํ ํ๋ฆฟ์ผ๋ก ์์ํ์ฌ ๋ค์ ์น์ ์์ ์์ฑํฉ๋๋ค. ํ์ฌ๋ก์๋ ๋ชจ๋ ์์ฒญ์ ์งํํ ์ ์์ต๋๋ค.
@@filename(roles.guard)
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;
}
}
@@switch
import { Injectable } from '@nestjs/common';
@Injectable()
export class RolesGuard {
canActivate(context) {
return true;
}
}
Binding guards
ํ์ดํ ๋ฐ ์์ธ ํํฐ์ ๋ง์ฐฌ๊ฐ์ง๋ก ๊ฐ๋๋ ์ปจํธ๋กค๋ฌ ๋ฒ์(controller-scoped), ๋ฐฉ๋ฒ ๋ฒ์(method-scoped) ๋๋ ์ ์ญ ๋ฒ์(global-scoped) ์ผ ์ ์์ต๋๋ค. ์๋๋ @UseGuards()
๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ์ฌ ์ปจํธ๋กค๋ฌ ๋ฒ์์ ๋ณดํธ๋๋ฅผ ์ค์ ํ์ต๋๋ค. ์ด ๋ฐ์ฝ๋ ์ดํฐ๋ ๋จ์ผ ์ธ์ ๋๋ ์ผํ๋ก ๊ตฌ๋ถ๋ ์ธ์ ๋ชฉ๋ก์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด ํ ๋ฒ์ ์ ์ธ์ผ๋ก ์ ์ ํ ๊ฐ๋ ์ธํธ๋ฅผ ์ฝ๊ฒ ์ ์ฉํ ์ ์์ต๋๋ค.
@@filename()
@Controller('cats')
@UseGuards(RolesGuard)
export class CatsController {}
info ํํธ
@UseGuards()
๋ฐ์ฝ๋ ์ดํฐ๋@nestjs/common
ํจํค์ง์์ ๊ฐ์ ธ์ต๋๋ค.
์์์ ์ฐ๋ฆฌ๋ ์ธ์คํด์ค ๋์ ์ ๋กค ๊ฐ๋ (RolesGuard) ์ ํ์ ์ ๋ฌํ์ฌ ํ๋ ์ ์ํฌ์ ์ธ์คํด์คํ์ ์์กด์ฑ ์ฃผ์ ์ ๊ฐ๋ฅํ๊ฒ ํ์ต๋๋ค. ํ์ดํ ๋ฐ ์์ธ ํํฐ์ ๋ง์ฐฌ๊ฐ์ง๋ก ๋ด๋ถ ์ธ์คํด์ค๋ฅผ ์ ๋ฌํ ์๋ ์์ต๋๋ค.
@@filename()
@Controller('cats')
@UseGuards(new RolesGuard())
export class CatsController {}
์์ ๊ตฌ์ฑ์์ด ์ปจํธ๋กค๋ฌ๊ฐ ์ ์ธํ ๋ชจ๋ ํธ๋ค๋ฌ์ ๊ฐ๋๋ฅผ ์ฐ๊ฒฐํฉ๋๋ค. ๊ฐ๋๊ฐ ๋จ์ผ ๋ฉ์๋์๋ง ์ ์ฉ๋๋๋ก ํ๋ ค๋ฉด ๋ฉ์๋ ์์ค์์ @UseGuards()
๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ ์ฉํฉ๋๋ค.
์ ์ญ ๊ฐ๋๋ฅผ ์ค์ ํ๋ ค๋ฉด Nest ์ ํ๋ฆฌ์ผ์ด์
์ธ์คํด์ค์ useGlobalGuards()
๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ญ์์ค.
@@filename()
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new RolesGuard());
warning ์๋ฆผ ํ์ด๋ธ๋ฆฌ๋ ์ฑ์ ๊ฒฝ์ฐ
useGlobalGuards()
๋ฉ์๋๋ ๊ฒ์ดํธ์จ์ด ๋ฐ ๋ง์ดํฌ๋ก ์๋น์ค์ ๋ํ ๋ณดํธ๋ฅผ ์ค์ ํ์ง ์์ต๋๋ค. "ํ์ค"(ํ์ด๋ธ๋ฆฌ๋๊ฐ ์๋) ๋ง์ดํฌ๋ก ์๋น์ค ์ฑ์ ๊ฒฝ์ฐuseGlobalGuards()
๋ ๊ฐ๋๋ฅผ ์ ์ญ์ผ๋ก ๋ง์ดํธํฉ๋๋ค.
์ ์ญ ๊ฐ๋๋ ๋ชจ๋ ์ปจํธ๋กค๋ฌ์ ๋ชจ๋ ๊ฒฝ๋ก ์ฒ๋ฆฌ๊ธฐ์ ๋ํด ์ ์ฒด ์์ฉ ํ๋ก๊ทธ๋จ์์ ์ฌ์ฉ๋ฉ๋๋ค. ์์กด์ฑ ์ฃผ์
์ ๊ด์ ์์, ๋ชจ๋ ์ธ๋ถ์์ ๋ฑ๋ก๋ ์ ์ญ ๊ฐ๋ (์์ ์์์์ ๊ฐ์ด useGlobalGuards()
๋ก)๋ ์์กด์ฑ์ด ์ฃผ์
๋ ์ ์์ต๋๋ค. ์ด๋ ๋ชจ๋์ ์ปจํ
์คํธ ๋ฐ์์ ์ํ๋๊ธฐ ๋๋ฌธ์
๋๋ค. ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋ค์ ๊ตฌ์ฑ์ ์ฌ์ฉํ์ฌ ๋ชจ๋ ๋ชจ๋์์ ์ง์ ๊ฐ๋๋ฅผ ์ค์ ํ ์ ์์ต๋๋ค.
@@filename(app.module)
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_GUARD,
useClass: RolesGuard,
},
],
})
export class AppModule {}
info ํํธ ๊ฐ๋์ ๋ํ ์์กด์ฑ ์ฃผ์ ์ ์ํํ๊ธฐ ์ํด์ด ์ ๊ทผ ๋ฐฉ์์ ์ฌ์ฉํ ๋, ์ด ๊ตฌ์ฑ์ด ์ฌ์ฉ๋๋ ๋ชจ๋์ ๊ด๊ณ์์ด, ๊ฐ๋๋ ์ค์ ๋ก ์ ์ญ์ ๋๋ค. ์ด๋์์ ํด์ผ ํฉ๋๊น? ๊ฐ๋ (์ ์์์
RolesGuard
)๊ฐ ์ ์๋ ๋ชจ๋์ ์ ํํ์ญ์์ค. ๋ํ ์ปค์คํ ํ๋ก ๋ฐ์ด๋ ๋ฑ๋ก์ ๋ค๋ฃจ๋ ์ ์ผํ ๋ฐฉ๋ฒ์useClass
๊ฐ ์๋๋๋ค. ์ฌ๊ธฐ์ ๋ํด ์์ธํ ์์๋ณด์ญ์์ค.
Reflection
์ฐ๋ฆฌ์ RolesGuard
๊ฐ ์๋ํ๊ณ ์์ง๋ง ์์ง ๋๋ํ์ง๋ ์์ต๋๋ค. ์ฐ๋ฆฌ๋ ์์ง ๊ฐ์ฅ ์ค์ํ ๋ณดํธ ๊ธฐ๋ฅ์ธ ์คํ ์ปจํ
์คํธ(execution context)๋ฅผ ํ์ฉํ์ง ์์ต๋๋ค. ์์ง ์ญํ ์ด๋ ๊ฐ ์ฒ๋ฆฌ๊ธฐ์ ํ์ฉ๋๋ ์ญํ ์ ๋ํด์๋ ์์ง ์์ง ๋ชปํฉ๋๋ค. ์๋ฅผ ๋ค์ด CatsController
๋ ๊ฒฝ๋ก๋ง๋ค ๋ค๋ฅธ ๊ถํ ์ฒด๊ณ๋ฅผ ๊ฐ์ง ์ ์์ต๋๋ค. ์ผ๋ถ๋ ๊ด๋ฆฌ์๋ง ์ฌ์ฉํ ์ ์๊ณ ๋ค๋ฅธ ์ผ๋ถ๋ ๋ชจ๋ ์ฌ๋์๊ฒ ๊ณต๊ฐ๋ ์ ์์ต๋๋ค. ์ ์ฐํ๊ณ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ๋ฐฉ์์ผ๋ก ์ญํ ๊ณผ ๊ฒฝ๋ก๋ฅผ ์ด๋ป๊ฒ ์ผ์น์ํฌ ์ ์์ต๋๊น?
์ฌ์ฉ์ ์ง์ ๋ฉํ ๋ฐ์ดํฐ๊ฐ ์ฌ์ฉ๋๋ ๊ณณ์
๋๋ค. Nest๋ @SetMetadata()
๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ํตํด ํธ๋ค๋ฌ๋ฅผ ๋ผ์ฐํธํ๊ธฐ ์ํด ์ปค์คํ
๋ฉํ ๋ฐ์ดํฐ๋ฅผ ์ฒจ๋ถํ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ์ด ๋ฉํ ๋ฐ์ดํฐ๋ ์ค๋งํธ ๊ฐ๋๊ฐ ๊ฒฐ์ ์ ๋ด๋ ค์ผ ํ๋ ๋๋ฝ๋ ์ญํ ๋ฐ์ดํฐ๋ฅผ ์ ๊ณตํฉ๋๋ค. @SetMetadata()
๋ฅผ ์ฌ์ฉํ์ฌ ๋ด
์๋ค :
@@filename(cats.controller)
@Post()
@SetMetadata('roles', ['admin'])
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
@@switch
@Post()
@SetMetadata('roles', ['admin'])
@Bind(Body())
async create(createCatDto) {
this.catsService.create(createCatDto);
}
info ํํธ
@SetMetadata()
๋ฐ์ฝ๋ ์ดํฐ๋@nestjs/common
ํจํค์ง์์ ๊ฐ์ ธ์ต๋๋ค.
์์ ๊ตฌ์ฑ์์ ์ฐ๋ฆฌ๋ roles
๋ฉํ ๋ฐ์ดํฐ (roles
๊ฐ ํต์ฌ์ด๊ณ ['admin']
์ด ํน์ ๊ฐ์)๋ฅผ create()
๋ฉ์๋์ ์ฒจ๋ถํ์ต๋๋ค. ์ด๊ฒ์ด ์๋ํ๋ ๋์ ๊ฒฝ๋ก์์ ์ง์ @SetMetadata()
๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ์ข์ง ์์ต๋๋ค. ๋์ ์๋์ ๊ฐ์ด ์์ ๋ง์ ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ๋ง๋์ญ์์ค.
@@filename(roles.decorator)
import { SetMetadata } from '@nestjs/common';
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
@@switch
import { SetMetadata } from '@nestjs/common';
export const Roles = (...roles) => SetMetadata('roles', roles);
์ด ์ ๊ทผ ๋ฐฉ์์ ํจ์ฌ ๊นจ๋ํ๊ณ ์ฝ๊ธฐ ์ฝ๊ณ ๊ฐ๋ ฅํ๊ฒ ์
๋ ฅ๋ฉ๋๋ค. ์ด์ ์ปค์คํ
@Roles()
๋ฐ์ฝ๋ ์ดํฐ๊ฐ ์์ผ๋ฏ๋ก ์ด๋ฅผ ์ฌ์ฉํ์ฌ create()
๋ฉ์๋๋ฅผ ์ฅ์ ํ ์ ์์ต๋๋ค.
@@filename(cats.controller)
@Post()
@Roles('admin')
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
@@switch
@Post()
@Roles('admin')
@Bind(Body())
async create(createCatDto) {
this.catsService.create(createCatDto);
}
Putting it all together
์ด์ ๋์๊ฐ์ ์ด๊ฒ์ RolesGuard
์ ํจ๊ป ๋ฌถ์ด ๋ด
์๋ค. ํ์ฌ๋ ๋ชจ๋ ๊ฒฝ์ฐ์ ๋จ์ํ true๋ฅผ ๋ฐํํ์ฌ ๋ชจ๋ ์์ฒญ์ ์งํํ ์ ์์ต๋๋ค. ํ์ฌ ์ฌ์ฉ์์๊ฒ ํ ๋น ๋ ์ญํ ์ ์ฒ๋ฆฌ์ค์ธ ํ์ฌ ๊ฒฝ๋ก์ ํ์ํ ์ค์ ์ญํ ๊ณผ ๋น๊ตํ์ฌ ๋ฐํ ๊ฐ์ ์กฐ๊ฑด๋ถ๋ก ๋ง๋ค๊ณ ์ถ์ต๋๋ค. ๊ฒฝ๋ก์ ์ญํ (์ปค์คํ
๋ฉํ ๋ฐ์ดํฐ)์ ์ก์ธ์คํ๊ธฐ ์ํด ํ๋ ์ ์ํฌ์ ์ํด ์ ๊ณต๋๊ณ @nestjs/core
ํจํค์ง์์ ์ ๊ณต๋๋ Reflector
ํฌํผ ํด๋์ค๋ฅผ ์ฌ์ฉํฉ๋๋ค.
@@filename(roles.guard)
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Reflector } from '@nestjs/core';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private readonly 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;
const hasRole = () => user.roles.some((role) => roles.includes(role));
return user && user.roles && hasRole();
}
}
@@switch
import { Injectable, Dependencies } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
@Injectable()
@Dependencies(Reflector)
export class RolesGuard {
constructor(reflector) {
this.reflector = reflector;
}
canActivate(context) {
const roles = this.reflector.get('roles', context.getHandler());
if (!roles) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user;
const hasRole = () => user.roles.some((role) => roles.includes(role));
return user && user.roles && hasRole();
}
}
info ํํธ node.js ์ธ๊ณ์์๋ ๊ถํ์ด ์๋ ์ฌ์ฉ์๋ฅผ
request
์ค๋ธ์ ํธ์ ์ฒจ๋ถํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ ๋๋ค. ๋ฐ๋ผ์ ์์ ์ํ ์ฝ๋์์request.user
์ ์ฌ์ฉ์ ์ธ์คํด์ค์ ํ์ฉ๋ ์ญํ ์ด ํฌํจ๋์ด ์๋ค๊ณ ๊ฐ์ ํฉ๋๋ค. ์ฑ์์ ์๋ง๋ ์ฌ์ฉ์ ์ง์ ์ธ์ฆ ๊ฐ๋(authentication guard) (๋๋ ๋ฏธ๋ค์จ์ด)์์ ํด๋น ์ฐ๊ฒฐ์ ๋ง๋ค ๊ฒ์ ๋๋ค.
Reflector
ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ฉด ์ง์ ๋ key์ ์ํด ๋ฉํ ๋ฐ์ดํฐ์ ์ฝ๊ฒ ์ก์ธ์คํ ์ ์์ต๋๋ค (์ด ๊ฒฝ์ฐ ํค๋ roles
์
๋๋ค. roles.decorator.ts
ํ์ผ๊ณผ ๊ฑฐ๊ธฐ์์ ๋ง๋ค์ด์ง SetMetadata()
ํธ์ถ์ ๋ค์ ์ฐธ์กฐํ์ญ์์ค). ์์ ์์์ ํ์ฌ ์ฒ๋ฆฌ ๋ ์์ฒญ ๋ฉ์๋์ ๋ฉํ ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํ๊ธฐ ์ํด context.getHandler()
๋ฅผ ์ ๋ฌํ์ต๋๋ค. getHandler()
๋ ๊ฒฝ๋ก ํธ๋ค๋ฌ ํจ์์ ์ฐธ์กฐ๋ฅผ ์ ๊ณตํ๋ค๋ ๊ฒ์ ๊ธฐ์ตํ์ญ์์ค.
์ปจํธ๋กค๋ฌ ๋ฉํ ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํ๊ณ ์ด๋ฅผ ์ฌ์ฉํ์ฌ ํ์ฌ ์ฌ์ฉ์ ์ญํ ์ ๊ฒฐ์ ํจ์ผ๋ก์จ ์ด ๊ฐ๋๋ฅผ ๋ณด๋ค ์ผ๋ฐ์ ์ผ๋ก ๋ง๋ค ์ ์์ต๋๋ค. ์ปจํธ๋กค๋ฌ ๋ฉํ ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํ๊ธฐ ์ํด ์ฐ๋ฆฌ๋ context.getHandler()
๋์ context.getClass()
๋ฅผ ์ ๋ฌํฉ๋๋ค:
@@filename()
const roles = this.reflector.get<string[]>('roles', context.getClass());
@@switch
const roles = this.reflector.get('roles', context.getClass());
๊ถํ์ด ์ถฉ๋ถํ์ง ์์ ์ฌ์ฉ์๊ฐ ์๋ ํฌ์ธํธ๋ฅผ ์์ฒญํ๋ฉด Nest๋ ๋ค์ ์๋ต์ ์๋์ผ๋ก ๋ฐํํฉ๋๋ค.
{
"statusCode": 403,
"message": "Forbidden resource"
}
๋ค์์ ๊ฐ๋๊ฐ false
๋ฅผ ๋ฐํํ๋ฉด ํ๋ ์ ์ํฌ์์ ForbiddenException
์ด ๋ฐ์ํฉ๋๋ค. ๋ค๋ฅธ ์ค๋ฅ ์๋ต์ ๋ฆฌํดํ๋ ค๋ฉด ๊ณ ์ ํ ์์ธ๋ฅผ ์ฒ๋ฆฌํด์ผ ํฉ๋๋ค. ์๋ฅผ ๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
throw new UnauthorizedException();
๊ฐ๋์ ์ํด ๋ฐ์ ๋ ์์ธ๋ ์์ธ ๊ณ์ธต(์ ์ญ ์์ธ ํํฐ ๋ฐ ํ์ฌ ์ปจํ ์คํธ์ ์ ์ฉ๋๋ ๋ชจ๋ ์์ธ ํํฐ)์ ์ํด ์ฒ๋ฆฌ๋ฉ๋๋ค.
Last updated
Was this helpful?