Quản lý phân quyền Go với Casbin: Từ RBAC đến ABAC cho hệ thống thực tế

Development tutorial - IT technology blog
Development tutorial - IT technology blog

Cơn ác mộng mang tên “Spaghetti Authorization”

Dự án Go đầu tay của mình từng suýt đổ bể chỉ vì đống if-else phân quyền. Lúc đầu, mọi thứ rất đơn giản: chỉ cần check nếu user là admin thì cho phép sửa bài. Thế nhưng, khi hệ thống chạm mốc 10.000 user, khách hàng bắt đầu đưa ra những yêu cầu “khó nhằn”.

Họ muốn thêm role Editor, Moderator, rồi đến cả Guest. Oái oăm nhất là những điều kiện như: “Editor chỉ được sửa bài của chính mình” hoặc “Moderator chỉ được xóa comment từ 8h sáng đến 5h chiều”. Chỉ sau 2 tuần, code của mình ngập ngụa trong logic check quyền. Mỗi lần thay đổi một rule nhỏ, mình lại phải lục lọi hàng chục file, sửa code rồi build lại toàn bộ. Đó chính là cái bẫy “Hardcoded Authorization” mà nhiều dev hay mắc phải.

Tại sao code phân quyền tự chế thường hụt hơi khi scale?

Sau khi tốn cả tuần để refactor, mình rút ra 3 lý do khiến hệ thống tự xây dựng nhanh chóng trở nên quá tải:

  • Logic bị dính chặt (Tight Coupling): Phân quyền nằm rải rác trong business logic. Muốn đổi cấu trúc role, bạn phải sửa code ở khắp nơi.
  • Khó triển khai ABAC: RBAC (phân quyền theo vai trò) thì dễ. Nhưng khi đụng tới ABAC (dựa trên thuộc tính – ví dụ: chỉ chủ bài viết mới có quyền sửa), đống if-else sẽ biến thành một mê cung thực sự.
  • Rủi ro bảo mật: Mỗi thành viên trong team lại check quyền một kiểu khác nhau. Chỉ cần một phút lơ là, bạn sẽ để lại lỗ hổng cho hacker khai thác.

Đi tìm giải pháp tối ưu

Mình đã cân nhắc 3 phương án trước khi chọn được “chân ái”:

  1. Tự xây bảng DB: Tạo các bảng roles, permissions. Cách này tốn công bảo trì và cực khó khi cần xử lý kế thừa quyền (Role Hierarchy).
  2. Middleware đơn giản: Chỉ chặn được ở mức router, không giải quyết được bài toán phân quyền chi tiết (fine-grained) đến từng resource.
  3. Sử dụng Casbin: Thư viện chuyên biệt hỗ trợ mô hình PERM (Policy, Effect, Request, Matchers). Đây là cách tiếp cận chuyên nghiệp nhất.

Casbin – Tách biệt luật chơi khỏi dữ liệu

Sau 6 tháng chạy Casbin trên production cho một sàn thương mại điện tử với hơn 50 API endpoints, mình thấy đây là quyết định đúng đắn. Điểm ăn tiền nhất của Casbin là nó tách biệt Cơ chế (Mechanism) khỏi Chính sách (Policy).

Mô hình PERM: Xương sống của Casbin

Bạn chỉ cần định nghĩa file model.conf một lần duy nhất. Nó quy định cách hệ thống vận hành thông qua 4 thành phần:

  • Request (r): Ai (sub) đang làm gì (act) trên tài nguyên nào (obj)?
  • Policy (p): Danh sách các quy tắc cho phép.
  • Matchers (m): Công thức để khớp Request với Policy.
  • Effect (e): Quyết định cuối cùng (ví dụ: chỉ cần một quy tắc khớp là cho qua).

Triển khai thực tế với Go

Cài đặt thư viện cực kỳ đơn giản:

go get github.com/casbin/casbin/v2

Dưới đây là file model.conf cho hệ thống RBAC có hỗ trợ kế thừa quyền:

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow))

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

Áp dụng vào code Go để kiểm tra quyền truy cập:

package main

import (
	"fmt"
	"github.com/casbin/casbin/v2"
)

func main() {
	e, _ := casbin.NewEnforcer("model.conf", "policy.csv")

	sub := "alice" // Người dùng
	obj := "data1" // Tài nguyên
	act := "read"  // Hành động

	ok, err := e.Enforce(sub, obj, act)

	if err != nil {
		fmt.Println("Lỗi hệ thống")
	} else if ok {
		fmt.Println("Truy cập thành công!")
	} else {
		fmt.Println("Bạn không có quyền này!")
	}
}

Khi làm việc với các cấu trúc policy phức tạp, mình thường dùng toolcraft.app để format JSON hoặc test nhanh dữ liệu. Nó giúp mình tránh được việc cài đặt quá nhiều extension gây nặng VS Code.

Nâng cấp lên ABAC: Quyền năng thực sự

Nếu muốn quy định: “Chủ bài viết mới được sửa”, bạn không cần sửa code Go. Chỉ cần cập nhật Matcher trong file cấu hình:

[matchers]
m = r.sub == r.obj.Owner || r.sub == "admin"

Khả năng tùy biến này giúp bạn xử lý mọi logic “dị” nhất mà khách hàng yêu cầu mà không làm bẩn code nghiệp vụ.

Lưu ý xương máu khi dùng Casbin

Sau nửa năm “chinh chiến”, mình rút ra vài kinh nghiệm thực tế:

  • Nói không với CSV trên Production: Hãy dùng Gorm Adapter để lưu policy vào PostgreSQL hoặc MySQL. File CSV chỉ phù hợp để test nhanh hoặc làm demo.
  • Caching là sống còn: Check quyền diễn ra ở mọi request. Khi policy lên tới hàng nghìn dòng, hãy dùng Redis để cache kết quả. Tốc độ phản hồi nên dưới 1ms.
  • Đừng tiết kiệm comment: Hãy chú thích kỹ file model.conf. Tin mình đi, 3 tháng sau quay lại bạn sẽ không hiểu đống Matcher đó viết gì nếu thiếu comment đâu.

Lời kết

Casbin không phải là lời giải cho mọi bài toán, nhưng nó là công cụ mạnh mẽ nhất để xử lý phân quyền trong Go. Nó giúp tách biệt hoàn toàn logic bảo mật, giúp team dev và vận hành hiểu nhau hơn mà không cần đọc code. Nếu dự án của bạn bắt đầu phức tạp, hãy thử Casbin ngay hôm nay.

Share: