您现在的位置是:亿华云 > 知识
权限想要细化到按钮,怎么做?
亿华云2025-10-04 20:04:48【知识】0人已围观
简介因为写了不少 Spring Security 文章的缘故,所以总是有小伙伴来问松哥:按钮级别的权限怎么实现?甚至有一些看过 vhr 的小伙伴也问这种问题,其实有的时候搞得我确实挺郁闷的,最近刚好要做
因为写了不少 Spring Security 文章的怎么做缘故,所以总是权限有小伙伴来问松哥:按钮级别的权限怎么实现?甚至有一些看过 vhr 的小伙伴也问这种问题,其实有的想细时候搞得我确实挺郁闷的,最近刚好要做 TienChin 项目,按钮我就再把这个问题拎出来和小伙伴们仔细捋一捋。怎么做
1. 权限颗粒度首先小伙伴们都知道权限有不同的权限颗粒度,在 vhr 项目中,想细整体上我是按钮基于请求地址去处理权限的,这个粒度算粗还是怎么做算细呢?
有的小伙伴们可能认为这个权限粒度太粗,所谓细粒度的权限权限应该是基于按钮的。
如果有小伙伴们做过前后端不分的想细开发,应该会有这样的按钮体会:在 Shiro 或者 Spring Security 框架中,都提供了一些标签,怎么做通过这些标签可以做到在满足某种角色或者权限的权限情况下,显示某个按钮;当用户不具备某种角色或者权限的想细时候,按钮则会自动隐藏起来。服务器租用
但是大家想想,按钮的显示与隐藏不过是前端页面为了提高用户体验而作出的样式的变化而已,本质上,当你点击一个按钮的时候,还是发送了一个 HTTP 请求,那么服务端处理该请求的接口,必须要进行权限控制。既然要在接口上进行权限控制,那么跟 vhr 的区别在哪里呢?
现在流行前后端分离开发,所以 Shiro 或者 Spring Security 中的那些前端标签现在基本上都不用了,取而代之的做法是用户在登录成功之后,向服务端发送请求,获取当前登录用户的权限以及角色信息,然后根据这些权限、角色等信息,在前端自动的去判断一个菜单或者按钮应该是显示还是隐藏,这么做的服务器托管目的是为了提高用户体验,避免用户点击一个没有权限的按钮。前端的显示或者隐藏仅仅只是为了提高用户体验,真正的权限控制还是要后端来做。
后端可以在接口或者业务层对权限进行处理,具体在哪里做,就要看各自的项目了。
所以,vhr 中的权限,从设计上来说,粒度并不算粗,也是细粒度的,只不过跟菜单表放在了一起,小伙伴们可能感觉有点粗。但是,菜单表是可以继续细化的,我们可以继续在菜单表中添加新的记录,新记录的亿华云计算 hidden 字段为 true,则菜单是隐藏的,就单纯只是细化权限而已。
如下图可以继续添加新的访问规则,只不过把 enabled 字段设置为 false 即可(这样菜单就不会显示出来了,单纯就只是权限的配置)。
所以 vhr 的权限设计是 OK 的。
当你理解了 vhr 中的权限设计,再来看 TienChin 这个项目,或者说看 RuoYi-Vue 这个脚手架,就会发现非常 easy 了。
2. 权限表首先我们来看看资源表的定义,也就是 sys_menu。
CREATE TABLE `sys_menu` (
`menu_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 菜单ID,
`menu_name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 菜单名称,
`parent_id` bigint(20) DEFAULT 0 COMMENT 父菜单ID,
`order_num` int(4) DEFAULT 0 COMMENT 显示顺序,
`path` varchar(200) COLLATE utf8mb4_unicode_ci DEFAULT COMMENT 路由地址,
`component` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 组件路径,
`query` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 路由参数,
`is_frame` int(1) DEFAULT 1 COMMENT 是否为外链(0是 1否),
`is_cache` int(1) DEFAULT 0 COMMENT 是否缓存(0缓存 1不缓存),
`menu_type` char(1) COLLATE utf8mb4_unicode_ci DEFAULT COMMENT 菜单类型(M目录 C菜单 F按钮),
`visible` char(1) COLLATE utf8mb4_unicode_ci DEFAULT 0 COMMENT 菜单状态(0显示 1隐藏),
`status` char(1) COLLATE utf8mb4_unicode_ci DEFAULT 0 COMMENT 菜单状态(0正常 1停用),
`perms` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 权限标识,
`icon` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT # COMMENT 菜单图标,
`create_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT COMMENT 创建者,
`create_time` datetime DEFAULT NULL COMMENT 创建时间,
`update_by` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT COMMENT 更新者,
`update_time` datetime DEFAULT NULL COMMENT 更新时间,
`remark` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT COMMENT 备注,
PRIMARY KEY (`menu_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3054 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT=菜单权限表;其实这里很多字段都和我们 vhr 项目项目很相似,我也就不重复啰嗦了,我这里主要和小伙伴们说一个字段,那就是 menu_type。
menu_type 表示一个菜单字段的类型,一个菜单有三种类型,分别是目录(M)、菜单(C)以及按钮(F)。这里所说的目录,相当于我们在 vhr 中所说的一级菜单,菜单相当于我们在 vhr 中所说的二级菜单。
当用户从前端登录成功后,要去动态加载的菜单的时候,就查询 M 和 C 类型的数据即可,F 类型的数据不是菜单项,查询的时候直接过滤掉即可,通过 menu_type 这个字段可以轻松的过滤掉 F 类型的数据。小伙伴们想想,F 类型的数据过滤掉之后,剩下的数据不就是一级菜单和二级菜单了,那不就和 vhr 又一样了么!
最后再来说说 F 类型的,F 类型的就是按钮级别的权限了,前端每一个按钮的执行,需要哪些权限,现在就在这里定义好。
举一个简单的例子大家来看下:
当需要展示用户管理这个菜单的时候,需要 system:user:list 这个权限,当需要点击用户修改这个按钮的时候,则需要 system:user:edit 这个权限。
其他相关的表基本上和 vhr 都是一样的,用户有用户表 sys_user,角色有角色表 sys_role,用户和角色关联的表是 sys_user_role,资源和角色关联的表是 sys_role_menu。
当用户登录成功后,后端会提供一个接口,将当前用户的角色和权限统统返回给前端:
查询角色思路:根据用户 id,先去sys_user_role 表中查询到角色 id,再根据角色 id 去sys_role 表中查询到对应的角色(这里为了方便大家理解这么描述,实际上一个多表联合查询即可)。
查询权限思路:根据用户 id,先去sys_user_role 表中查询到角色 id,再根据角色 id 去sys_role 表中查询到对应的角色,再拿着角色 id 去sys_role_menu 表中查询到对应的menu_id,再根据menu_id 去sys_menu 表中查询到对应的 menu 中的权限(这里为了方便大家理解这么描述,实际上一个多表联合查询即可)。
前端有了用户的权限以及角色之后,就可以自行决定是否显示某一个菜单或者是否展示某一个按钮了。
3. 后端权限判断我先来说说这块 TienChin 项目中是怎么做的(即 RuoYi 脚手架的实现方案),再来和 vhr 进行一个对比。
在 TienChin 项目中是通过注解来控制权限的,接口的访问权限都是通过注解来标记的,例如下面这种:
@PreAuthorize("@ss.hasPermi(system:menu:add)")
@PostMapping
public AjaxResult add(@Validated @RequestBody SysMenu menu) {
//省略
}
/
*** 修改菜单
*/
@PreAuthorize("@ss.hasPermi(system:menu:edit)")
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysMenu menu) {
//省略
}
/
*** 删除菜单
*/
@PreAuthorize("@ss.hasPermi(system:menu:remove)")
@DeleteMapping("/{ menuId}")
public AjaxResult remove(@PathVariable("menuId") Long menuId) {
//省略
}每一个接口需要什么权限,都是通过 @PreAuthorize 注解来实现的。
不过上面这种写法说到底还是有一点“硬编码”,因为访问哪个接口需要哪些权限,在代码中固定了,如果接口和权限直接的关系能够保存到数据库中,那么用户就可以在自己需要的时候,随时进行灵活修改,岂不美哉!
在 vhr 项目中,松哥利用 Spring Security 中自定义 FilterInvocationSecurityMetadataSource 和 AccessDecisionManager 实现了服务端动态控制权限。
相对来说,vhr 中的实现方案更灵活一些,因为可以配置接口和权限之间的关系。不过怎么说呢?其实像 RuoYi-Vue 这样硬编码其实也不是不可以,毕竟接口和权限之间的映射关系还是稍显“专业”一些,普通用户可能并不懂该如何配置,这个加入说系统提供了这个功能,那么更多的还是面向程序员这一类专业人员的,那么程序员到底是否需要这个功能呢?我觉得还是得具体情况具体分析。
总之,小伙伴们可以结合自己项目的实际情况,来决定接口和权限之间的映射关系是否需要动态管理,如果需要动态管理,那么可以按照 vhr 中的方案来,如果不需要动态管理,那么就按照 RuoYi-Vue 脚手架中的方式来就行了。
好啦,这就是 RuoYi-Vue 这个脚手架中关于权限的设计,现在有一个新的问题摆在面前:如何给用户设置权限的?现在整个系统的权限架构师安排的明明白白的,那么用户的权限又是从何而来的呢?这个我们下篇文章继续拆解。
很赞哦!(198)
相关文章
- 3.dns修改成功后,点击“域名解析”,按提示进行操作。解析格式一般如下:
- Google 的后端工程师都开始写小程序了?反编译 “猜画小歌”看看
- “拼多多式工作制”你敢要吗?技术员:钱多累得像骡子
- 手把手教你写网络爬虫(7):URL去重
- 为什么起域名意义非凡?起域名有什么名堂?
- 移除注释的完善思路:真的可以用正则实现?
- 百度大牛总结10条Python面试题陷阱,看看你是否会中招
- 码农跳槽指南:如何在新公司建立自己的“支配地位”?
- 审核通过的域名将显示在域名竞拍页面,并进入正式拍卖期,买家可以在拍卖周期内出价,加价幅度与拍卖保证金说明,点此查看。
- Python 爬取了马蜂窝的出行数据,告诉你这个夏天哪里最值得去!