npx nx generate @nx/nest:guard admin-role --project=server --directory=guards --language=ts --no-interactive
1. 가드 생성
2. 일반적인 가드와 데코레이터 폴더 경로 Server Side
import {
CanActivate,
ExecutionContext,
HttpException,
Injectable,
UnauthorizedException,
Logger,
} from '@nestjs/common';
import { PrismaService } from '../../../prisma/prisma.service';
import { AuthUtil } from '../auth-util.service';
import { AccessTokenType } from '@namdorang/interface';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(
private readonly authUtil: AuthUtil,
private readonly prismaService: PrismaService
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest(); // Http 요청을 중간에 가로 채기
// 인증 정보 확인을 위해 변수에 할당
const header = request.headers;
const authorization = header.authorization; // authoriztion 이 없음!! 왜?
// 국제 표준 형식 : Authorization: <type> <credentials>
if (!authorization) {
throw new UnauthorizedException('사용자 정보를 찾을 수 없습니다.');
}
// <type> 타입 이름 => Bearer
if (!authorization.startsWith('Bearer ')) {
throw new UnauthorizedException('사용자 정보를 찾을 수 없습니다.');
}
// <credentials> => random string
const token = authorization.split(' ')[1];
if (!token) {
throw new UnauthorizedException('사용자 정보를 찾을 수 없습니다.');
}
let id;
try {
const payload = this.authUtil.verifyToken<AccessTokenType>(token);
if (!payload.id) {
throw new UnauthorizedException('유효하지 않은 토큰입니다.');
}
id = payload.id;
} catch (e) {
throw new HttpException('사용할 수 없는 토큰입니다.', 498);
}
// await
const admin = await this.prismaService.admin.findUnique({ where: { id } });
if (!admin) {
throw new UnauthorizedException('유효하지 않은 토큰입니다.');
}
request.user = admin; // http header 에 인증 정보를 담기
return true;
}
}
import {
CanActivate,
ExecutionContext,
Injectable,
Logger,
UnauthorizedException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { AdminRole } from '@prisma/client';
@Injectable()
// 가드 사용 시 CanActivate 구현
export class AdminRoleGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {} // Reflector 메타 데이터 회수
// ExecutionContext: 현재 실행되는 프로세스의 다양한 정보를 가져올 수 있는 새로운 helper method들을 가지고 있음
async canActivate(context: ExecutionContext): Promise<boolean> {
const required = this.reflector.getAllAndOverride<string[]>('admin_roles', [
context.getHandler(),
context.getClass(),
]);
Logger.log('admin-role.guard start');
const request = context.switchToHttp().getRequest();
Logger.log(request.user);
const user = request.user;
Logger.log(user.username);
const roles: AdminRole[] = (await user)?.roles;
const intersections = roles?.filter((role) => required.includes(role));
if (!intersections || intersections?.length < 1) {
throw new UnauthorizedException('사용 권한이 없습니다.');
}
Logger.log('admin-role.guard end');
return true;
}
}
import {
Logger,
SetMetadata,
UseGuards,
applyDecorators,
} from '@nestjs/common';
import { AdminRole } from '@prisma/client';
import { AuthGuard } from '../guards/auth.guard';
import { AdminRoleGuard } from '../guards/admin-role.guard';
/**해당 코드는 NestJS에서 사용되는 커스텀 데코레이터인 AdminAuth를 정의하는 함수입니다.
* 이 데코레이터는 관리자 권한을 가진 사용자만 접근할 수 있는 엔드포인트를 보호하는 데 사용됩니다.
*/
export function AdminAuth(...roles: AdminRole[]) {
Logger.debug('fwaffwafwfwafwafwafwafw');
return applyDecorators(
/**SetMetadata는 NestJS에서 메타데이터를 설정하기 위한 함수입니다.
* 여기서는 'admin_roles'라는 키로 roles 값을 설정합니다.
* 이를 통해 해당 엔드포인트에 필요한 관리자 역할을 지정할 수 있습니다. */
SetMetadata('admin_roles', roles),
/**
* UseGuards는 NestJS에서 가드를 사용하기 위한 함수입니다.
* 가드는 엔드포인트에 접근하기 전에 실행되는 미들웨어로, 특정 조건을 확인하여 접근을 허용하거나 거부합니다.
* 이 코드에서는 AuthGuard와 AdminRoleGuard 가드를 사용합니다.
*/
UseGuards(AuthGuard, AdminRoleGuard)
);
}
admin-auth.decoratoe.ts 내부: 이제 두 가드를 동시에 사용합니다.
auth.contorller.ts 파일 내부 인증 오퍼레이션에
@AdminAuth(AdminRole.NORMAL, AdminRole.SUPER)
로 선언하여 사용할 수 있습니다.