[Góc Thực Chiến] Nhìn Lại RBAC Mức độ Architecture: Những "Vết Sẹo" và giới hạn sau nhiều năm nâng cấp hệ thống
Chào anh em. Nếu ở những năm đầu sự nghiệp, thiết kế được 5 cái bảng (Users, Roles, Permissions và 2 bảng mapping) để chạy mượt mà cái RBAC đã là một niềm tự hào, thì khi bạn đồng hành cùng một hệ thống từ lúc nó chỉ có vài nghìn user cho đến khi scale lên hàng triệu user, hàng chục phòng ban, góc nhìn của bạn sẽ thay đổi hoàn toàn.
Hôm nay, mình không nói lại khái niệm RBAC cơ bản nữa, Bài này dành để một xẻ những cái "bẫy" kiến trúc (Architecture Pitfalls) mà anh em kiểu gì cũng sẽ dẫm phải khi dự án bắt đầu phình to, và cách những người đi trước đã xử lý chúng.
1. Nỗi Đau Đầu Tiên: Sự Bùng Nổ Của "Role" (Role Explosion)
Giai đoạn đầu, bạn nghĩ chỉ cần 3 Roles: Admin, Editor, Editor là đủ .
Nhưng khi công ty to lên, nghiệp vụ phức tạp dần. Sếp yêu cầu: "Tạo cho anh một role vừa có quyền Editor, vừa được xem báo cáo tài chính của User, nhưng không được xóa".
Thế là bạn đẻ ra một role tên là Editor_FinanceViewer. Vài tháng sau, danh sách Roles của bạn biến thành một bãi rác khổng lồ: Marketing_Intern, Sale_Leader_HCM, Sale_Member_HN... Lên tới hàng trăm Roles. Đến lúc này, việc quản lý "Role nòa đang có Permission gì" trở thành một cơn ác mộng.
Kinh nghiệm xử lý:
- Áp dụng Hierarchical RBAC (Role Kế Thừa): Giống như lập trình hướng đối tượng, Role cũng có thể kế thừa nhau. Role Senior_Editor sẽ tự động kế thừa mọi Permission của Junior_Editor, cộng thêm vài quyền nâng cao. Nó giúp bạn giảm thiểu việc cấu hình lặp lại.
- Role Grouping: Nhóm các Role lại theo phòng ban (Departments).
2. Giới Hạn Chí Mạng Của RBAC: Bài Toán "Dữ Liệu Của Ai?"
Đây là cú lừa đau nhất mà RBAC truyền thống không giải được.
Giả sử bạn có quyền (Permission) là edit_post. RBAC kiểm tra: "Anh A có quyền edit_post không? Có. Cho qua!".
NHƯNG: Anh A là tác giả của bài viết số 1, anh B là tác giả của bài viết số 2. Anh A dùng Postman bắn API sửa bài viết số 2 của anh B. Theo logic của RBAC, anh A vẫn qua cửa vì hệ thống chỉ biết anh A có quyền edit_post, chứ nó không biết khái niệm "Chỉ được sửa bài của chính mình".
Kinh nghiệm xử lý:
Khi dự án đạt đến độ phức tạp này, RBAC không còn đủ. Chúng ta phải nâng cấp lên hoặc kết hợp với ABAC (Attribute-Based Access Control) - Phân quyền theo thuộc tính hoặc Policy-Based Access Control.
Lúc này, Permission không chỉ là một chuỗi string edit_post nữa, mà là một tập hợp các Policy (Chính sách):
- User có quyền
edit_postNẾUpost.author_id == user.id" - "User có quyền
approve_documentNẾUdocument.amount < 5000$"
3. Vấn Đề Revocation (Thu Hồi Quyền) Khẩn Cấp & Bẫy Caching JWT
Để tối ưu hiệu năng, anh em thường nhét thẳng danh sách Permissions vào trong JWT Payload hoặc lưu trên Redis.
Chuyện gì xảy ra nếu nhân viên C vừa bị đuổi việc, và Admin lập tức xoá quyền của nhân viên này trong Database? Nếu Token của nhân viên C có hạn (expiration) là 24 giờ, thì trong 24 giờ đó, hệ thống (do không query DB mà chỉ đọc Token) vẫn ngây thơ nghĩ rằng nhân viên C còn nguyên quyền hạn. Cậu ta hoàn toàn có thể vào phá nát database.
Kinh nghiệm xử lý:
-
Thiết kế Token vòng đời ngắn (Short-lived Access Token): Access Token chỉ nên sống 5-15 phút. Khi hết hạn, hệ thống bắt buộc phải dùng Refresh Token để lấy Token mới, lúc này Backend sẽ có cơ hội check lại DB xem user có bị tước quyền không.
-
Cơ chế Blacklist / Revoked Token Cache: Bất cứ khi nào Admin thay đổi Role/Permission của một User, Backend PHẢI đẩy một cờ (flag)
permissions_changed_atvào Redis. Các request sau đó dù Token còn hạn nhưng nếutoken.issued_at < permissions_changed_atthì bắt buộc phải từ chối (Force Logout) hoặc cấp lại token mới.
4. Bỏ Quên Lịch Sử (Audit Logging) - Ai Cấp Quyền Cho Ai?
Hệ thống của bạn một ngày đẹp trời bị rò rỉ dữ liệu. Bạn phát hiện một tài khoản Dev nội bộ tự nhiên có quyền Super Admin. Bạn mở DB ra xem, thấy bản ghi rõ rành rành, nhưng... ai là người đã gán cái Role đó? Và gán từ lúc nào? DB 5 bảng của bạn không hề lưu trữ điều này.
**Kinh nghiệm xử lý: **
Với các hệ thống yêu cầu bảo mật cao (như Fintech, Y tế, hoặc doanh nghiệp lớn), phân quyền KHÔNG THỂ thiếu Audit Trail.
Bảng user_roles và role_permissions luôn phải có các cột: granted_by (ai cấp), granted_at (cấp lúc nào), reason (lý do - nếu cần). Hoặc an toàn hơn, mọi thao tác đổi quyền đều phải ghi log vào một hệ thống trung tâm riêng biệt không thể xoá.
Tổng Kết Lại
RBAC rất tuyệt vời để làm nền móng. Nhưng hệ thống cũng giống như một cơ thể sống, nó lớn lên và đòi hỏi những chiếc áo rộng hơn. Hiểu được ranh giới giữa RBAC thuần túy và lúc nào cần đưa thêm Rule/Policy (ABAC) vào, lúc nào cần tối ưu Cache & Audit... chính là ranh giới giữa một Junior và một người thiết kế hệ thống vững tay.
All rights reserved