8 phút

Menu điều hướng nhận biết quyền: trực quan nhưng vẫn thực thi kiểm soát truy cập

Menu điều hướng nhận biết quyền giúp tăng tính rõ ràng, nhưng bảo mật phải được thực thi ở backend. Xem các mẫu đơn giản cho vai trò, chính sách và cách ẩn UI an toàn.

Menu điều hướng nhận biết quyền: trực quan nhưng vẫn thực thi kiểm soát truy cập

Vấn đề mà menu nhận biết quyền thực sự giải quyết

Khi người ta nói “ẩn nút”, họ thường có một trong hai ý: giảm rối cho người dùng không dùng được tính năng, hoặc ngăn lạm dụng. Chỉ mục đầu tiên là điều có thể làm được ở frontend.

Menu điều hướng nhận biết quyền chủ yếu là công cụ UX. Chúng giúp ai đó mở app và ngay lập tức thấy họ có thể làm gì, thay vì chạm phải màn hình “Truy cập bị từ chối” sau mỗi vài lần nhấp. Chúng cũng giảm chi phí hỗ trợ bằng cách tránh các câu hỏi như “Tôi phê duyệt hoá đơn ở đâu?” hoặc “Tại sao trang này bị lỗi?”.

Ẩn giao diện không phải là bảo mật. Đó là sự rõ ràng.

Ngay cả một đồng nghiệp tò mò vẫn có thể:

  • Gõ một deep link đến trang ẩn nếu họ biết URL
  • Mở bookmark cũ đến màn hình admin
  • Gọi API của bạn trực tiếp bằng script hoặc công cụ
  • Chỉnh sửa request từ trình duyệt và thử các ID khác nhau

Vì vậy vấn đề thật sự mà menu nhận biết quyền giải quyết là hướng dẫn trung thực. Chúng giữ giao diện phù hợp với công việc, vai trò và ngữ cảnh của người dùng, đồng thời làm rõ khi nào một thứ gì đó không khả dụng.

Một trạng thái tốt sẽ trông như sau:

  • Người dùng hầu hết chỉ thấy các hành động hợp lý với họ và hiếm khi gặp lỗi bất ngờ.
  • Developer có một nguồn chân lý duy nhất cho quy tắc truy cập, thay vì kiểm tra rải rác.
  • Thay đổi sản phẩm an toàn hơn vì một mục menu mới buộc bạn quyết định quyền cần có.
  • Bảo mật được thực thi nơi quan trọng: backend từ chối hành động bị cấm mọi lúc, ngay cả khi UI vô tình hiển thị chúng.

Ví dụ: trong một CRM nhỏ, một Sales Rep nên thấy Leads và Tasks, nhưng không thấy User Management. Nếu họ dán URL quản lý người dùng thì trang phải fail-closed, và server vẫn chặn mọi cố gắng liệt kê người dùng hoặc thay đổi vai trò.

Hiển thị không phải là phân quyền

Hiển thị là những gì giao diện chọn hiển thị. Phân quyền là những gì hệ thống thực sự cho phép khi một request đến server.

Menu nhận biết quyền giảm bớt sự nhầm lẫn. Nếu ai đó sẽ không bao giờ được phép thấy Billing hoặc Admin, ẩn những mục đó giữ app gọn và giảm ticket hỗ trợ. Nhưng ẩn nút không phải là khoá. Người ta vẫn có thể thử endpoint bên dưới bằng dev tools, bookmark cũ hoặc request sao chép.

Một quy tắc thực tế: quyết định trải nghiệm bạn muốn, rồi thực thi quy tắc đó ở backend dù UI làm gì.

Khi bạn quyết định cách trình bày một hành động, ba mẫu sau bao phủ hầu hết trường hợp:

  • Ẩn khi tính năng nên không bị khám phá với hầu hết vai trò (ví dụ công cụ nội bộ).
  • Vô hiệu hoá khi tính năng tồn tại nhưng người dùng không thể dùng ngay (ví dụ Export bị vô hiệu cho tới khi nâng gói, cho tới khi chọn bản ghi, hoặc khi dữ liệu đang tải).
  • Hiển thị kèm giải thích khi người dùng cần hiểu tại sao họ không thể tiếp tục (“Bạn có thể xem hoá đơn, nhưng chỉ Owners mới sửa phương thức thanh toán”).

