若依Vue系统中的权限管理部分的功能都集中在了系统管理菜单模块中,如下图所示。其中权限部分主要涉及到了用户管理、角色管理、菜单管理、部门管理这四个部分。
一、若依Vue系统中的权限分类
根据观察,若依Vue系统中的权限分为以下几类
菜单权限:用户登录系统之后能看到哪些菜单
按钮权限:用户在一个页面上能看到哪些按钮,比如新增、删除等按钮
接口权限:用户带着认证信息请求后端接口,是否有权限访问,该接口和前端页面上的按钮一一对应
数据权限:用户有权限访问后端某个接口,但是不同的用户相同的接口相同的入参,根据权限大小不同,返回的结果应当不一样——权限大的能够看到的数据更多。
1.菜单权限
这个比较好理解,拥有不同权限的用户登录系统之后看到的菜单是不一样的,从新建菜单到给一个用户分配菜单权限,上一篇文章已经讲过,不赘述。
用户登录之后会请求后端的com.ruoyi.web.controller.system.SysLoginController#getRouters接口获取登录用户的菜单数据:
select distinct m.menu_id, m.parent_id, m.menu_name, m.path, m.component, m.visible, m.status, ifnull(m.perms,'') as perms, m.is_frame, m.is_cache, m.menu_type, m.icon, m.order_num, m.create_time
from sys_menu m
left join sys_role_menu rm on m.menu_id = rm.menu_id
left join sys_user_role ur on rm.role_id = ur.role_id
left join sys_role ro on ur.role_id = ro.role_id
left join sys_user u on ur.user_id = u.user_id
where u.user_id = #{userId} and m.menu_type in ('M', 'C') and m.status = 0 AND ro.status = 0
order by m.parent_id, m.order_num
菜单类型(M目录 C菜单 F按钮);菜单状态(0显示 1隐藏)
这是典型的用户-角色-菜单模型。
前端会根据该接口返回的数据渲染出不同的菜单。
2.按钮权限
新增按钮权限和新增菜单差不多,下图是我在新闻列表页面上新增了一个按钮叫做新闻新增,该按钮的权限分配和菜单的权限分配方法是一样的。
3.接口权限
每一个按钮基本上都会对应着一个后端的接口,前端会根据权限标志显示或者隐藏按钮,但是如果用户不点击按钮,直接通过http请求工具请求后端咋办?所以接口权限也是要有的,该权限和按钮上权限完全一致。
若依系统使用了SpringSecurity框架,接口权限都是基于注解@PreAuthorize实现的,比如,用户管理页面中的修改用户按钮对应的后端接口长这个样子
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@Log(title = "用户管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody SysUser user)
{
...
}
和其对应的前端按钮权限标志一样
如果没有权限访问接口,则会返回类似如下信息:
{
"msg": "请求访问:/system/user/list,认证失败,无法访问系统资源",
"code": 401
}
4.数据权限
用户有权限访问后端某个接口,但是不同的用户相同的接口相同的入参,根据权限大小不同,返回的结果应当不一样——权限大的能够看到的数据更多。
体现在若依Vue系统中,举个例子,现在若以系统中有两个用户,一个是超级管理员admin,一个是普通用户kdyzm
它们两者均有用户管理、菜单管理、角色管理权限,所以它们能够看到相应的菜单并作出相应的操作,比如删除、新增、修改等。这里有个问题,kdyzm应当只能看到一部分数据,而超级管理员应当能够看到所有数据,在若依系统中,是通过对角色数据权限修改进行控制的。
所以,相同的权限,超级管理员能够看到的用户数量和普通用户kdyzm能够看到的用户数量是不一样的。
超级管理员看到的用户管理页面:
普通用户kdyzm看到的用户管理页面:
二、各种类型权限实现原理
1.菜单权限
菜单权限很简单,实际上就是简单的用户-角色-菜单模型,那么菜单是什么时候加载的呢?ruoyi-ui\src\permission.js,加载的逻辑在这个文件中。
permission.js文件中设置了导航守卫,每次路由发生变化的时候就会触发router.beforeEach的回调函数。
router.beforeEach((to, from, next) => {
NProgress.start()
if (getToken()) {
/* has token*/
if (to.path === '/login') {
next({ path: '/' })