目录
- 一、概念
- 二、权限与访问控制的「能力全景图」
- 三、前端视角的「权限控制分层模型」(核心)
-
- 1、登录态层(Authentication State)
- 2、路由层(Page Access Control)
- 3、菜单层(Navigation Control)
- 4、组件 / 操作层(Action Control)
- 四、前端权限系统的“典型数据流”
- 五、权限模型的三种常见设计(前端必须懂)
-
- 1、RBAC(基于角色)
- 2、PBAC(基于权限点)
- 3、ABAC(基于属性/规则)
- 六、重点「权限与访问控制」业务剖析
-
- 1、用户鉴权
-
- (1)、用户鉴权概念
- (2)、用户鉴权的「标准模型」(企业级通用)
- (3)、主流鉴权方案全景对比(你必须会)
- (4)、JWT 鉴权机制(前端必须吃透)
-
- ①、JWT 本质
* ②、企业级 JWT 设计(重点)
- ①、JWT 本质
- (5)、前端如何“正确”做用户鉴权(分层讲)
-
- ①、登录阶段(Login)
* ②、凭证存储(非常关键)
* ③、请求鉴权(Request Authentication)
* ④、过期处理(企业级重点)
- ①、登录阶段(Login)
- (6)、一个「简单但完整」的企业级典例
-
- ①、登录流程
* ②、前端核心代码(示意)
* ③、后端核心原则(前端要懂)
- ①、登录流程
- (1)、用户鉴权概念
- 2、黑白名单
-
- (1)、黑白名单概念
- (2)、黑白名单在系统中的“正确位置”
- (3)、黑白名单的业务分类(前端必须理解)
-
- ①、按“对象”分类
* ②、按“作用方式”分类
- ①、按“对象”分类
- (4)、前端在黑白名单中的真实职责(重点)
- (5)、企业级黑白名单的“数据模型”(你要会看)
- (6)、前端如何“正确”使用黑白名单(分层讲)
-
- ①、初始化阶段(登录后)
* ②、菜单 / 页面层控制
* ③、操作前校验(体验级)
* ④、接口异常兜底(关键)
- ①、初始化阶段(登录后)
- (7)、一个「简单但企业级」的典例
-
- ①、后端规则(你要懂)
* ②、前端权限上下文(示意)
* ③、前端按钮控制
* ④、操作前提示
* ⑤、接口兜底处理
- ①、后端规则(你要懂)
- (1)、黑白名单概念
- 3、动态菜单
-
- (1)、动态菜单的概念
- (2)、动态菜单在系统中的“正确位置”
- (3)、为什么企业系统一定要用动态菜单?
- (4)、动态菜单的三种主流设计模式(前端必须懂)
-
- ①、后端全量下发菜单(最常见)
* ②、前端根据权限过滤菜单(次常见)
* ③、混合模式(企业级常见)
- ①、后端全量下发菜单(最常见)
- (5)、动态菜单的“标准数据模型”
- (6)、前端做动态菜单的“标准步骤”
-
- ①、登录后初始化权限上下文
* ②、根据权限生成菜单树
* ③、渲染菜单(UI 框架无关
* ④、路由层兜底(非常关键)
- ①、登录后初始化权限上下文
- (7)、一个「简单但企业级」的动态菜单典例
-
- ①、后端返回数据(示意)
* ②、前端生成菜单
* ③、路由守卫兜底
- ①、后端返回数据(示意)
- (1)、动态菜单的概念
一、概念
“用户权限与访问控制类业务”是指在用户身份已确认的前提下,通过角色、权限、规则和策略,决定用户:
- 能不能访问
- 能不能操作
- 能看到什么
“用户权限与访问控制”在系统中的位置(非常重要):
1用户身份认证 2(你是谁) 3 ↓ 4用户权限与访问控制 5(你能不能) 6 ↓ 7业务功能执行 8
权限系统是身份认证与业务执行之间的“闸门”。
“用户权限与访问控制类业务”在前端主要体现为:
- 登录态管理
- 路由访问控制
- 动态菜单
- 操作级权限控制。
前端负责:权限的展示与引导
- 前端权限系统的目标不是保证安全,而是降低误操作、减少越权尝试、提升用户体验。
后端负责:权限的裁决与执行
- 真正的安全控制必须由后端统一裁决,前后端形成权限的双层校验体系。
二、权限与访问控制的「能力全景图」
从前端视角,把这个业务域拆成 8 大核心能力:
| 能力 | 解决的问题 | 前端是否参与 |
|---|---|---|
| 身份状态管理 | 是否登录 | ✅ |
| 角色模型 | 用户是什么身份 | ✅ |
| 权限点模型 | 能做哪些操作 | ✅ |
| 路由访问控制 | 能进哪些页面 | ✅ |
| 动态菜单 | 能看到哪些入口 | ✅ |
| 按钮级权限 | 能点哪些操作 | ✅ |
| 黑白名单 | 是否强制禁止/放行 | ⚠️(展示) |
| 接口级校验 | 是否真正允许 | ❌(后端) |
📌 前端主要负责:入口、展示、引导、体验控制
三、前端视角的「权限控制分层模型」(核心)
前端权限不是一层,而是四层:
- 登录态层
- 路由层
- 菜单层
- 组件 / 操作层
1、登录态层(Authentication State)
前端职责:
- 判断是否已登录
- 维护 Token / Session
- 处理登录失效
典型实现:
- 路由守卫
- 请求拦截器
- Token 过期重定向
📌 这是所有权限的前提条件
2、路由层(Page Access Control)
问题:用户能否访问某个页面?
实现方式:
- 路由 meta.permission
- 登录后动态注册路由
- 未授权跳转 403
📌 防止“手输 URL 访问”
3、菜单层(Navigation Control)
问题:用户能看到哪些功能入口?
实现方式:
- 后端返回菜单树
- 或前端根据权限过滤菜单
- 菜单 ≠ 路由(重要)
📌 菜单是权限的 UI 映射
4、组件 / 操作层(Action Control)
问题:用户能不能执行具体操作?
实现方式:
- v-if / 权限指令
- hooks / 高阶组件
- 操作前二次校验
📌 按钮权限永远不能当安全机制
四、前端权限系统的“典型数据流”
1登录成功 2↓ 3获取用户信息(角色 / 权限 / 菜单 / 名单状态) 4↓ 5初始化权限上下文(Store) 6↓ 7动态生成路由 8↓ 9渲染菜单 10↓ 11控制组件 / 操作 12
📌 这是90% 企业级系统的真实流程
五、权限模型的三种常见设计(前端必须懂)
- 基于角色的 RBAC
- 基于权限点的 PBAC
- 基于属性/规则的 ABAC
1、RBAC(基于角色)
1用户 → 角色 → 权限 2
前端特点:
- 好理解
- 菜单生成简单
- 扩展性一般
2、PBAC(基于权限点)
1用户 → 权限点 2
前端特点:
- 按钮级控制友好
- 菜单/操作粒度细
- 前端维护成本较高
3、ABAC(基于属性/规则)
1用户属性 + 资源属性 + 环境属性 2
前端特点:
- 前端只做展示
- 复杂规则交给后端
- 常见于金融风控
📌 金融系统:RBAC + ABAC 混合
六、重点「权限与访问控制」业务剖析
1、用户鉴权
没有可靠的用户鉴权,后面的权限、菜单、风控全部没有意义。
用户鉴权是整个系统安全的第一道防线:
- 前端负责凭证的安全存储、携带和失效处理。
- 后端负责身份真实性校验和凭证签发。
在企业和金融系统中通常采用双 Token 机制以兼顾安全性和用户体验。
(1)、用户鉴权概念
用户鉴权,是用于确认“当前发起请求的主体是谁,并且是否真实可信”的过程。
关键词只有一个:
- 你是谁
鉴权 ≠ 权限(必须先区分):
| 项目 | 用户鉴权 | 用户权限 |
|---|---|---|
| 核心问题 | 你是谁 | 你能做什么 |
| 是否必须 | ✅ | ❌(视业务) |
| 是否安全核心 | ✅ | ⚠️ |
| 典型产物 | Token / Session | 菜单 / 按钮 / 规则 |
📌 鉴权是所有安全体系的地基
(2)、用户鉴权的「标准模型」(企业级通用)
抽象成 4 个核心步骤:
- 身份声明(Credentials)
- 身份验证(Verify)
- 身份凭证发放(Token / Session)
- 身份凭证校验(Request Auth)
各步骤的职责分工(前端视角):
| 阶段 | 前端职责 | 后端职责 |
|---|---|---|
| 登录 | 收集凭证 | 验证真实性 |
| 发证 | 存储凭证 | 生成 Token |
| 使用 | 携带 Token | 校验 Token |
| 失效 | 处理过期 | 判定失效 |
📌 前端 不验证身份真实性,只负责携带与管理凭证
(3)、主流鉴权方案全景对比(你必须会)
| 方案 | 是否企业常用 | 特点 |
|---|---|---|
| Cookie + Session | ✅ | 简单、CSRF 风险 |
| JWT(Bearer Token) | ✅ | 无状态、最主流 |
| OAuth2 / SSO | ✅ | 企业/平台级 |
| 双 Token(Access + Refresh) | ✅ | 金融级 |
| 证书 / Key | ⚠️ | 系统对系统 |
(4)、JWT 鉴权机制(前端必须吃透)
①、JWT 本质
1JWT = Header + Payload + Signature 2
- Payload:用户身份声明(userId / role / exp)
- Signature:防篡改
📌 JWT ≠ 加密,只是签名
②、企业级 JWT 设计(重点)
❌ 错误做法
- JWT 放 localStorage
- 永久有效
- 前端完全信任 JWT
✅ 正确做法(金融级)
- Access Token:短期(5~30min)
- Refresh Token:长期(7~30d)
- Refresh Token 存 HttpOnly Cookie
- Access Token 仅用于请求
(5)、前端如何“正确”做用户鉴权(分层讲)
①、登录阶段(Login)
前端:
- 收集账号 / 密码 / 验证码
- HTTPS 发送
后端:
- 验证身份
- 返回 Token
1{ 2 "accessToken": "xxx", 3 "expiresIn": 1800, 4 "user": { 5 "id": 1, 6 "name": "Alice" 7 } 8} 9
②、凭证存储(非常关键)
企业级推荐方案:
| 凭证 | 存储位置 |
|---|---|
| Access Token | 内存 / Store |
| Refresh Token | HttpOnly Cookie |
📌 防 XSS + 防 CSRF 的平衡方案
③、请求鉴权(Request Authentication)
前端统一拦截器:
1axios.interceptors.request.use(config => { 2 config.headers.Authorization = `Bearer ${accessToken}` 3 return config 4}) 5
④、过期处理(企业级重点)
1接口返回 401 2↓ 3使用 Refresh Token 4↓ 5刷新 Access Token 6↓ 7重放原请求 8
📌 用户无感刷新登录态
(6)、一个「简单但完整」的企业级典例
下面是一个典型后台管理系统的鉴权方案。
技术栈:
前端:React / Vue SPA
后端:REST API
鉴权:JWT + 双 Token
场景:金融后台
①、登录流程
1/login 2↓ 3校验账号密码 4↓ 5返回 accessToken 6↓ 7refreshToken 写入 HttpOnly Cookie 8
②、前端核心代码(示意)
登录成功处理:
1store.setAccessToken(token) 2store.setUser(userInfo) 3
请求拦截器:
1axios.interceptors.response.use( 2 res => res, 3 async err => { 4 if (err.response.status === 401) { 5 await refreshToken() 6 return axios(err.config) 7 } 8 return Promise.reject(err) 9 } 10) 11
路由守卫(鉴权入口):
1if (!accessToken && to.path !== '/login') { 2 redirect('/login') 3} 4
③、后端核心原则(前端要懂)
所有接口校验 Token:
- Token 校验独立于菜单 / 权限
- Token 失效立即拒绝请求
📌 鉴权是“所有接口”的第一道门
2、黑白名单
黑白名单并不属于权限模型,而是一种高优先级的访问控制与风控机制。
在系统中,它通常位于身份认证和权限校验之后,
前端仅负责展示与引导,最终是否允许访问由后端统一裁决。
权限决定“正常能不能做”,黑白名单决定“特殊情况下能不能做”。
(1)、黑白名单概念
黑白名单是一种“强制访问控制与风控兜底机制”,
用于在身份与权限校验通过之后,对特定主体进行“额外禁止或额外放行”。
📌 核心关键词:
强制 / 兜底 / 高优先级
黑白名单解决的不是“权限问题”:
| 问题 | 是否由黑白名单解决 |
|---|---|
| 你是谁 | ❌ |
| 你有什么权限 | ❌ |
| 是否强制禁止 | ✅ |
| 是否特权放行 | ✅ |
| 是否临时控制 | ✅ |
📌 黑白名单永远高于角色、权限、菜单
(2)、黑白名单在系统中的“正确位置”
标准安全顺序(金融系统):
- 身份认证(Authentication)
- 权限校验(Authorization)
- 黑白名单 / 风控校验(Access Control)
- 业务执行
黑白名单不是前置认证,而是“最终闸门”
(3)、黑白名单的业务分类(前端必须理解)
①、按“对象”分类
| 对象 | 示例 |
|---|---|
| 用户 | 封禁账号、风控用户 |
| 角色 | 禁止某类用户操作 |
| IP | 风控 IP |
| 设备 | 异常设备 |
| 接口 | 临时封禁能力 |
📌 前端通常只感知“用户级 / 功能级”
②、按“作用方式”分类
黑名单(Block):
- 强制禁止
- 无视权限
- 常用于风控
白名单(Allow):
- 强制放行
- 可绕过部分限制
- 常用于内部 / 灰度
(4)、前端在黑白名单中的真实职责(重点)
⚠️ 非常关键的一点:
- 前端不是黑白名单的裁决者,只是“感知者”和“执行者(展示层)”。
前端能做什么?
| 能力 | 是否允许 |
|---|---|
| 隐藏入口 | ✅ |
| 禁用按钮 | ✅ |
| 操作前提示 | ✅ |
| 阻断请求 | ⚠️(体验层) |
| 决定是否安全 | ❌ |
🚫 前端不能做什么?
- 决定是否真正允许
- 作为唯一拦截手段
- 依赖本地逻辑保证安全
(5)、企业级黑白名单的“数据模型”(你要会看)
后端返回的典型结构:
1{ 2 "userId": 1001, 3 "blacklist": { 4 "global": false, 5 "actions": ["withdraw", "transfer"], 6 "reason": "风控冻结" 7 }, 8 "whitelist": { 9 "actions": ["internal-debug"] 10 } 11} 12
📌 特点:
- 不是简单 true / false
- 有作用范围
- 有原因
- 可动态调整
(6)、前端如何“正确”使用黑白名单(分层讲)
①、初始化阶段(登录后)
1登录成功 2↓ 3获取用户画像(含名单状态) 4↓ 5写入权限上下文(Store) 6
②、菜单 / 页面层控制
1if (blacklist.actions.includes('withdraw')) { 2 hideMenu('withdraw') 3} 4
📌 只做展示控制
③、操作前校验(体验级)
1function beforeAction(action) { 2 if (blacklist.actions.includes(action)) { 3 showToast('当前账号已被限制操作') 4 return false 5 } 6 return true 7} 8
📌 防止无意义请求 + 明确提示用户
④、接口异常兜底(关键)
即使前端没拦住:
1接口请求 2↓ 3后端黑名单校验 4↓ 5返回 403 / 451 6↓ 7前端统一提示 8
📌 这是最终安全兜底
(7)、一个「简单但企业级」的典例
场景说明:金融后台系统
功能:提现
风控:部分用户被临时冻结提现
①、后端规则(你要懂)
1if (user in withdraw_blacklist) { 2 reject request 3} 4
②、前端权限上下文(示意)
1const authContext = { 2 permissions: ['withdraw'], 3 blacklist: ['withdraw'] 4} 5
③、前端按钮控制
1<Button 2 disabled={blacklist.includes('withdraw')} 3 onClick={handleWithdraw} 4> 5 提现 6</Button> 7
④、操作前提示
1function handleWithdraw() { 2 if (blacklist.includes('withdraw')) { 3 alert('账号风控中,暂不可提现') 4 return 5 } 6 submitWithdraw() 7} 8
⑤、接口兜底处理
1axios.post('/withdraw').catch(err => { 2 if (err.status === 403) { 3 alert('操作被系统拦截') 4 } 5}) 6
📌 前后端双层拦截
3、动态菜单
动态菜单本质上是权限与访问控制结果在前端导航层的映射,
它的目标是降低误操作风险、提升可用性与合规性,
但并不参与最终的安全裁决,真正的权限校验必须由后端完成。
动态菜单不是“你能不能做”,而是“你该不该看到”。
(1)、动态菜单的概念
动态菜单,是“用户权限与访问控制结果在前端的导航层映射”,用于决定“当前用户能看到哪些功能入口”。
📌 关键词:
权限结果 / 导航层 / 展示与引导
🚫 动态菜单不解决什么?
- 不解决身份认证
- 不解决真正的安全校验
- 不防止接口越权
📌 它不是安全机制,而是“权限的 UI 表现”
(2)、动态菜单在系统中的“正确位置”
在企业 / 金融系统中,完整链路是:
1身份认证(你是谁) 2↓ 3权限 / 黑白名单(你能不能) 4↓ 5动态菜单(你能看到什么) 6↓ 7路由访问 8↓ 9具体操作 10
菜单一定发生在“权限已确定之后”
(3)、为什么企业系统一定要用动态菜单?
业务复杂度决定的:
- 不同角色看到完全不同系统
- 功能频繁开关 / 灰度
- 多租户 / 多业务线
📌 静态菜单根本不可维护
金融系统的现实需求:
- 降低误操作风险
- 减少越权尝试
- 强化合规性(“你不该看到的就不要看到”)
(4)、动态菜单的三种主流设计模式(前端必须懂)
①、后端全量下发菜单(最常见)
1后端根据权限 2↓ 3直接返回可用菜单树 4↓ 5前端直接渲染 6
特点:
- 前端最简单
- 权限集中在后端
- 金融系统最常用
📌 推荐指数:⭐⭐⭐⭐⭐
②、前端根据权限过滤菜单(次常见)
1后端返回权限点 2↓ 3前端本地菜单表 4↓ 5根据权限过滤 6
特点:
- 前端更灵活
- 维护成本高
- 易出错
📌 适合中后台、内部系统
③、混合模式(企业级常见)
1后端返回菜单骨架 + 权限 2↓ 3前端补充结构 / 本地能力 4
📌 大型系统 / 微前端常用
(5)、动态菜单的“标准数据模型”
企业级菜单一定是树结构 + 元数据:
1[ 2 { 3 "key": "fund", 4 "title": "资金管理", 5 "icon": "money", 6 "path": "/fund", 7 "children": [ 8 { 9 "key": "withdraw", 10 "title": "提现", 11 "path": "/fund/withdraw", 12 "permission": "withdraw" 13 } 14 ] 15 } 16] 17
核心字段说明:
| 字段 | 作用 |
|---|---|
| key | 唯一标识 |
| title | 菜单名 |
| path | 路由路径 |
| permission | 所需权限 |
| children | 子菜单 |
📌 菜单 ≠ 路由,但通常一一对应
(6)、前端做动态菜单的“标准步骤”
①、登录后初始化权限上下文
1登录成功 2↓ 3获取用户信息(角色 / 权限 / 菜单) 4↓ 5存入 Store 6
②、根据权限生成菜单树
1function filterMenu(menu, permissions) { 2 return menu 3 .filter(item => 4 !item.permission || permissions.includes(item.permission) 5 ) 6 .map(item => ({ 7 ...item, 8 children: item.children 9 ? filterMenu(item.children, permissions) 10 : [] 11 })) 12} 13
📌 纯函数、可测试
③、渲染菜单(UI 框架无关
1<Menu> 2 {menu.map(item => ( 3 <MenuItem key={item.key} to={item.path}> 4 {item.title} 5 </MenuItem> 6 ))} 7</Menu> 8
④、路由层兜底(非常关键)
即使菜单隐藏了:
1用户手输 URL 2↓ 3路由守卫校验权限 4↓ 5无权限 → 403 6
📌 菜单永远不能当路由安全
(7)、一个「简单但企业级」的动态菜单典例
场景说明:金融后台
角色:普通用户 / 管理员
功能:查看账户 / 提现 / 审批
①、后端返回数据(示意)
1{ 2 "permissions": ["view", "withdraw"], 3 "menu": [ 4 { 5 "key": "account", 6 "title": "账户", 7 "path": "/account" 8 }, 9 { 10 "key": "withdraw", 11 "title": "提现", 12 "path": "/withdraw", 13 "permission": "withdraw" 14 }, 15 { 16 "key": "approve", 17 "title": "审批", 18 "path": "/approve", 19 "permission": "approve" 20 } 21 ] 22} 23
②、前端生成菜单
1const menu = filterMenu(menuFromServer, permissions) 2
结果只剩:
- 账户
- 提现
③、路由守卫兜底
1if (!hasPermission(route.permission)) { 2 redirect('/403') 3} 4
📌 企业级必备