“Bạn có thể xem nhưng không sửa” là tình huống phổ biến và đáng thiết kế rõ ràng. Xử lý nó như hai permission: một cho đọc dữ liệu và một cho thay đổi. Trong menu, bạn có thể hiển thị Chi tiết Khách hàng cho mọi người có quyền đọc, nhưng chỉ hiển thị Chỉnh sửa khách hàng cho những ai có quyền ghi. Trên trang, render các trường ở chế độ chỉ đọc và khoá các điều khiển chỉnh sửa, trong khi vẫn cho phép trang tải.

Quan trọng nhất, backend quyết định kết quả cuối cùng. Ngay cả khi UI ẩn mọi hành động admin, server vẫn cần kiểm tra permission trên mọi request nhạy cảm và trả về phản hồi “không được phép” rõ ràng khi ai đó thử.

Chọn mô hình permission mà bạn có thể duy trì

Cách nhanh nhất để triển khai menu nhận biết quyền là bắt đầu với một mô hình mà đội bạn có thể giải thích trong một câu. Nếu bạn không giải thích được, bạn sẽ không giữ được nó đúng.

Dùng roles để gom nhóm, không dùng roles để đặt nghĩa. Admin và Support là thùng hữu ích. Nhưng khi roles bắt đầu nhân lên (Admin-West-Coast-ReadOnly), UI trở thành mê cung và backend trở nên đoán mò.

Ưu tiên permissions làm nguồn chân lý cho những gì ai đó có thể làm. Giữ chúng nhỏ và theo hành động, như invoice.create hoặc customer.export. Điều này mở rộng tốt hơn so với role sprawl vì tính năng mới thường thêm hành động mới, không phải vị trí công việc mới.

Rồi thêm policies (luật) cho ngữ cảnh. Ở đây bạn xử lý “chỉ sửa record của chính bạn” hoặc “chỉ duyệt hoá đơn dưới $5,000”. Policies tránh việc bạn tạo hàng chục permission gần giống nhau chỉ khác bởi điều kiện.

Một lớp dễ duy trì trông như sau:

  • Roles gom nhóm permissions (chức năng công việc)
  • Permissions mô tả hành động (cái gì)
  • Policies thêm ngữ cảnh (khi nào, bản ghi nào)

Đặt tên quan trọng hơn bạn nghĩ. Nếu UI nói Export Customers nhưng API dùng download_all_clients_v2, cuối cùng bạn sẽ ẩn sai thứ hoặc chặn nhầm thứ. Giữ tên thân thiện, nhất quán và dùng chung giữa frontend và backend:

  • Dùng noun.verb (hoặc resource.action) nhất quán
  • Khớp nhãn UI với ý định permission, không phải tên mã nội bộ
  • Giữ tên permission cũ hoạt động khi bạn đổi tên tính năng

Ví dụ: trong CRM, role Sales có thể gồm lead.createlead.update, nhưng một policy giới hạn update chỉ với lead mà người dùng sở hữu. Điều đó giữ menu rõ ràng trong khi backend vẫn nghiêm ngặt.

Từng bước: triển khai roles và permissions end-to-end

Menu nhận biết quyền đem lại cảm giác tốt vì giảm rối và tránh nhấp nhầm. Nhưng chúng chỉ hữu ích khi backend vẫn nắm quyền. Hãy coi UI như gợi ý, và server là thẩm phán.

Bắt đầu bằng việc ghi ra bạn đang bảo vệ gì. Không phải trang, mà là hành động. Xem danh sách khách hàng khác với export khách hàng và xóa khách hàng. Đây là xương sống của menu nhận biết quyền mà không biến thành kịch bản an ninh giả.

