摘要
在現代 Web 應用中,權限控制已經不再是“後端的事”。隨着前後端分離、單頁應用(SPA)流行,前端權限控制逐漸成為用户體驗和系統安全的雙重關鍵。如果只靠後端控制,前端體驗太差;如果只靠前端控制,那就等於裸奔。怎麼權衡?怎麼落地?這就是本文要探討的重點。
引言
你是否遇到過:不同用户登錄後看到的菜單不同、某些按鈕灰了點不了、訪問一些頁面會自動跳轉 403 頁面?這都來自於“前端權限控制”的精細化設計。
現在的權限控制越來越細粒度,除了“角色”之外,還有“操作級”控制。比如同一個頁面裏,有的人能“查看”,有的人能“新增”,還有人只能“導出”。
這一套看似簡單,實則涉及:路由控制、組件控制、權限管理、後端驗證、緩存同步等多個環節。我們接下來就一步步來拆解。
前端權限控制系統設計思路
用户登錄後獲取權限信息
後端返回用户的角色、權限碼等信息,前端登錄成功後將其緩存(如 Vuex、Pinia、localStorage 等)。
示例數據結構
// 登錄成功後後端返回的數據結構
const user = {
username: 'zsfan',
role: 'admin',
permissions: ['user:add', 'user:edit', 'dashboard:view']
};
根據權限控制:路由 + 菜單 + 按鈕
我們可以將權限碼掛載到路由元信息(meta)上,動態控制菜單顯示與頁面訪問權限。
權限路由守衞:不該進的頁面別讓進
動態路由 + 路由守衞實現控制
使用 Vue Router 的 beforeEach 方法做守衞,結合權限碼判斷是否有訪問權限。
示例代碼
// 路由配置中添加權限元信息
{
path: '/user/add',
component: () => import('@/views/UserAdd.vue'),
meta: { permission: 'user:add' }
}
// 路由守衞控制訪問
router.beforeEach((to, from, next) => {
const permissions = getUserPermissions(); // 從緩存或 Vuex 獲取權限列表
const required = to.meta.permission;
if (required && !permissions.includes(required)) {
next('/403'); // 無權限跳轉403
} else {
next();
}
});
控制按鈕和組件顯示:不該點的按鈕也隱藏掉
v-if + 權限判斷方法
讓按鈕或控件只在有權限時才顯示。
示例代碼
<!-- 只有有 user:add 權限才顯示 -->
<button v-if="hasPermission('user:add')">新增用户</button>
function hasPermission(code) {
const permissions = getUserPermissions(); // 獲取權限
return permissions.includes(code);
}
這樣做的好處是,不同用户登錄看到的按鈕完全不同,體驗非常清爽。
後端權限驗證:別信前端,核心操作必須後台校驗
前端權限只是“演戲”,真正的數據操作必須後端判斷權限
Node.js 示例代碼
app.post('/api/user/add', (req, res) => {
const user = req.user; // 從 token 中解析的用户信息
if (!user.permissions.includes('user:add')) {
return res.status(403).json({ message: '你沒有新增用户的權限' });
}
// 有權限,正常處理
res.json({ message: '新增成功' });
});
典型場景實戰
場景一:菜單根據權限動態渲染
// 動態生成菜單
const allMenus = [
{ name: '用户管理', path: '/user', permission: 'user:view' },
{ name: '添加用户', path: '/user/add', permission: 'user:add' }
];
const userMenus = allMenus.filter(menu =>
user.permissions.includes(menu.permission)
);
場景二:按鈕級權限控制
有些功能你不希望每個員工都能用,比如“導出數據”、“重置密碼”——用權限碼控制按鈕顯示。
<button v-if="hasPermission('user:reset')">重置密碼</button>
<button v-if="hasPermission('user:export')">導出數據</button>
場景三:前端組件封裝權限指令(Vue自定義指令)
// 自定義權限指令 v-permission
app.directive('permission', {
mounted(el, binding) {
const permissions = getUserPermissions();
if (!permissions.includes(binding.value)) {
el.parentNode && el.parentNode.removeChild(el);
}
}
});
<!-- 使用 -->
<button v-permission="'user:edit'">編輯</button>
QA 問答環節
Q1:前端控制是不是多餘,反正後端也會攔?
不是。前端權限主要是提升用户體驗,讓用户不去點那些不能點的東西;後端才是真正的防線,負責攔截非法訪問。
Q2:權限碼應該放在哪管理?
建議所有權限碼統一定義和管理,比如:
// permission-codes.js
export const PERMISSIONS = {
USER_ADD: 'user:add',
USER_EDIT: 'user:edit',
DASHBOARD_VIEW: 'dashboard:view'
};
這樣方便維護,防止拼寫錯誤。
Q3:權限緩存會不會被篡改?
可以結合 JWT 簽名 + 本地緩存控制,或者通過加密緩存,但要明白:前端緩存不能作為權限依據,只能作為顯示依據。
總結
前端權限控制,説簡單也簡單,説複雜也能無限擴展。它的核心原則是:
- 前端只控制“界面展示”,不能控制“數據和行為”
- 權限要後端返回、前端解析
- 所有權限判斷必須同步做“後端驗證”
一個好的權限系統,不僅是安全保障,更是用户體驗的加分項。別再只靠“按鈕v-if”了,從系統架構層去考慮權限管理,才是真正的開發者思維。