Đặt một lớp guard giữa router và page, và luôn nhớ đây chỉ là lớp trải nghiệm — quyền thật do server quyết định.
Mô hình thường dùng: session chứa danh sách permissions, mỗi route khai báo quyền nó cần, guard so khớp.
jsx
function RequirePermission({ need, children }) {
const { status, permissions } = useSession()
if (status === 'loading') return <FullPageSpinner />
if (status === 'unauthenticated') return <Navigate to="/login" replace state={{ from: location }} />
if (!permissions.includes(need)) return <Navigate to="/403" replace />
return children
}Ba điểm hay bị hỏi thêm:
- Tránh nháy nội dung: khi trạng thái auth còn
loadingthì render placeholder, đừng render page rồi mới redirect. - Nhớ đường đích: lưu
fromđể sau khi đăng nhập quay lại đúng trang, dùngreplaceđể nút Back không rơi lại trang bị chặn. - Ẩn nút khác với chặn thật: ẩn menu chỉ làm gọn giao diện. Người dùng vẫn có thể gõ thẳng URL hoặc gọi API bằng công cụ khác, nên mỗi endpoint phải tự kiểm tra quyền.
Với app có nhiều vai trò, tách một hàm can(action, resource) dùng chung cho cả guard lẫn việc ẩn/hiện nút để logic quyền chỉ nằm một chỗ.