Công thức thực tế end-to-end

  1. Lập inventory hành động trên UI và API. Với mỗi tính năng, liệt kê các thao tác cụ thể: read, create, update, delete, export, invite, change billing, chạy báo cáo admin.
  2. Định nghĩa permissions trên server làm nguồn chân lý. Lưu mapping role-to-permission trong database (hoặc config), và bắt mọi handler API hỏi “người này có permission X trên resource Y không?”
  3. Trả một payload capabilities nhỏ cho UI. Sau login (hoặc khi refresh session), trả boolean như canEditCustomers, canDeleteCustomers, canExport, hoặc một danh sách gọn các chuỗi permission. Giữ nó tối giản.
  4. Thực thi kiểm tra trên mọi write, cộng các read nhạy cảm. Mọi mutation phải kiểm tra permission. Một số read cũng nên (ví dụ lương, audit logs, exports, hoặc bất kỳ endpoint nào bỏ qua lọc bình thường).
  5. Thêm vài test tập trung vào roles. Chọn 2-3 role chính và test các hành động rủi ro cao nhất. Ví dụ: Sales có thể tạo deals nhưng không export customers, và Admin có thể invite users.

Một quy tắc nhỏ nhưng quan trọng: đừng bao giờ tin flag role hoặc permission do client gửi lên. UI có thể ẩn nút dựa trên capabilities, nhưng API vẫn phải từ chối các request không được phép.

Các mẫu frontend giữ tính trung thực

Biến học được thành credits
Chia sẻ những gì bạn xây với Koder.ai và đổi lấy credits cho dự án tiếp theo.

Menu nhận biết quyền nên giúp người dùng tìm ra những gì họ có thể làm, không giả vờ thi hành bảo mật. Frontend là thanh ray hướng dẫn. Backend là khoá.

Xây menu từ một nguồn chân lý duy nhất

Thay vì rải kiểm tra permission khắp các nút, định nghĩa navigation từ một config duy nhất chứa permission yêu cầu cho mỗi mục, rồi render từ config đó. Điều này giữ quy tắc dễ đọc và tránh kiểm tra bị quên ở những ngóc ngách UI.

Một mẫu đơn giản như sau:

const menu = [
  { label: "Contacts", path: "/contacts", requires: "contacts.read" },
  { label: "Export", action: "contacts.export", requires: "contacts.export" },
  { label: "Admin", path: "/admin", requires: "admin.access" },
];

const visibleMenu = menu.filter(item => userPerms.includes(item.requires));

Ưu tiên ẩn cả phần lớn (ví dụ Admin) thay vì rải các kiểm tra trên từng link trang admin. Ít nơi để sai hơn.

Trạng thái trung thực: ẩn vs vô hiệu hoá

Ẩn mục khi người dùng sẽ không bao giờ được phép dùng chúng. Vô hiệu hoá khi người dùng có quyền, nhưng ngữ cảnh hiện tại thiếu.

Ví dụ: Xoá contact nên bị vô hiệu hoá cho tới khi một contact được chọn. Cùng permission, chỉ là thiếu ngữ cảnh. Khi vô hiệu hoá, thêm một thông tin ngắn “tại sao” gần control (tooltip, helper text hoặc note inline): Chọn contact để xóa.

Một bộ quy tắc bền:

  • Ẩn khi người dùng thiếu permission.
  • Vô hiệu hoá khi người dùng có permission nhưng chưa chọn mục, form không hợp lệ, hoặc dữ liệu còn tải.
  • Không bao giờ coi local storage là sự thật cho permissions. Nó chỉ là cache.
  • Kiểm tra lại permissions sau login, change role, hoặc đổi account.

Thực thi phía backend không thể bị né tránh

Ẩn menu giúp người dùng tập trung, nhưng không bảo vệ gì cả. Backend phải là thẩm phán cuối cùng vì request có thể được replay, sửa, hoặc kích hoạt ngoài UI.

Một quy tắc tốt: mỗi hành động thay đổi dữ liệu cần một kiểm tra authorization duy nhất, ở một chỗ, mà mọi request phải đi qua. Có thể là middleware, wrapper handler, hoặc một lớp policy nhỏ bạn gọi ở đầu mỗi endpoint. Chọn một cách và tuân theo, nếu không bạn sẽ bỏ sót đường đi.

