Guards — Authorization, Role-based
Guard nima?
Tasavvur qiling: siz katta mehmonxonaga keldingiz. Kirish eshigidan o'tdingiz — bu Middleware. Lift oldida xavfsizlik xodimi turibdi. U sizning kartangizni tekshiradi — 5-qavatga ruxsatingiz bormi? Bor bo'lsa — o'tkazadi. Yo'q bo'lsa — o'tkazmaydi. Bu xavfsizlik xodimi — Guard.
Middleware va Guard ning farqi shunda:
Middleware — so'rov kimdan kelayotganini bilmaydi. U faqat so'rovni ko'radi, loglaydi, header qo'shadi. "Bu foydalanuvchi kim?" — bu savol uni qiziqtirmaydi.
Guard — aynan shu savolga javob beradi. "Bu foydalanuvchi kim? Uning bu sahifaga kirish huquqi bormi?" Guard true qaytarsa — so'rov davom etadi. false qaytarsa — 403 Forbidden xatosi qaytadi.
Guard qanday ishlaydi?
Guard CanActivate interfeysini implement qiladi. Bu interfeys bitta metod talab qiladi — canActivate. Bu metod true yoki false qaytaradi.
// common/guards/auth.guard.ts
import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common";
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
// HTTP so'rovini olamiz
const request = context.switchToHttp().getRequest();
// Header dan token ni olamiz
const token = request.headers["authorization"];
// Token bo'lsa — o'tkazamiz, bo'lmasa — to'xtatamiz
return !!token;
}
}
ExecutionContext — bu Guard ning ko'zi. U orqali so'rov haqida hamma narsani bilasiz: HTTP so'rovimi, WebSocket mi, gRPC mi — hammasiga kirish imkoni bor.
context.switchToHttp().getRequest() — HTTP so'rovini olish. Middleware dagidek req ob'ekti — header lar, body, foydalanuvchi ma'lumotlari hammasi shu yerda.
!!token — token mavjud bo'lsa true, bo'lmasa false. Ikki ! belgisi har qanday qiymatni boolean ga aylantiradi.
Guard ni ulash
Guard uchta joyda ishlatilishi mumkin — global, controller darajasida, metod darajasida.
Metod darajasida — faqat shu endpoint himoyalanadi:
// users/users.controller.ts
import { Controller, Get, UseGuards } from "@nestjs/common";
import { AuthGuard } from "../common/guards/auth.guard";
@Controller("users")
export class UsersController {
@Get()
@UseGuards(AuthGuard) // faqat shu metod himoyalangan
findAll() {
return "Faqat login qilganlar ko'ra oladi";
}
@Get("public")
publicEndpoint() {
return "Hamma ko'ra oladi"; // Guard yo'q
}
}
Controller darajasida — controllerdagi barcha metodlar himoyalanadi:
@Controller('users')
@UseGuards(AuthGuard) // barcha metodlar himoyalangan
export class UsersController {
@Get()
findAll() { ... }
@Post()
create() { ... }
}
Global darajasida — butun ilova himoyalanadi:
// main.ts
import { AuthGuard } from "./common/guards/auth.guard";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new AuthGuard());
await app.listen(3000);
}
Global Guard qo'yilganda — barcha endpointlar himoyalanadi. Bu ba'zan muammo — login va ro'yxatdan o'tish endpointlari ham himoyalanib qoladi. Ularni ochiq qoldirish uchun keyingi bo'limda @Public() decorator ni ko'ramiz.
JWT Guard — real hayotdagi misol
Haqiqiy loyihalarda Guard JWT token ni tekshiradi. JWT — bu foydalanuvchi login qilganda berilgan maxsus raqamli imzolangan ma'lumot. Har bir so'rovda shu token yuboriladi.
npm install @nestjs/jwt
// common/guards/jwt-auth.guard.ts
import {
Injectable,
CanActivate,
ExecutionContext,
UnauthorizedException,
} from "@nestjs/common";
import { JwtService } from "@nestjs/jwt";
@Injectable()
export class JwtAuthGuard implements CanActivate {
constructor(private readonly jwtService: JwtService) {}
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
// Authorization header dan token olamiz
// Format: "Bearer eyJhbGciOiJIUzI1NiJ9..."
const authHeader = request.headers["authorization"];
if (!authHeader) {
throw new UnauthorizedException("Token mavjud emas");
}
// "Bearer " qismini olib tashlaymiz
const token = authHeader.split(" ")[1];
if (!token) {
throw new UnauthorizedException("Token noto'g'ri formatda");
}
try {
// Token ni tekshiramiz va ichidagi ma'lumotni olamiz
const payload = this.jwtService.verify(token);
// Foydalanuvchi ma'lumotini so'rovga qo'shamiz
// Controller da @Req() orqali olish mumkin bo'ladi
request.user = payload;
return true;
} catch (error) {
throw new UnauthorizedException("Token yaroqsiz yoki muddati o'tgan");
}
}
}
jwtService.verify(token) — token ni tekshiradi. Token to'g'ri bo'lsa — ichidagi ma'lumotni qaytaradi. Noto'g'ri bo'lsa — xato otadi.
request.user = payload — bu juda muhim qator. Foydalanuvchi ma'lumotini so'rovga yozib qo'yamiz. Keyingi bosqichlarda — Controller, Service — bu ma'lumotni olish mumkin bo'ladi.
Controller da foydalanuvchini olish:
import { Controller, Get, UseGuards, Req } from "@nestjs/common";
import { JwtAuthGuard } from "../common/guards/jwt-auth.guard";
@Controller("profile")
export class ProfileController {
@Get()
@UseGuards(JwtAuthGuard)
getProfile(@Req() req: any) {
// Guard request.user ga yozgan edi — shu yerda olamiz
return req.user;
}
}
Role-based Authorization — rol asosida ruxsat
Ba'zan faqat login qilgan bo'lish yetarli emas. Masalan, foydalanuvchini o'chirish — faqat admin rol egasi qila olishi kerak. Bu — rol asosida ruxsat berish.
Buning uchun avval @Roles() decorator yaratamiz:
// common/decorators/roles.decorator.ts
import { SetMetadata } from "@nestjs/common";
export enum Role {
USER = "user",
MODERATOR = "moderator",
ADMIN = "admin",
}
// Bu decorator endpointga "kerakli rol" ma'lumotini yozadi
export const Roles = (...roles: Role[]) => SetMetadata("roles", roles);
SetMetadata — bu NestJS ning qurilmasi. U berilgan ma'lumotni route ga "yorliq" sifatida yopishtirib qo'yadi. Guard keyinchalik bu yorliqni o'qiy oladi.
Endi RolesGuard yozamiz:
// common/guards/roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common";
import { Reflector } from "@nestjs/core";
import { Role } from "../decorators/roles.decorator";
@Injectable()
export class RolesGuard implements CanActivate {
// Reflector — metadata ni o'qish uchun NestJS qurilmasi
constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
// Bu endpointga qaysi rollar kerakligini o'qiymiz
const requiredRoles = this.reflector.getAllAndOverride<Role[]>("roles", [
context.getHandler(), // metod darajasidagi metadata
context.getClass(), // controller darajasidagi metadata
]);
// Agar rol ko'rsatilmagan bo'lsa — hamma o'ta oladi
if (!requiredRoles) {
return true;
}
// So'rovdan foydalanuvchini olamiz
// JwtAuthGuard avval ishlagan va request.user ga yozgan bo'lishi kerak
const { user } = context.switchToHttp().getRequest();
// Foydalanuvchining roli kerakli rollar ichida bormi?
return requiredRoles.some((role) => user?.role === role);
}
}
Reflector — bu NestJS ning maxsus xizmati. U SetMetadata bilan yozilgan ma'lumotlarni o'qiydi.
getAllAndOverride — avval metod darajasidagi metadata ni qidiradi, topilmasa controller darajasidagini oladi. Bu juda qulay — controller da umumiy rol, metodda aniq rol belgilash imkonini beradi.
Guard larni birga ishlatish
JwtAuthGuard va RolesGuard birga ishlashi kerak. Avval token tekshiriladi, keyin rol:
// users/users.controller.ts
import { Controller, Get, Delete, Param, UseGuards } from "@nestjs/common";
import { JwtAuthGuard } from "../common/guards/jwt-auth.guard";
import { RolesGuard } from "../common/guards/roles.guard";
import { Roles, Role } from "../common/decorators/roles.decorator";
@Controller("users")
@UseGuards(JwtAuthGuard, RolesGuard) // ketma-ketlik muhim — avval JWT, keyin Rol
export class UsersController {
@Get()
@Roles(Role.USER, Role.ADMIN) // user va admin ko'ra oladi
findAll() {
return "Foydalanuvchilar ro'yxati";
}
@Delete(":id")
@Roles(Role.ADMIN) // faqat admin o'chira oladi
remove(@Param("id") id: number) {
return `${id}-foydalanuvchi o\'chirildi`;
}
}
Guard lar ham Pipe lar kabi chapdan o'ngga ketma-ket ishlaydi. JwtAuthGuard avval ishlaydi — token tekshiriladi, request.user ga yoziladi. Keyin RolesGuard ishlaydi — request.user.role ni o'qiydi va tekshiradi.
Agar JwtAuthGuard false qaytarsa — RolesGuard umuman ishlamaydi. Bu to'g'ri xatti-harakat — avval kim ekanligini bilib ol, keyin nimaga ruxsati borligini tekshir.
@Public() decorator — ochiq endpointlar
Global Guard qo'yilganda login va ro'yxatdan o'tish endpointlari ham himoyalanib qoladi. Bu muammo. Yechim — @Public() decorator:
// common/decorators/public.decorator.ts
import { SetMetadata } from "@nestjs/common";
export const IS_PUBLIC_KEY = "isPublic";
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
Endi JwtAuthGuard ni yangilaymiz — @Public() bo'lsa o'tkazib yuboradi:
// common/guards/jwt-auth.guard.ts
import { IS_PUBLIC_KEY } from "../decorators/public.decorator";
@Injectable()
export class JwtAuthGuard implements CanActivate {
constructor(
private readonly jwtService: JwtService,
private readonly reflector: Reflector,
) {}
canActivate(context: ExecutionContext): boolean {
// Bu endpoint @Public() bilan belgilanganmi?
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
// Ha bo'lsa — tekshirmasdan o'tkazib yuboramiz
if (isPublic) {
return true;
}
// Qolgan logika avvalgidek...
const request = context.switchToHttp().getRequest();
const authHeader = request.headers["authorization"];
if (!authHeader) {
throw new UnauthorizedException("Token mavjud emas");
}
const token = authHeader.split(" ")[1];
try {
const payload = this.jwtService.verify(token);
request.user = payload;
return true;
} catch {
throw new UnauthorizedException("Token yaroqsiz yoki muddati o'tgan");
}
}
}
Auth controller da ishlatish:
// auth/auth.controller.ts
import { Controller, Post, Body } from "@nestjs/common";
import { Public } from "../common/decorators/public.decorator";
@Controller("auth")
export class AuthController {
@Post("login")
@Public() // bu endpoint ochiq — token shart emas
login(@Body() body: any) {
return "Login muvaffaqiyatli";
}
@Post("register")
@Public() // bu ham ochiq
register(@Body() body: any) {
return "Ro'yxatdan o'tish muvaffaqiyatli";
}
}
Xulosa
Guard — NestJS da ruxsat boshqaruvining asosi. Har bir so'rov Guard dan o'tadi va faqat ruxsati bor foydalanuvchilar kerakli endpointlarga kira oladi.
Eslab qoling:
CanActivate— Guard ning interfeysi,canActivatemetoditrue/falseqaytaradiExecutionContext— so'rov haqida hamma ma'lumot shu yerda@UseGuards()— Guard ni ulashSetMetadata— endpointga ma'lumot yopishtirib qo'yishReflector— o'sha ma'lumotni Guard ichida o'qish- Guard lar ketma-ket ishlaydi — avvalgisi
falseqaytarsa keyingisi ishlamaydi @Public()— global Guard dan istisno qilish uchun
Keyingi dars
10-dars: Interceptors — Response Mapping, Logging