当前位置: 首页 > 科技观察

我想细化按钮的权限,怎么办?

时间:2023-03-20 19:13:35 科技观察

因为写了很多SpringSecurity的文章,总有朋友来问宋哥:按钮级权限怎么实现?有些看过vhr的朋友甚至会问这样的问题。事实上,有时这让我很沮丧。最近正好想做TienChin的项目,所以把这个问题拿出来和小伙伴们好好讨论一下。.1.权限粒度首先,大家都知道权限是有不同粒度的。在vhr项目中,我是整体根据请求地址来处理权限的。这个粒度是粗的还是细的?可能有朋友觉得权限粒度太粗了,所谓细粒度的权限应该是按按钮来的。如果有做过不分前后端开发的朋友,应该会有这样的体会:在Shiro或者SpringSecurity框架中,提供了一些标签,通过标签可以满足某些角色或权限。接下来,显示一个按钮;当用户不具有某个角色或权限时,该按钮将自动隐藏。但是仔细想想,按钮的显示和隐藏,只是为了提升用户体验而改变前端页面的样式而已。本质上,当你点击一个按钮时,你仍然发送一个HTTP请求,服务器处理这个请求。接口必须进行权限控制。既然是要在接口上进行权限控制,那和vhr有什么区别呢?现在流行前后端分开开发,所以Shiro或者SpringSecurity里面的那些前端标签现在基本不用了。而是用户在登录成功后向服务器发送请求,获取当前登录用户的权限和角色信息。然后,前端根据这些权限、角色等信息,自动判断某个菜单或按钮是否显示或隐藏。这样做的目的是提高用户体验,防止用户在未经许可的情况下点击按钮。前端的显示或者隐藏只是为了提升用户体验,真正的权限控制还是需要后端来完成。后端可以在接口层或业务层处理权限,具体在哪做取决于各自的项目。所以从设计上来说,vhr中的权限不是粗粒度的,而是细粒度的,只是和菜单表放在一起,小伙伴们可能会觉得有点粗。不过,菜单表还可以继续细化。我们可以继续在菜单表中添加新的记录。如果新记录的hidden字段为true,则隐藏菜单,只是细化权限而已。如下图,可以继续添加新的访问规则,只需将enabled字段设置为false即可(这样就不会显示菜单了,简单来说就是一个权限配置)。所以vhr的权限设计是OK的。当你了解了vhr中的权限设计,再看TienChin项目,或者若一-Vue脚手架,你会发现很容易。2.权限表首先我们来看一下资源表的定义,也就是sys_menu。CREATETABLE`sys_menu`(`menu_id`bigint(20)NOTNULLAUTO_INCREMENTCOMMENT'menuID',`menu_name`varchar(50)COLLATEutf8mb4_unicode_ciNOTNULLCOMMENT'menuname',`parent_id`bigint(20)DEFAULT'0'COMMENT'父菜单ID',`order_num`int(4)DEFAULT'0'COMMENT'显示顺序',`path`varchar(200)COLLATEutf8mb4_unicode_ciDEFAULT''COMMENT'路由地址',`component`varchar(255)COLLATEutf8mb4_unicode_ciDEFAULTNULLCOMMENT'组件路径',`query`varchar(255)COLLATEutf8mb4_unicode_ciDEFAULTNULLCOMMENT'路由参数',`is_frame`int(1)DEFAULT'1'COMMENT'是否为外部链接(0为1否)',`is_cache`int(1)DEFAULT'0'COMMENT'是否缓存(0缓存1不缓存)',`menu_type`char(1)COLLATEutf8mb4_unicode_ciDEFAULT''COMMENT'菜单类型(M目录C菜单Fbutton)',`visible`char(1)COLLATEutf8mb4_unicode_ciDEFAULT'0'COMMENT'menustatus(0display1hidden)',`status`char(1)COLLATEutf8mb4_unicode_ciDEFAULT'0'COMMENT'menustatus(0normal1disabled)',`perms`varchar(100)COLLATEutf8mb4_unicode_ciDEFAULTNULLCOMMENT'PermissionID',`icon`varchar(100)COLLATEutf8mb4_unicode_ciDEFAULT'#'COMMENT'菜单图标',`create_by`varchar(64)COLLATEutf8mb4_unicode_ciDEFAULT''COMMENT'创建者',`create_time`datetimeDEFAULTNULLCOMMENT'创建时间',`update_by`varchar(64)COLLATEutf8mb4_unicode_ciDEFAULT''COMMENT'更新者',`update_time`datetimeDEFAULTNULLCOMMENT'更新时间',`remark`varchar(500)COLLATEutf8mb4_unicode_ciDEFAULT''COMMENT'备注',PRIMARYKEY(`menu_id`))ENGINE=InnoDBAUTO_INCREMENT=3054DEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_unicode_ciCOMMENT='菜单权限表';其实这里很多字段和我们的vhr项目很像,就不在赘述了,我这里主要跟朋友说说字段,也就是menu_typemenu_type表示一个菜单字段的类型,一个菜单有三种类型,分别是directory(M)、菜单(C)和按钮(F)。这里所说的目录相当于vhr中的一级菜单,menu相当于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,再去sys_role表根据角色id查询对应的角色,然后取角色id到sys_role_menu表查询对应的menu_id,然后根据menu_id去sys_menu表查询对应菜单中的权限(这里为了方便大家理解描述,其实多表联合查询就是足够的)。前端有了用户的权限和角色后,可以决定是否显示某个菜单或者是否显示某个按钮。3、后端权限判断先说一下这个TienChin项目是怎么做的(也就是若一脚手架的实现),然后和vhr对比一下。在TienChin项目中,通过注解来控制权限,通过注解来标记接口的访问权限,例如:@PreAuthorize("@ss.hasPermi('system:menu:add')")@PostMappingpublicAjaxResultadd(@Validated@RequestBodySysMenumenu){//省略}/***修改菜单*/@PreAuthorize("@ss.hasPermi('system:menu:edit')")@PutMappingpublicAjaxResultedit(@Validated@RequestBodySysMenumenu){//Omit}/***删除菜单*/@PreAuthorize("@ss.hasPermi('system:menu:remove')")@DeleteMapping("/{menuId}")publicAjaxResultremove(@PathVariable("menuId")LongmenuId){//省略}每个接口需要什么权限,通过@PreAuthorize注解实现。不过,上面的写法到头来还是有点“硬编码”,因为访问哪个接口需要哪些权限是固定在代码里的。如果可以将接口和权限的直接关系保存在数据库中,那么用户就可以使用自己需要的权限。随时随地灵活修改岂不是太棒了!在vhr项目中,松哥使用了SpringSecurity中自定义的FilterInvocationSecurityMetadataSource和AccessDecisionManager来实现服务器的动态控制权限。相对来说,vhr中的实现方案更加灵活,因为接口和权限的关系是可以配置的。但是怎么办?其实像若一-Vue这样的硬编码其实也不是没有可能。毕竟接口和权限的映射关系还是有点“专业”。普通用户可能不明白如何配置它。这个加法说系统提供这个功能。所以更多的是针对程序员这样的专业人士,那么程序员真的需要这个功能吗?我觉得还是要具体情况具体分析。总之,朋友们可以结合自己项目的实际情况来决定是否需要对接口和权限的映射关系进行动态管理。如果需要动态管理,那么按照vhr中的scheme即可。如果不需要动态管理,那么按照若一-Vue脚手架中的方法即可。嗯,这就是若一-Vue脚手架中权限的设计。现在有一个新的问题摆在我们面前:如何给用户设置权限?既然整个系统的权限架构师都安排好了,那用户的权限从哪里来呢?下一篇我们会继续拆解这个。