Đặt kiểm tra trên đường đi request

Tách authorization ra khỏi validation input. Trước hết quyết định “người này có được phép làm việc này không?”, rồi mới validate payload. Nếu bạn validate trước, có thể lộ thông tin (ví dụ ID bản ghi tồn tại) cho người không nên biết hành động đó.

Một mẫu mở rộng:

  • Xác thực caller và dựng danh tính rõ ràng (user id, org id, roles, permissions).
  • Load ngữ cảnh resource cần cho authorization (thường chỉ owner id hoặc org id).
  • Gọi một hàm policy duy nhất (ví dụ: Can(user, "invoice.delete", invoice)).
  • Nếu bị từ chối, dừng ngay và trả đúng status.
  • Rồi mới validate và thực hiện logic nghiệp vụ.

Trả về phản hồi rõ ràng, nhất quán

Dùng status code giúp frontend và logs:

  • 401 Unauthorized khi caller chưa đăng nhập.
  • 403 Forbidden khi đã đăng nhập nhưng không được phép.

Cẩn thận với 404 Not Found như mánh che giấu. Nó hữu ích để không tiết lộ resource tồn tại, nhưng nếu dùng lung tung thì debug sẽ khó. Chọn quy tắc nhất quán cho từng loại resource.

Đảm bảo cùng authorization chạy dù hành động đến từ click nút, app mobile, script, hay gọi API trực tiếp.

Cuối cùng, log các lần bị từ chối để debug và audit, nhưng giữ logs an toàn. Ghi ai, hành động gì, loại resource ở mức cao. Tránh các trường nhạy cảm, payload đầy đủ hoặc bí mật.

Các edge case quyết định thành bại của thiết kế

Hầu hết lỗi permission xuất hiện khi người dùng đi theo đường mà menu không dự đoán. Đó là lý do menu nhận biết quyền có ích, nhưng chỉ khi bạn thiết kế cho cả các đường vượt qua menu.

Nếu menu ẩn Billing cho một role, người dùng vẫn có thể dán URL lưu sẵn hoặc mở từ lịch sử trình duyệt. Xử lý mọi lần load trang như một request mới: lấy permissions hiện tại của user, và chính màn hình phải từ chối load dữ liệu bảo vệ khi thiếu permission. Một thông báo thân thiện “Bạn không có quyền truy cập” là ổn, nhưng bảo vệ thực sự là backend trả về không có dữ liệu.

Gọi API trực tiếp và các endpoint bulk

Bất kỳ ai cũng có thể gọi API của bạn từ dev tools, script, hoặc client khác. Vì vậy kiểm tra permissions trên mọi endpoint, không chỉ các trang admin. Rủi ro dễ bỏ sót là bulk actions: một /items/bulk-update có thể vô tình cho non-admin thay đổi các field họ chưa bao giờ thấy trong UI.

Roles cũng có thể thay đổi giữa phiên. Nếu admin bỏ quyền, user có thể vẫn giữ token cũ hoặc menu cache cũ. Dùng token ngắn hạn hoặc lookup permission ở server, và xử lý 401/403 bằng cách refresh permissions và cập nhật UI.

Thiết bị chung tạo một bẫy khác: cache menu có thể rò rỉ giữa các account. Lưu visibility menu theo key user ID, hoặc tránh lưu vĩnh viễn.

Năm bài test đáng chạy trước khi release:

  • Mở deep link đến trang ẩn khi đang đăng nhập với role hạn chế
  • Gọi API được bảo vệ bằng request sao chép từ dev tools
  • Thay đổi role người dùng khi họ đang hoạt động, rồi thử lại các hành động
  • Đăng xuất, đăng nhập bằng user khác, và kiểm tra menu được reset
  • Thử endpoints bulk với các trường được phép và không được phép trộn lẫn

Ví dụ minh hoạ: menu CRM đơn giản với thực thi thực tế

Xuất bản trạng thái UI trung thực
Tạo endpoint capabilities và nối UI của bạn với nó bằng Koder.ai.

