权限不只是"能不能点"
做后台权限时最常见的错误,是把它简化成"这个角色能看到哪些菜单"。等业务跑起来就会发现真正的问题是:
- 运营能看到全部订单,区域经理只能看到自己负责的供应商;
- 财务能导出结算单,客服只能看不能导;
- 部门调整后,历史数据的可见范围要不要跟着变。
第一个问题属于功能权限,第二、三个属于数据权限。两者必须分开建模,否则会出现"给了菜单就等于给了全量数据"的越权。
RBACM:五张关系表
模型的名字取功能区(Module)、角色(Role)、账号(Account)、权限项(Permission)的首字母,核心是五组关系:
| 关系 | 作用 | 落点 |
|---|---|---|
| 账号-角色 | 一个人可以身兼多职 | 多对多 |
| 角色-菜单 | 决定左侧导航显示什么 | 多对多 |
| 角色-权限 | 决定能调用哪些按钮级接口 | 多对多,细到按钮 |
| 角色-模块 | 决定这个角色"有没有买这个能力" | 默认拒绝 |
| 角色-部门 | 决定数据可见范围 | 多对多,带范围类型 |
关键设计是菜单与权限项分离:菜单负责"看得见",权限项负责"做得了"。一个页面可以出现,但导出按钮按权限码隐藏;接口侧再校验一次权限码,前端隐藏只是体验,后端校验才是安全边界。
另一条是模块默认拒绝:新增模块时,所有既有角色默认没有权限,必须显式授权。这条规则让"上线新功能后所有租户突然都多了一个入口"的情况不会发生。
数据范围:四种可见口径
数据权限不落在角色上,而是落在"角色-部门"这一层,每个绑定带一个范围类型:
- 全部数据;
- 本部门;
- 本部门及下级;
- 仅本人。
业务表怎么知道自己属于谁?答案是归属维度统一收口到档案表:供应商档案与门店档案上带部门与负责人两列,业务表(商品、订单、结算单、库存流水等)通过业务号反查档案得到归属。查询时按档案归属过滤,而不是在每张业务表上再冗余一份部门。
这样做的好处是:调整某个供应商的归属部门,只需改一行档案,所有关联业务数据的可见范围立即生效,不需要批量刷历史数据。
写操作也要校验:详情、编辑、作废这些入口统一走一次行级校验,越权时返回明确的错误码而不是"查不到"——前者便于排查,后者会让运营以为数据丢了。
后台工程骨架的三条约定
后台是 Vue3 + Vite + Element Plus 单页应用,工程上有三条约定值得说明:
- hash 路由。部署在静态托管或 Apache 子目录时,history 路由刷新会 404。改用 hash 后,刷新、分享链接、直接访问二级页面都不会挂——这是私有化部署场景必须做的妥协。
- 接口基址与跨域白名单。前台与接口不同域部署时,后端只放行配置里的精确白名单,不使用通配;预检请求做限流,避免被当成压测入口。
- 枚举字典化。订单状态、结算状态这类枚举不在代码里硬编码中文,统一从字典接口取,前端只认 code。新增状态不必改前端代码,也不必担心"两处文案不一致"。
一个反复被验证的原则
权限设计的目标不是"看起来灵活",而是默认安全:
- 新模块默认拒绝;
- 新菜单默认不显示;
- 数据范围默认最小;
- 前端隐藏 + 后端校验双保险。
后台权限一旦做歪,后面每加一个页面都要补一次漏洞。宁可前期多花两天把模型理清,也不要后期靠人肉 review 兜底。