Що таке guards у NestJS і як реалізувати аутентифікацію?
Guards у NestJS
Guards визначають, чи слід обробляти запит обробником маршруту чи ні. Вони реалізують інтерфейс CanActivate і повертають true (дозволити) або false (відмовити). Guards найчастіше використовуються для автентифікації та авторизації.
Створення Guard
typescript
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 this.validateRequest(request);
}
private validateRequest(request: any): boolean {
return !!request.headers.authorization;
}
}JWT Auth Guard
typescript
// guards/jwt-auth.guard.ts
import {
Injectable, CanActivate, ExecutionContext, UnauthorizedException
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Request } from 'express';
@Injectable()
export class JwtAuthGuard implements CanActivate {
constructor(private jwtService: JwtService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest<Request>();
const token = this.extractToken(request);
if (!token) throw new UnauthorizedException('Токен не надано');
try {
const payload = await this.jwtService.verifyAsync(token, {
secret: process.env.JWT_SECRET,
});
request['user'] = payload; // прикріпити до запиту
return true;
} catch {
throw new UnauthorizedException('Недійсний або прострочений токен');
}
}
private extractToken(request: Request): string | null {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : null;
}
}Roles Guard (Авторизація)
typescript
// guards/roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ROLES_KEY } from '../decorators/roles.decorator';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.getAllAndOverride<string[]>(
ROLES_KEY,
[context.getHandler(), context.getClass()],
);
if (!requiredRoles) return true; // ролі не потрібні → дозволити
const { user } = context.switchToHttp().getRequest();
return requiredRoles.some(role => user.roles?.includes(role));
}
}
// decorators/roles.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const ROLES_KEY = 'roles';
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);Застосування Guards
typescript
// Рівень методу
@Get('profile')
@UseGuards(JwtAuthGuard)
getProfile(@Request() req) {
return req.user;
}
// Рівень контролера
@Controller('admin')
@UseGuards(JwtAuthGuard, RolesGuard)
export class AdminController {}
// Глобально (всі маршрути)
// main.ts
app.useGlobalGuards(new JwtAuthGuard(jwtService));
// Або через модуль (бажано — підтримує DI)
@Module({
providers: [
{ provide: APP_GUARD, useClass: JwtAuthGuard },
],
})Декоратор публічних маршрутів
typescript
// decorators/public.decorator.ts
export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
// У JwtAuthGuard.canActivate():
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
if (isPublic) return true; // пропустити автентифікацію для маршрутів @Public()
// Використання:
@Public()
@Post('auth/login')
login(@Body() loginDto: LoginDto) { ... }Guard vs Middleware vs Interceptor
| Guard | Middleware | Interceptor | |
|---|---|---|---|
Має ExecutionContext | ✅ | ❌ | ✅ |
| Може отримувати метадані | ✅ | ❌ | ✅ |
| Призначення | Автентифікація / Авторизація | Логування, CORS | Перетворення відповіді |
| Може перервати запит | ✅ | ✅ | ✅ |
Резюме
Guards є шаром безпеки NestJS. Реалізуйте CanActivate, отримуйте запит через ExecutionContext і повертайте true або викидайте UnauthorizedException. Використовуйте Reflector + кастомні метадані декоратори (як-от @Roles()) для детального контролю доступу на основі ролей.
Коротка відповідь
Для співбесідиPremium
Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.