Hãy tưởng tượng một CRM nội bộ với ba role: Sales, Support và Admin. Mọi người đăng nhập và app hiển thị menu bên trái, nhưng menu chỉ là tiện nghi. An toàn thực sự là những gì server cho phép.

Đây là một bộ permission đơn giản và dễ đọc:

  • Leads: view, create, edit, delete, export
  • Tickets: view, create, edit, assign
  • Billing: view, edit
  • Users: view, manage

UI bắt đầu bằng việc hỏi backend về hành động mà user hiện tại được phép (thường là danh sách chuỗi permission) kèm ngữ cảnh cơ bản như user id và team. Menu được xây từ đó. Nếu bạn không có billing.view, bạn không thấy Billing. Nếu bạn có leads.export, bạn thấy nút Export trên màn hình Leads. Nếu bạn chỉ có thể edit lead của mình, nút Edit vẫn có thể xuất hiện nhưng nên bị vô hiệu hoá hoặc hiện thông báo rõ khi lead không thuộc sở hữu bạn.

Bây giờ phần quan trọng: mọi endpoint hành động đều thực thi cùng luật.

Ví dụ: Sales có thể tạo leads và sửa leads họ sở hữu. Support có thể xem tickets và gán tickets, nhưng không đụng vào billing. Admin có thể quản lý users và billing.

Khi ai đó cố xóa lead, backend kiểm tra:

  1. Người dùng có leads.delete không?
  2. Nếu quy tắc là chỉ-own, có đúng là lead.owner_id == user.id không?
  3. Nếu lead bị khoá (ví dụ đã được lập hoá đơn), có bị cấm xoá cho mọi người trừ Admin không?

Ngay cả khi một Support user gọi thủ công endpoint delete, họ sẽ nhận forbidden. Mục menu ẩn chưa bao giờ là biện pháp bảo vệ. Quyết định của backend mới là.

Sai lầm phổ biến và bẫy

Cái bẫy lớn nhất với menu nhận biết quyền là nghĩ rằng công việc hoàn tất khi menu đã nhìn đúng. Ẩn nút giảm nhầm lẫn, nhưng không giảm rủi ro.

Những lỗi thường gặp nhất:

  • “Không hiển thị” thành “không thể thực hiện.” Ai đó vẫn có thể gọi API trực tiếp, dùng URL cũ, hoặc kích hoạt hành động qua dev tools. Hãy coi UI hiding là tiện nghi, không phải cửa.
  • Permissions chỉ được kiểm tra ở UI. Nếu frontend quyết định ai có thể xoá record, bạn đã thua. Backend phải là thẩm phán cuối cùng cho mọi hành động được bảo vệ.
  • Một flag isAdmin cho mọi thứ. Ban đầu thấy nhanh, rồi lan rộng. Sớm hay muộn mọi ngoại lệ đều thành case đặc biệt và không ai giải thích nổi quy tắc truy cập.
  • Tin client nói role. Không chấp nhận role, isAdmin, hoặc permissions từ browser là sự thật. Dẫn xuất danh tính và quyền từ session hoặc token của bạn, rồi lookup server-side.
  • Quên kiểm tra read. Teams tập trung vào write, nhưng rò rỉ dữ liệu thường đến từ read. Liệt kê khách hàng, xem hoá đơn, export CSV, và tìm kiếm thường cần kiểm tra.

Ví dụ cụ thể: bạn ẩn menu Export leads cho non-managers. Nếu endpoint export không kiểm tra permission, bất kỳ user nào đoán được request (hoặc sao chép từ đồng nghiệp) vẫn có thể tải file.

Checklist trước khi ra mắt

Nguyên mẫu RBAC trong vài phút
Xây dựng ứng dụng nhận biết quyền trong Koder.ai từ một prompt chat và lặp nhanh an toàn.

Trước khi phát hành menu nhận biết quyền, làm một lần rà soát cuối cùng tập trung vào những gì người dùng thực sự có thể làm, không chỉ những gì họ thấy.

Đi qua app theo từng role chính và thử cùng một tập hành động. Làm cả bằng UI và bằng cách gọi endpoint trực tiếp (hoặc dev tools) để chắc server là nguồn chân lý.

