权限控制必须前后端协同:后端注入结构化权限字段,前端仅据此通过data-permission统一显隐并同步处理required/tabindex/aria-hidden;同时后端接口必须每次请求都校验权限,缺一不可。

权限显隐必须基于后端返回的结构化字段,不能靠 DOM 或本地状态判断
前端做显隐只是视觉反馈,不是权限控制。硬编码 role === "admin"、读取 localStorage.getItem("role")、甚至用 URL 路径判断权限,全都不安全——用户改一行 JS 就能绕过。真实权限必须由后端在首次加载时注入,比如通过模板渲染:{{ user.permissions }},或 API 响应:{"permissions": ["user:read", "user:delete"]}。之后所有显隐逻辑都只查这个对象,不查 DOM、不查 URL、不查本地存储。
用 data-permission 统一标记受控元素,避免依赖位置索引
表格列、按钮、表单字段如果靠 nth-child(3) 或 children[2] 定位,一旦后端调整字段顺序或增删列,前端就失效。正确做法是统一打标:
<button data-permission="user:delete">删除</button><td data-permission="order:amount">¥12,000</td>
然后用 JS 批量处理:遍历所有带 data-permission 的元素,检查当前权限数组是否包含该值,不存在则加 .hidden 类(对应 display: none)。
表单字段联动显隐时,需同步控制 required、tabindex 和 aria-hidden
单纯设 style.display = "none" 会留下隐患:
- 隐藏的
<input required>在提交时仍被浏览器校验,导致表单卡住 - 键盘 Tab 仍能聚焦到隐藏字段,破坏可访问性
- 屏幕阅读器可能误读内容
正确做法是三者同步:
el.style.display = "none";
el.required = false;
el.tabIndex = -1;
el.setAttribute("aria-hidden", "true");
反向显示时也要全部还原。更稳妥的替代方案是用 <fieldset disabled></fieldset>,它天然禁用内部所有控件,且不参与表单提交。
显隐 ≠ 权限隔离,服务端接口鉴权不可省略
即使所有按钮、字段、菜单都按权限隐藏了,只要后端没对 POST /api/users/delete 做校验,攻击者仍能用 curl 直接调用成功。常见错误包括:
- 只校验 GET 接口,忽略 POST/PUT/DELETE
- 只校验路径,不校验请求体中携带的
target_id是否属于当前用户 - 用前端传来的
permission_flag=true当作判断依据
真正有效的鉴权必须每次请求都执行类似 if !has_permission(req.user, "user:delete") { return 403 } 的逻辑。权限控制最易被忽略的一点:显隐逻辑和接口鉴权是两套独立防线,缺一不可。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











