一、问题背景:权限系统做得好,项目少一半 bug
广告平台的典型权限场景:
- 多角色(广告主、运营、审核员、管理员)
- 页面权限 + 按钮权限 + 接口权限 + 数据权限
- 权限频繁变更,要求灵活配置 + 实时生效
初期项目权限系统写得“够用”,后期越补越乱。
我在重构阶段主导设计了一套可配置、可插拔、可回溯的权限机制。
二、权限系统的核心结构设计
抽象三类权限:
interface PermissionData {
menus: string[]; // 页面级权限
actions: string[]; // 操作级(按钮、接口)
scopes: string[]; // 数据作用域权限
}
后台返回如下结构:
{
"menus": ["/ad-plan", "/material"],
"actions": ["ad:create", "ad:delete"],
"scopes": ["city:guangzhou"]
}
三、路由动态挂载逻辑
权限匹配 + 动态注册:
const allRoutes = [...]
const registerRoutesByPermission = (menus: string[]) => {
const filtered = allRoutes.filter(r => menus.includes(r.path))
filtered.forEach(r => router.addRoute(r))
}
四、按钮级权限控制(两种方式)
方法1:自定义指令 v-permission
app.directive('permission', {
mounted(el, binding) {
const actions = userStore.actions
if (!actions.includes(binding.value)) {
el.parentNode?.removeChild(el)
}
}
})
使用方式:
<n-button v-permission="'ad:delete'">删除</n-button>
方法2:封装组件 PermissionButton
<PermissionButton code="ad:create">新增计划</PermissionButton>
五、数据权限(作用域)逻辑
接口调用时注入作用域参数:
const getAdList = () => {
return request.get('/api/ads', {
params: {
...filters,
city: userStore.scope.city
}
})
}
六、权限失效后的动态刷新
权限变化后,触发:
await userStore.fetchPermissions()
resetRoutes()
registerRoutesByPermission(userStore.menus)
七、权限配置管理策略
权限码统一维护在一处,导出使用:
export const PermissionCodes = {
CREATE_AD: 'ad:create',
DELETE_AD: 'ad:delete',
VIEW_REPORT: 'report:view'
}
方便查找、配置、后端同步维护。
八、扩展能力设计
- 插件式注入权限判断函数
export const hasPermission = (code: string) => {
return userStore.actions.includes(code)
}
- 对外暴露 API 判断逻辑,供组件自由调用
if (hasPermission('report:view')) showReportCard()
九、总结:权限系统的三个关键词
- 灵活:菜单、组件、接口都能动态控制
- 统一:所有权限点集中管理,命名规范
- 可组合:与 router、组件、接口天然融合
权限系统是大前端工程架构中最容易“沦为脆弱拼图”的部分,但一旦设计清晰,就能极大提升系统安全性与可维护性。
这是我在广告平台架构工作中的关键模块之一。