Checklist:

  • Với mọi hành động được bảo vệ, xác nhận có kiểm tra authorization phía server tại điểm thực thi (không chỉ khi build menu).
  • Để UI render hành động từ capabilities do server cung cấp (ví dụ danh sách can-list trong session hoặc một endpoint permissions) thay vì suy đoán từ tên role.
  • Đảm bảo UI xử lý forbidden responses bình thường: hiển thị thông báo rõ ràng, giữ trang ổn định, tránh để người dùng ở trạng thái hỏng.
  • Test thay đổi role và permission. Khi admin cập nhật truy cập, nó nên có hiệu lực nhanh và dễ đoán (refresh token, invalidate session, hoặc versioning permission).
  • Chạy vài test giá trị cao bao phủ các role và hành động liên quan tiền bạc, export, xóa, và cài đặt admin.

Một cách thực tế để phát hiện lỗ hổng: chọn một nút “nguy hiểm” (xóa user, export CSV, thay billing) và trace end-to-end. Mục menu nên ẩn khi phù hợp, API nên từ chối các cuộc gọi trái phép, và UI nên hồi phục mượt khi nhận 403.

Bước tiếp theo: phát hành an toàn mà không làm chậm tiến độ

Bắt đầu nhỏ. Bạn không cần ma trận truy cập hoàn hảo ngay ngày đầu. Chọn vài hành động quan trọng (view, create, edit, delete, export, manage users), map chúng vào các role hiện có, và tiếp tục. Khi tính năng mới xuất hiện, chỉ thêm các hành động mới nó mang lại.

Trước khi xây màn hình, làm một pass lập kế hoạch nhanh liệt kê hành động, không phải trang. Một mục menu như Invoices che giấu nhiều hành động: view list, view details, create, refund, export. Viết chúng ra trước giúp UI và backend rõ ràng hơn, và tránh sai lầm phổ biến là chặn cả trang trong khi để endpoint rủi ro chưa được bảo vệ.

Khi refactor quy tắc truy cập, đối xử nó như thay đổi rủi ro: giữ lưới an toàn. Snapshot cho phép so sánh hành vi trước và sau. Nếu một role mất truy cập cần thiết, hoặc được cấp truy cập không đúng, rollback nhanh hơn sửa nóng trên production khi người dùng bị khoá.

Quy trình phát hành đơn giản giúp đội di chuyển nhanh mà không đoán mò:

  • Viết danh sách hành động cho tính năng và đặt tên mỗi permission.
  • Thêm kiểm tra backend trước, rồi nối visibility UI vào cùng permissions đó.
  • Test một role được phép và một role bị từ chối cho mỗi hành động rủi ro.
  • Log các lần bị từ chối (không chứa dữ liệu nhạy cảm) để phát hiện lỗ hổng.
  • Chụp snapshot trước rollout để rollback nhanh nếu cần.

Nếu bạn xây trên nền tảng chat như Koder.ai (koder.ai), cấu trúc này vẫn áp dụng: định nghĩa permissions và policies một lần, UI đọc capabilities từ server, và kiểm tra backend là bắt buộc trong mọi handler.

Câu hỏi thường gặp

Permission-aware menus thực sự giải quyết điều gì?

Permission-aware menus chủ yếu giải quyết sự rõ ràng, không phải bảo mật. Chúng giúp người dùng tập trung vào những gì họ thực sự có thể làm, giảm các lần nhấp đi vào ngõ cụt, và bớt các câu hỏi hỗ trợ kiểu “tại sao tôi thấy cái này?”.

Bảo mật vẫn phải được thực thi ở backend, vì bất kỳ ai cũng có thể thử deep link, bookmark cũ hoặc gọi API trực tiếp bất kể UI hiển thị ra sao.

Khi nào tôi nên ẩn mục menu thay vì vô hiệu hoá nó?

Ẩn khi một tính năng nên hoàn toàn không được khám phá với một vai trò, và sẽ không có con đường hợp lệ để họ dùng nó.

