文章目录
第 8 章 授权
学习目标
能够描述或描绘 RBAC 模型的 7 张表及其关系
理解管理员角色设置的需求与实现思路,完成管理员角色设置功能
理解角色权限设置的需求与实现思路,完成角色权限设置的功能
掌握 spring security 授权控制的两种方式,实现用户权限控制的功能,前三步为此做准备
理解用户菜单展示的需求与实现思路,完成用户菜单展示的功能
序列 - 8:
1. RBAC 模型
1.1 什么是 RBAC
权限系统提的最多的就是 RBAC(Role-Based Access Control 基于角色的访问控
制)。 所谓角色,其实就是权限的集合,某个角色就是某几个权限的结合。其目的是为
了简化授权和鉴权的过程。
如果给所有人开放所有权限,那么滥用的权限会危害整个项目
小公司和比较简单的权限系统使用的基于用户的访问控制如下:
这种访问控制只适用于操作人员比较少的系统,这样数据就简单
人很多,将成倍复杂
所以我们就需要基于角色的访问控制
1.2 表结构分析
企业开发中 RBAC 模型设计为 7 张表,其中 4 张为基础表,3 张为中间表。
也有权限和菜单合并,最后只有 5 张表,但是通常有 7 张表
可以视为 3 组
1.2.1 用户与角色
用户和角色为多对多关系,通过用户角色中间表关联
tb_admin 管理员表:
tb_role 角色表:
tb_admin_role 管理员角色中间表:
1.2.2 角色与权限
角色和权限为多对多关系,通过角色权限中间表关联
tb_role 角色表:
tb_resource 权限表(资源表):
一般有两层,资源 id,资源分组(一组权限点)
资源 key 代表资源标识,未来会将其标注在某个方法上,控制某个 url
后面的名字是给用户看的,代表权限名字,实际上是分配了一个资源组的 key
之所以将资源和菜单分开是为了更加精细化的分配权限
tb_role_resource 角色资源中间表:
1.2.3 权限与菜单
一般已经由开发者预定义
权限和菜单为多对多关系,通过权限菜单中间表关联
tb_resource 权限表(资源表):
tb_menu 菜单表:
tb_resource_menu 资源菜单中间表:
2. 管理员角色设置
2.1 需求分析
java 只要加个中间件就能解决
在给用户分配权限时,将其赋予某些角色
管理员和角色为多对多关系,在保存管理员时实现对管理员角色中间表的添加。
在修改某个管理员时,需要将该管理员的角色 id 列表读取出来。
2.2 思路分析
2.2.1 提交角色设置
(1)创建管理员角色中间表的实体类和数据访问接口 因为要操作他了
(2)创建组合实体类,包含管理员实体类和角色 ID 集合两个属性 新类
(3)修改管理员实体类,为 id 添加以下注解可以标识该主键为自增 因为要返回 id
添加此注解后,可以获取新增的 id 值。
(4)修改管理员 add 方法,取出管理员实体保存,取出角色 ID 集合,循环添加到管理员
角色中间表。
(5)注意在保存管理员密码时需要对密码进行 bcrypt 加密。
(6)前端可以根据代码生成器生成的代码修改即可。“所属角色” 使用 elementui 的 select 选择器 ,为 el‐select 设置 multiple 属性即可启用多选 ,详见官方文档。
2.2.2 读取角色设置
(1)修改 findById 方法的返回值为组合实体类,修改其中的逻辑,组合实体类的角色 id
集合需要查询管理员角色中间表。
(2)修改 update 方法,删除原来的相关的中间表数据,再循环添加中间表数据。
(3)读取后需要把密码属性设置为 null, 如果用户没有在界面输入密码则保持密码不
变,(这是为了防止密码在读取后再次加密,这就直接爆炸)如果填写了密码需要进行 bcrypt 加密。
3. 角色权限设置
3.1 需求分析
显示所有的权限列表,并自动勾选已经保存的权限。用户勾选权限后,点击提交,将勾
选的权限 id 提交给后端保存
3.2 思路分析
3.2.1 提交权限设置
(1)创建角色权限中间表的实体类和数据访问接口 要对其操作了
(2)前后端约定要提交的数据格式,包括 “角色 id” 和“权限 id 列表”。根据约定的数据格式
创建组合实体类。
(3)后端添加方法,接收组合实体类参数,提取 “角色 id” 和“权限 id 列表”,循环读取权限id 插入到角色权限中间表中。
3.2.2 读取权限设置
(1)后端查询权限表(资源表),以树状结构返回数据。前端使用两层 v-for 循环输出列表。
(2)后端添加方法,根据角色查询权限 id 列表,前端获取权限 id 列表后实现复选框的勾选。
4. 用户权限控制
4.1 需求分析
当用户执行一个不存在的权限的 url,需要拦截请求。
4.2 spring security 授权控制
4.2.1 基于 URL 访问控制
在 UserDetailsServiceImpl 的 loadUserByUsername 方法,实现对当前用户的授权
这里的权限就是资源表的资源 key
然而数据应该是动态的,这里定死只是为了解释
修改 applicationContext_security.xml
限定权限所对应的资源访问
hasAnyAuthority():拥有任意权限都可以访问 find * 放开了所有的查询权限,因为这对系统影响较低,危险
hasAuthority(‘brand’): 拥有 brand 的权限可以访问 拥有某个权限才可以访问
hasAnyAuthority(‘goods_add’,‘goods_edit’)” :拥有 goods_add 和 goods_edit 其中一个
权限就可以访问 只要有其中一个权限就能访问
4.2.2 基于方法的访问控制
对方法控制,常见的加注解,因此要放开注解扫描
对当前用户授权,同上 ,对方法的访问控制如下:
(1)修改 applicationContext_security.xml ,增加配置 ,启用注解
(2)在进行权限控制的方法上添加注解
拥有 brand 的权限可以访问
brand 为资源 key
4.3 思路分析
(1)编写 SQL 语句,通过登录名查询资源 KEY 列表不知道该角色拥有什么权限,查询然而没有直接的权限表,常见为嵌套查询从里向外写先根据用户名查询出用户 id
用中间表查询该 id 的角色对应 id, 可能出现多个角色,所以拥 IN
根据角色 id 查询角色 - 资源中间表,获得对应的资源权限 id
根据资源 id 获得用户可以使用的资源信息
最终只需要资源 key 即可
这里我们用到了 4 层嵌套循环
(2)数据访问接口新增方法,根据登录名查询资源 KEY 列表
(3)服务层实现根据登录名查询资源 KEY 列表
(4)UserDetailsServiceImpl 的 loadUserByUsername 方法,调用根据登录名查询资源
KEY 列表的方法,将资源 key 列表添加到当前用户。
(5)修改 applicationContext_security.xml,添加对 url 的拦截,或在方法上添加注解实
现对方法的拦截。
5. 用户菜单筛选
5.1 需求分析
用户登录后进入主界面,显示的菜单为用户所拥有的权限关联的菜单。不具有权限的菜单不显示。
5.2 思路分析
(1)编写 SQL 语句,根据当前登录名获取菜单列表的方法
根据资源 key 找到绑定的菜单
此时需要的是资源 id 而非用户权限 id,此时
要根据 resourceid 去 resource-menu 中间表中得到 menuid
仅仅得到 menuid 是不够的,因此根据 menuid 得到该用户所支配的资源菜单的信息
然而需要的不仅仅是三级菜单,还需要二,一级菜单,否则太单调
当然,也需要一级菜单,不断套娃
我们需要用户所对应的菜单,包括一,二,三级菜单
但是上面的数据库代码只能分开获得,鱼和熊掌不能兼得。
因此要分别写三个语句,然后 union 联合一下, 就相当于一条语句,可以同时得出三个菜单
不得不说嵌套查询真的慢
注意通过上述语句,获取的菜单列表只包含三级菜单,而我们需要返回包括一级菜单、
二级菜单和三级菜单的菜单列表。只要三级菜单存在,就要有它的二级菜单;只要有一
个二级菜单就要有它的一级菜单。
查询二级菜单列表:
查询一级菜单列表:
最后我们通过 UNION 运算符将三个语句连接成一条语句
联合后 sql
(2)将上述 SQL 封装为查询方法
(3)在 controller 获取当前用户名,调用上述查询方法。