Class 08: Authorization – Phân quyền
Authorization (phân quyền) là quá trình xác định xem người dùng đã xác thực có quyền truy cập tài nguyên, thực hiện hành động hay không. Trong nhiều ứng dụng, chúng ta cần phân biệt quyền của user dựa trên vai trò (role) khác nhau.
Gán vai trò (role) cho user
Vai trò (role) là gì?
Role là nhóm quyền hoặc cấp độ phân quyền gán cho user. Ví dụ:
admin
: có quyền toàn bộ hệ thốnguser
: quyền giới hạn, chỉ truy cập chức năng cơ bảnmoderator
: quyền trung gian...
Gán role cho user trong database
Thông thường, trường role
sẽ được thêm vào entity User
. Ví dụ:
// user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
username: string;
@Column()
password: string;
@Column()
role: string; // Ví dụ: 'admin', 'user', 'moderator'
}
Khi tạo user, bạn gán role phù hợp:
const user = this.userRepository.create({
username: 'khoi',
password: hashedPassword,
role: 'admin',
});
await this.userRepository.save(user);
Tạo Guard kiểm tra role
Guard trong NestJS
Guard là lớp dùng để kiểm soát truy cập route, quyết định cho phép hoặc chặn dựa trên logic tùy chỉnh.
Guard phân quyền theo role
Ta sẽ viết một Guard để kiểm tra role của user từ request đã được xác thực (thông thường user info được lấy từ request.user
sau khi xác thực JWT).
// roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
// Lấy danh sách role được phép truy cập từ metadata @Roles
const roles = this.reflector.get<string[]>('roles', context.getHandler());
if (!roles) {
return true; // Nếu không có metadata roles thì cho phép truy cập
}
const request = context.switchToHttp().getRequest();
const user = request.user;
// Kiểm tra user có role trong danh sách roles được phép hay không
return user && roles.includes(user.role);
}
}
Custom Decorator cho @Roles
Metadata trong NestJS
Chúng ta dùng Reflect.metadata
để gán metadata cho các route handler, dùng Reflector
lấy ra trong Guard.
Tạo decorator @Roles
@Roles
// roles.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
Sử dụng RolesGuard và @Roles trong Controller
import { Controller, Get, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { RolesGuard } from './roles.guard';
import { Roles } from './roles.decorator';
@Controller('admin')
@UseGuards(JwtAuthGuard, RolesGuard)
export class AdminController {
@Get('dashboard')
@Roles('admin')
getAdminDashboard() {
return { message: 'Chỉ admin mới có thể xem được trang này' };
}
@Get('profile')
@Roles('admin', 'moderator')
getAdminOrModProfile() {
return { message: 'Admin hoặc moderator mới truy cập được' };
}
}
Ở ví dụ trên:
JwtAuthGuard
sẽ kiểm tra xác thực JWT và thêm thông tin user vào request.RolesGuard
sẽ kiểm tra vai trò user với decorator@Roles
.
Ví dụ đầy đủ về phần auth có role
// auth.service.ts
async validateUser(username: string, pass: string): Promise<any> {
const user = await this.userRepository.findOne({ where: { username } });
if (user && (await bcrypt.compare(pass, user.password))) {
const { password, ...result } = user;
return result;
}
return null;
}
Giả sử result
trả về có { id, username, role }
thì trong JwtStrategy
ta trả về thông tin này làm payload token:
async validate(payload: any) {
return { userId: payload.sub, username: payload.username, role: payload.role };
}
Khi user gửi request kèm token, NestJS sẽ gán request.user
có đủ trường role
để RolesGuard
dùng.
Bài tập thực hành
Thêm trường
role
trong entity User (vd: 'admin', 'user', 'moderator').Viết custom decorator
@Roles
để gán role cho route.Viết
RolesGuard
để kiểm tra quyền user truy cập.Bảo vệ route
/admin/dashboard
chỉ cho phépadmin
truy cập.Bảo vệ route
/admin/profile
cho phépadmin
vàmoderator
truy cập.Test thử với các user có role khác nhau qua Postman hoặc curl.
(Optional) Tạo route
/user/profile
cho tất cả user đã đăng nhập đều truy cập được.
Tổng kết
Phân quyền là bước tiếp theo sau xác thực, giúp giới hạn truy cập theo vai trò người dùng
Custom decorator và Guard giúp xây dựng hệ thống phân quyền gọn, tái sử dụng cao
Thực hiện phân quyền dễ dàng với NestJS nhờ
Reflector
và metadataCần kết hợp tốt giữa xác thực (Auth) và phân quyền (Authorization)
Last updated