Vô hiệu hoá khi người dùng có thể có quyền nhưng thiếu ngữ cảnh hiện tại, ví dụ chưa chọn bản ghi, form không hợp lệ, hoặc dữ liệu đang tải. Nếu vô hiệu hoá, thêm giải thích ngắn để người dùng không nghĩ là bị lỗi.

Tại sao “ẩn nút” không phải là bảo mật?

Bởi vì hiển thị không đồng nghĩa với phân quyền. Người dùng có thể dán URL, dùng bookmark admin cũ, hoặc gọi API ngoài UI.

Hãy coi UI như hướng dẫn. Hãy coi backend là nơi quyết định cuối cùng cho mọi yêu cầu nhạy cảm.

Frontend nên biết người dùng được phép làm gì bằng cách nào?

Server của bạn nên trả về một phản hồi “capabilities” nhỏ sau khi đăng nhập hoặc refresh session, dựa trên kiểm tra phân quyền ở server. UI sau đó render menu và nút từ dữ liệu đó.

Không tin vào flag do client cung cấp như isAdmin từ trình duyệt; hãy tính toán quyền từ danh tính đã xác thực trên server.

Cách đơn giản nhất end-to-end để triển khai quyền là gì?

Bắt đầu bằng việc lập danh sách các hành động, không phải trang. Với mỗi tính năng, tách rõ: read, create, update, delete, export, invite, thay đổi billing.

Sau đó bắt buộc mỗi hành động ở handler backend (hoặc middleware/wrapper) trước khi làm việc gì khác. Nối menu với cùng tên quyền để UI và API đồng bộ.

Nên dùng role hay permission làm mô hình chính?

Mặc định thực tế: roles là thùng chứa, permissions là nguồn chân lý. Giữ permissions nhỏ và theo hành động (ví dụ invoice.create), và gán chúng vào roles.

Nếu roles bắt đầu nhân lên để mã hoá điều kiện (ví dụ theo vùng hay chỉ đọc), hãy chuyển điều kiện đó vào chính sách thay vì tạo vô số variant role.

Làm thế nào xử lý trường hợp “có thể xem nhưng không thể sửa” và các truy cập có điều kiện khác?

Dùng chính sách cho các quy tắc có điều kiện như “chỉ sửa record của mình” hoặc “chỉ duyệt hoá đơn dưới một ngưỡng”. Điều này giữ danh sách permission ổn định nhưng vẫn diễn đạt được các ràng buộc thực tế.

Backend nên đánh giá chính sách dựa trên ngữ cảnh resource (như owner ID, org ID), không dựa trên giả định từ UI.

Có thực sự cần kiểm tra quyền cho các endpoint đọc (read) không?

Không phải lúc nào cũng cần. Những read trả về dữ liệu nhạy cảm hoặc bỏ qua lọc bình thường cũng nên bị kiểm soát, ví dụ export, audit logs, dữ liệu lương, danh sách user admin, hoặc bất kỳ endpoint trả nhiều dữ liệu hơn UI thường hiển thị.

Quy tắc tốt: mọi write phải được kiểm tra, và các read nhạy cảm cũng cần kiểm tra.

Làm sao tránh lỗ hổng bảo mật với bulk actions và các endpoint “quyền mạnh”?

Endpoints bulk dễ bị quên vì có thể thay đổi nhiều record hoặc field trong một request. Người dùng có thể bị chặn ở UI nhưng vẫn gọi /items/bulk-update trực tiếp.

Kiểm tra permission cho hành động bulk và kiểm tra thêm những field được phép thay đổi cho vai trò đó, nếu không bạn có thể vô tình cho phép chỉnh các field vốn bị ẩn.

Các quyền thay đổi giữa phiên thì xử lý thế nào, hoặc khi cache UI lỗi thời?

Giả sử quyền có thể thay đổi khi người dùng đang đăng nhập. Khi API trả 401 hoặc 403, UI nên xử lý như trạng thái bình thường: refresh capabilities, cập nhật menu, và hiển thị thông báo rõ ràng.

Cũng tránh lưu trạng thái menu theo cách có thể rò rỉ giữa các tài khoản trên thiết bị chia sẻ; nếu cache, hãy key theo user ID hoặc không lưu vĩnh viễn.

Related posts