权限管理怎么做的?
我们当时做的是基于RBAC模型的权限管理,也就是基于角色进行权限控制。当时我们的权限管理总共涉及到了五张表。这五张表分别是
用户表(id,username[用户名],password[密码],sex[性别],birthday[生日],phone_number[手机号],email[邮箱],avatar[头像],create_time[创建时间],update_time[修改时间])、
角色表(id,name[角色名称],status[状态],create_time,update_time)、
权限表(id,name[权限名称],key[权限唯一标识],type[权限类型,1菜单,按钮],route_path[路由地址,不是前后端分离不用说],url[请求地址],pid[父权限ID],create_time,update_time)、
用户角色中间表(id,user_id(用户id),role_id(角色id),create_time,update_time)、
角色权限中间表(id,role_id(角色id),permission_id(权限id),create_time,update_time),
其中用户表和角色表是多对多的关系,所以我们引入了一张用户角色中间表配置用户和角色的多对多关系。而角色表和权限表也是多对多的关系,所以我们又引入了一张角色权限中间表来配置角色和权限的多对多关系。这就是权限管理的数据库表设计。然后在后台管理系统中,我们的权限管理又设计成了三个子模块,分别是用户管理、角色管理、权限管理。用户管理模块就是对用户的一个增删改查操作,在新增和修改用户时可以勾选要关联的角色,而且还需要判断用户名是否已存在,如果存在的话则不让新增或修改。角色管理这个模块就是对角色进行增删改查、启用、禁用、分配权限操作,在新增角色和修改角色时我们使用插件把系统中所有的权限以树形结构展示出来,然后可以勾选角色所要关联的权限。而且还需要判断角色是否已存在,如果存在的话则不让新增或修改。在删除角色时,我们还会判断当前角色是否被用户所关联,如果被关联则提示用户无法删除。最后就是权限管理模块了,权限管理也就是对权限的一个增删改查,我们首先把系统中所有的权限使用树形插件展示出来,然后又提供了增加、删除、修改按钮完成对权限树的操作。新增和修改权限时需要验证权限名称是否重复。删除时我们会把选中节点下所有的子节点也一并删除。刚才我说的是系统里面权限数据的维护,接下来我来说一下在系统里面具体怎么进行权限控制。因为当时我们这个项目采用的是前后端分离的开发模式,后端负责提供数据接口,前端负责进行路由跳转和页面渲染。然后前端和后端都需要进行权限控制。前端的权限控制是控制是否能够通过路由跳转到相应的组件和控制页面上的按钮是否显示。后端的权限控制是控制数据接口的访问权限。接下来我先来说一下前端的权限控制怎么做的,
- 首先用户登录成功之后,我们会将用户的所有权限放入一个全局的权限数组中。
- 然后我们遍历该数组查出所有的菜单权限,并且通过递归将菜单权限数据转为树形数据,然后通过双重for循环将树形数据进行遍历将菜单展示出来。然后如果用户点击了菜单,我们还通过路由守卫在进行路由跳转前进行拦截,并获取路由地址,然后遍历全局的权限数组,并和里面每一个菜单权限的路由地址进行匹配,如果匹配成功则说明该用户有权限,那么我们就通过next方法进行跳转。如果匹配不成功则说明该用户没有权限,我们就跳转到一个无权限操作的页面。
- 因为当时我们做的是按钮级别的权限控制,所以当时我们还对组件里面的按钮进行了显示和不显示的控制。具体做法就是给按钮绑定一个v-if指令,然后调用我们定义的一个全局函数并传入该按钮的权限唯一标识,在这个函数中我们遍历全局的权限数组,并且和每一个权限的唯一标识进行匹配,如果匹配成功就说明该用户有权限,我们就返回true,如果匹配不成功则说明该用户没有该权限,我们就返回false。
最后我再来说一下后端的权限控制怎么做的吧,当时后端我们也没有用任何的安全框架(shiro斜肉和Spring Security涩K油瑞特),因为自己写的东西使用起来更加顺手,后续维护起来也比较方便。当时在后端就是自己写了两个拦截器,一个是登录拦截器另外一个是权限拦截器。
我先来说一下登录拦截器吧,因为当时我们做的是基于Token的登录,也就是登录成功之后在服务器端生成Token然后存入Redis中并设置有效时间为半个小时。登录拦截器里面就是从请求头里面获取客户端传递过来的Token,然后验证Token的合法性(是否为空、长度是否合法、redis中是否存在该redis),如果不合法则直接return false并响应客户端Token不合法的提示信息。如果Token合法的话则从Token中解析到用户信息(用户ID和用户名),然后生成一个用户对象并放入request作用域中,然后return true进行放行。在接下来的操作中我们就可以从request作用域中获取到登录用户信息了。这就是登录拦截器实现的功能,然后我再来说下权限拦截器吧,因为我们在用户登录成功之后会将后台系统所有的权限集合和当前登录用户所拥有的权限集合放入redis中,所以在权限拦截器中我们首先会从redis中取出后台系统所有的权限集合和当前登录用户所拥有的权限集合。然后我们会先遍历后台系统所有权限集合并拿其中每一个权限的URL和当前请求地址进行等值比较,如果相等则说明当前请求需要进行权限拦截,如果全部遍历之后没有一个相等的则说明当前请求不需要进行权限拦截,那么就return true放行请求。如果当前请求需要进行权限拦截,接下来我们还会遍历当前登录用户所拥有的权限集合并拿每一个权限的URL和当前请求地址进行等值比较,如果相等则说明当前登录用户具有当前请求的访问权限直接return true放行,如果遍历结束没有一个相等的则说明当前登录用户没有当前请求的访问权限那就直接return false并响应客户端无权限访问的提示。