前端路由攔截的核心是通過在路由跳轉前后進行權限判斷,決定用戶是否能訪問特定頁面。常見實現方式包括:1. 全局路由守衛(如 vue 的 beforeeach 或 react 的 usenavigate 鉤子),集中管理權限并控制跳轉;2. 組件級別權限控制(如 react 的 hoc),靈活控制單個組件的訪問權限;3. 服務端渲染(ssr)時在服務器校驗權限,提高安全性但增加復雜度;4. 使用路由配置中的 meta 字段存儲權限信息,在全局守衛中統一校驗;5. 動態路由,根據用戶權限動態生成路由表以實現精細化控制。spa 中推薦使用全局路由守衛和組件級控制相結合的方式,并注意處理異步權限驗證及避免重定向死循環問題。
前端路由攔截,簡單來說,就是控制用戶能否訪問特定頁面。實現方式多種多樣,核心在于在路由跳轉前后進行判斷,決定是否允許跳轉。
解決方案
前端路由攔截的本質是在路由跳轉前后進行權限判斷。常見的實現方案包括:
立即學習“前端免費學習筆記(深入)”;
-
全局路由守衛 (vue router / React Router): 這是最常用的方法。在路由跳轉前,通過 beforeEach (Vue) 或 useNavigate (React) 鉤子,檢查用戶權限。如果權限不足,可以重定向到登錄頁或其他授權頁面。這種方式集中管理路由權限,方便維護。
- Vue Router 示例:
router.beforeEach((to, from, next) => { const requiresAuth = to.matched.some(record => record.meta.requiresAuth); const isLoggedIn = localStorage.getItem('token'); // 假設使用 token 驗證 if (requiresAuth && !isLoggedIn) { next('/login'); // 重定向到登錄頁 } else { next(); // 允許跳轉 } });
- React Router 示例 (使用 useNavigate 鉤子):
import { useNavigate, useLocation } from 'react-router-dom'; import { useEffect } from 'react'; function AuthGuard({ children, requiredRoles }) { const navigate = useNavigate(); const location = useLocation(); const isLoggedIn = localStorage.getItem('token'); // 假設使用 token 驗證 const userRole = localStorage.getItem('role'); // 假設存儲用戶角色 useEffect(() => { if (!isLoggedIn) { navigate('/login', { replace: true, state: { from: location } }); // 保存當前 location,登錄后跳轉回來 } else if (requiredRoles && !requiredRoles.includes(userRole)) { navigate('/unauthorized'); // 重定向到無權限頁面 } }, [isLoggedIn, navigate, location, requiredRoles, userRole]); return children; } export default AuthGuard;
-
組件級別權限控制 (Higher-Order Components – HOC): 通過高階組件包裹需要權限控制的組件,在組件渲染前進行權限判斷。這種方式更靈活,可以針對單個組件進行權限控制。
// React 示例 function withAuth(WrappedComponent, requiredRole) { return function(props) { const isLoggedIn = localStorage.getItem('token'); const userRole = localStorage.getItem('role'); if (!isLoggedIn || (requiredRole && userRole !== requiredRole)) { return <div>您沒有權限訪問此頁面</div>; // 可以重定向到其他頁面 } return <WrappedComponent {...props} />; }; } // 使用 const AdminDashboard = withAuth(Dashboard, 'admin'); // 只有 admin 角色才能訪問
-
服務端渲染 (SSR) 時的權限控制: 在服務端進行權限判斷,只有擁有權限的用戶才能獲取到頁面內容。這種方式安全性更高,但實現復雜度也更高。
- 例如,在 Next.JS 中,可以在 getServerSideProps 函數中進行權限校驗。
export async function getServerSideProps(context) { const { req, res } = context; const token = req.cookies.token; // 從 cookie 中獲取 token if (!token) { return { redirect: { destination: '/login', permanent: false, }, }; } // 校驗 token,獲取用戶信息 try { const user = await verifyToken(token); // 假設 verifyToken 是一個校驗 token 的函數 return { props: { user }, }; } catch (error) { return { redirect: { destination: '/login', permanent: false, }, }; } } function MyPage({ user }) { // ... } export default MyPage;
-
路由配置中的 Meta 字段: 在路由配置中添加 meta 字段,用于存儲權限信息。然后在全局路由守衛中讀取 meta 字段,進行權限判斷。這種方式可以更清晰地管理路由權限。
// Vue Router 示例 const routes = [ { path: '/admin', component: AdminDashboard, meta: { requiresAuth: true, role: 'admin' }, }, // ... ]; router.beforeEach((to, from, next) => { if (to.meta.requiresAuth) { const isLoggedIn = localStorage.getItem('token'); const userRole = localStorage.getItem('role'); if (!isLoggedIn) { next('/login'); } else if (to.meta.role && userRole !== to.meta.role) { next('/unauthorized'); } else { next(); } } else { next(); } });
-
動態路由: 根據用戶的權限動態生成路由表。這種方式可以更精細地控制用戶的訪問權限,但實現復雜度較高。
- 例如,在用戶登錄后,從服務器獲取用戶擁有的權限列表,然后根據權限列表動態生成路由表。
單頁面應用(SPA)路由攔截的最佳實踐是什么?
SPA 的路由攔截核心在于客戶端,因此全局路由守衛和組件級別權限控制是常用的選擇。全局路由守衛集中管理,易于維護;組件級別權限控制更靈活,適合復雜場景。選擇哪種方式取決于項目的具體需求和復雜度。SSR 也是一種選擇,但會增加項目的復雜度。
如何處理異步權限驗證?
權限驗證往往需要從服務器獲取用戶信息,這是一個異步過程。在全局路由守衛中,可以使用 async/await 或 promise 來處理異步權限驗證。
// Vue Router 示例 router.beforeEach(async (to, from, next) => { if (to.meta.requiresAuth) { const isLoggedIn = localStorage.getItem('token'); if (!isLoggedIn) { next('/login'); return; } try { // 假設 getUserInfo 是一個異步函數,用于從服務器獲取用戶信息 const user = await getUserInfo(); // 將用戶信息存儲到 Vuex 或其他狀態管理工具中 store.commit('setUser', user); next(); } catch (error) { // 處理錯誤,例如 token 過期 localStorage.removeItem('token'); next('/login'); } } else { next(); } });
如何避免路由攔截的死循環?
路由攔截的死循環通常發生在重定向到登錄頁時,登錄頁又需要登錄才能訪問。為了避免這種情況,可以在路由守衛中判斷當前是否已經位于登錄頁,如果是,則直接放行。
// Vue Router 示例 router.beforeEach((to, from, next) => { if (to.path === '/login') { next(); // 如果已經在登錄頁,則直接放行 return; } if (to.meta.requiresAuth) { const isLoggedIn = localStorage.getItem('token'); if (!isLoggedIn) { next('/login'); } else { next(); } } else { next(); } });