Guard (가드) 란?
개념
- Nest.js 에서 요청이 실제로 처리되기 전에 보통 권한이 있는지 확인하는 방패막이(Guard) 역할을 해주는 컴포넌트다.
- 가드가 없었다면 그냥 컨트롤러로 전해질수도 있는 요청에 이 가드가 중간 브로커 역할을 해서 인증 정보를 확인하거나 사용자 정보를 넣어주는 역할을 하는 것이다.
Nest.js 의 라이프사이클로 보는 가드(Guard)의 위치
세부 설명
CanActive
인터페이스를 구현하고,@Injectable
데코레이터가 붙은 클래스이다.- 런타임에 존재하는 조건에 따라 요청이 라우트 핸들러에 의해 처리될지 여부를 결정한다.
- 조건이란, 권한, 역할(ROLE), ACLs(Access Control Lists) 등을 말한다.
- 흔히 Authorization 이라 불리는 것들과 연관이 깊다.
- Express 애플리케이션에서는 미들웨어가 이 역할을 한다.
- 미들웨어로도 처리가 가능하지만, Guard 는
ExecutionContext
인스턴스에 접근이 가능해서 다음에 무엇이 실행될지 알 수 있는 것이 차이다.
- 미들웨어로도 처리가 가능하지만, Guard 는
예제 인증 가드 구현
BasicTokenGuard
- Basic Token 을 통해 인증을 진행하는 가드의 구현 예제이다.
- 마지막에
request.user
에 사용자 정보를 넣어주는데@Controller
메서드에서 이 가드를 거치면,@Request
데코레이터를 통해 해당 사용자 객체에 접근할 수 있다.
@Injectable()
export class BasicTokenGuard implements CanActivate {
constructor(private readonly authService: AuthService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest();
const rawToken = req.headers["authorization"];
if (!rawToken) {
throw new UnauthorizedException("인증 토큰이 존재하지 않습니다.");
}
const token = this.authService.extractTokenFromHeader(rawToken, false);
const { email, password } = this.authService.decodeBasicToken(token);
const user = this.authService.authenticateWithEmailAndPassword({
email,
password,
});
req.user = user;
return true;
}
}
BearerTokenGuard
BearerTokenGuard
는 사실상 상속용으로 구현했고, 실제로 사용되는 건RefreshTokenGuard
이다.
import {
CanActivate,
ExecutionContext,
Injectable,
UnauthorizedException,
} from "@nestjs/common";
import { AuthService, JwtPayload } from "../auth.service";
import { UsersService } from "src/users/users.service";
import { UsersModel } from "src/users/entities/users.entity";
interface BearerGuardRequest {
token: string;
tokenType: JwtPayload["type"];
user: UsersModel;
headers: {
authorization: string;
};
}
@Injectable()
export class BearerTokenGaurd implements CanActivate {
constructor(
private readonly authService: AuthService,
private readonly usersService: UsersService
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const req: BearerGuardRequest = context.switchToHttp().getRequest();
const rawToken = req.headers["authorization"];
if (!rawToken) {
throw new UnauthorizedException("인증 토큰이 존재하지 않습니다.");
}
const token = this.authService.extractTokenFromHeader(rawToken, true);
const payload = this.authService.verifyToken(token);
req.token = token;
req.tokenType = payload.type;
req.user = await this.usersService.getUserByEmail(payload.email);
return true;
}
}
@Injectable()
export class AccessTokenGuard extends BearerTokenGaurd {
async canActivate(context: ExecutionContext): Promise<boolean> {
await super.canActivate(context);
const req: BearerGuardRequest = context.switchToHttp().getRequest();
if (req.tokenType !== "access") {
throw new UnauthorizedException("액세스 토큰이 아닙니다!");
}
return true;
}
}
@Injectable()
export class RefreshTokenGuard extends BearerTokenGaurd {
async canActivate(context: ExecutionContext): Promise<boolean> {
await super.canActivate(context);
const req: BearerGuardRequest = context.switchToHttp().getRequest();
if (req.tokenType !== "refresh") {
throw new UnauthorizedException("리프레시 토큰이 아닙니다!");
}
return true;
}
}
반응형
'프레임워크 > Nest.js' 카테고리의 다른 글
Nest.js 인터셉터 (Interceptor) 란? (0) | 2024.02.07 |
---|---|
Nest.js 의 Class Validator 란? (0) | 2024.02.02 |
Nest.js 의 파이프(Pipes) 란? (0) | 2024.01.13 |
Nest.js Request Lifecycle 과 기본 아키텍처 (0) | 2024.01.06 |
Nest.js 소개 (0) | 2024.01.06 |