Mô hình tối thiểu là ba bảng: users, roles, permissions, cộng hai bảng nối user_roles và role_permissions. User nhận role, role gom permission dạng động từ + tài nguyên (order.read, order.refund, user.ban).
Quy tắc khi cài đặt:
- Kiểm tra ở server, mọi request. Ẩn nút trên UI chỉ là trải nghiệm, không phải bảo mật. Attacker gọi thẳng API.
- Kiểm tra theo permission, không theo tên role. Viết can(user, 'order.refund') thay vì if (user.role === 'admin'). Khi thêm role "kế toán" chỉ cần gán permission, không phải sửa rải rác trong code.
- Deny by default: endpoint không khai báo quyền thì bị chặn, chứ không phải mở.
- Đặt điểm kiểm tra ở một lớp duy nhất (middleware hoặc decorator trên route) để không ai quên.
router.post('/orders/:id/refund', requirePermission('order.refund'), refundHandler)Ghi quyền vào JWT thì tiện nhưng token sẽ cũ khi admin thu hồi quyền; hoặc để token sống ngắn, hoặc đọc quyền từ DB/cache mỗi request.
RBAC thuần chỉ trả lời "được làm hành động này không"; câu "được đụng vào bản ghi nào" cần thêm một lớp kiểm tra quyền sở hữu.