javaweb-青橙项目-8-83

文章目录

  • 第8章 授权
    • 学习目标
  • 1. RBAC模型
    • 1.1 什么是RBAC
    • 1.2 表结构分析
      • 1.2.1 用户与角色
      • 1.2.2 角色与权限
      • 1.2.3 权限与菜单
  • 2. 管理员角色设置
    • 2.1 需求分析
    • 2.2 思路分析
      • 2.2.1 提交角色设置
      • 2.2.2 读取角色设置
  • 3. 角色权限设置
    • 3.1 需求分析
    • 3.2 思路分析
      • 3.2.1 提交权限设置
      • 3.2.2 读取权限设置
  • 4. 用户权限控制
    • 4.1 需求分析
    • 4.2 spring security授权控制
      • 4.2.1 基于URL访问控制
      • 4.2.2 基于方法的访问控制
    • 4.3 思路分析
  • 5. 用户菜单筛选
    • 5.1 需求分析
    • 5.2 思路分析

第8章 授权

学习目标

能够描述或描绘RBAC模型的7张表及其关系
理解管理员角色设置的需求与实现思路,完成管理员角色设置功能
理解角色权限设置的需求与实现思路,完成角色权限设置的功能
掌握spring security授权控制的两种方式,实现用户权限控制的功能,前三步为此做准备
理解用户菜单展示的需求与实现思路,完成用户菜单展示的功能
序列-8:

1. RBAC模型

1.1 什么是RBAC

权限系统提的最多的就是 RBAC(Role-Based Access Control 基于角色的访问控
制)。 所谓角色,其实就是权限的集合,某个角色就是某几个权限的结合。其目的是为
了简化授权和鉴权的过程。
如果给所有人开放所有权限,那么滥用的权限会危害整个项目

小公司和比较简单的权限系统使用的基于用户的访问控制如下:

javaweb-青橙项目-8-83_第1张图片

这种访问控制只适用于操作人员比较少的系统,这样数据就简单
人很多,将成倍复杂
所以我们就需要基于角色的访问控制

javaweb-青橙项目-8-83_第2张图片

1.2 表结构分析

企业开发中 RBAC模型设计为7张表,其中4张为基础表,3张为中间表。
也有权限和菜单合并,最后只有5张表,但是通常有7张表
可以视为3组
javaweb-青橙项目-8-83_第3张图片

1.2.1 用户与角色

用户和角色为多对多关系,通过用户角色中间表关联
tb_admin 管理员表:

javaweb-青橙项目-8-83_第4张图片

tb_role 角色表:

javaweb-青橙项目-8-83_第5张图片

tb_admin_role 管理员角色中间表:
javaweb-青橙项目-8-83_第6张图片

1.2.2 角色与权限

角色和权限为多对多关系,通过角色权限中间表关联
tb_role 角色表:
javaweb-青橙项目-8-83_第7张图片
tb_resource 权限表(资源表):
一般有两层,资源id,资源分组(一组权限点)
资源key代表资源标识,未来会将其标注在某个方法上,控制某个url
后面的名字是给用户看的,代表权限名字,实际上是分配了一个资源组的key

之所以将资源和菜单分开是为了更加精细化的分配权限
javaweb-青橙项目-8-83_第8张图片

tb_role_resource 角色资源中间表:
javaweb-青橙项目-8-83_第9张图片

1.2.3 权限与菜单

一般已经由开发者预定义
权限和菜单为多对多关系,通过权限菜单中间表关联
tb_resource 权限表(资源表):
javaweb-青橙项目-8-83_第10张图片

tb_menu 菜单表:

javaweb-青橙项目-8-83_第11张图片

tb_resource_menu 资源菜单中间表:
javaweb-青橙项目-8-83_第12张图片

2. 管理员角色设置

2.1 需求分析

java只要加个中间件就能解决
在给用户分配权限时,将其赋予某些角色
管理员和角色为多对多关系,在保存管理员时实现对管理员角色中间表的添加。

javaweb-青橙项目-8-83_第13张图片

在修改某个管理员时,需要将该管理员的角色id列表读取出来。

2.2 思路分析

2.2.1 提交角色设置

(1)创建管理员角色中间表的实体类和数据访问接口 因为要操作他了
(2)创建组合实体类,包含管理员实体类和角色ID集合两个属性 新类
(3)修改管理员实体类,为id添加以下注解可以标识该主键为自增 因为要返回id

@GeneratedValue(strategy=GenerationType.IDENTITY)

添加此注解后,可以获取新增的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提交给后端保存

javaweb-青橙项目-8-83_第14张图片

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
然而数据应该是动态的,这里定死只是为了解释

//构建权限列表
List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>();
        grantedAuths.add(new SimpleGrantedAuthority("goods_add"));
        grantedAuths.add(new SimpleGrantedAuthority("goods_edit"));
        .....

修改applicationContext_security.xml
限定权限所对应的资源访问

<intercept‐url pattern="/*/find*.do" access="hasAnyAuthority()" />
<intercept‐url pattern="/brand/*.do" access="hasAuthority('brand')" />
<intercept‐url pattern="/spu/save.do"
        access="hasAnyAuthority('goods_add','goods_edit')" />

hasAnyAuthority():拥有任意权限都可以访问 find*放开了所有的查询权限,因为这对系统影响较低,危险
hasAuthority(‘brand’): 拥有brand的权限可以访问 拥有某个权限才可以访问
hasAnyAuthority(‘goods_add’,‘goods_edit’)" :拥有goods_add和goods_edit其中一个
权限就可以访问 只要有其中一个权限就能访问

4.2.2 基于方法的访问控制

对方法控制,常见的加注解,因此要放开注解扫描
对当前用户授权,同上 ,对方法的访问控制如下:
(1)修改applicationContext_security.xml ,增加配置 ,启用注解

<global‐method‐security pre‐post‐annotations="enabled" />

(2)在进行权限控制的方法上添加注解
拥有brand的权限可以访问

@PreAuthorize("hasAuthority('brand')")

brand为资源key

4.3 思路分析

(1)编写SQL语句,通过登录名查询资源KEY列表
不知道该角色拥有什么权限,查询
然而没有直接的权限表,常见为嵌套查询
从里向外写
先根据用户名查询出用户id
javaweb-青橙项目-8-83_第15张图片
用中间表查询该id的角色对应id,可能出现多个角色,所以拥IN
javaweb-青橙项目-8-83_第16张图片
根据角色id查询角色-资源中间表,获得对应的资源权限id
javaweb-青橙项目-8-83_第17张图片
根据资源id获得用户可以使用的资源信息
javaweb-青橙项目-8-83_第18张图片
最终只需要资源key即可

SELECT res_key FROM tb_resource WHERE id IN(
	SELECT resource_id FROM tb_role_resource WHERE role_id IN(
		SELECT role_id FROM tb_admin_role WHERE admin_id IN(
			SELECT id FROM tb_admin WHERE login_name='admin')))

这里我们用到了4层嵌套循环
javaweb-青橙项目-8-83_第19张图片

(2)数据访问接口新增方法,根据登录名查询资源KEY列表
(3)服务层实现根据登录名查询资源KEY列表
(4)UserDetailsServiceImpl的loadUserByUsername方法,调用根据登录名查询资源
KEY列表的方法,将资源key列表添加到当前用户。

//构建权限列表
List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>();
        List<String> resKeyList = resourceService.findResKeyByLoginName(s);
        for(String resKey :resKeyList ){
     
        grantedAuths.add(new SimpleGrantedAuthority(resKey));
        }

(5)修改applicationContext_security.xml,添加对url的拦截,或在方法上添加注解实
现对方法的拦截。

5. 用户菜单筛选

5.1 需求分析

用户登录后进入主界面,显示的菜单为用户所拥有的权限关联的菜单。不具有权限的菜单不显示。

5.2 思路分析

(1)编写SQL语句,根据当前登录名获取菜单列表的方法
根据资源key找到绑定的菜单
此时需要的是资源id而非用户权限id,此时
javaweb-青橙项目-8-83_第20张图片
要根据resourceid去resource-menu中间表中得到menuid
javaweb-青橙项目-8-83_第21张图片
仅仅得到menuid是不够的,因此根据menuid得到该用户所支配的资源菜单的信息
javaweb-青橙项目-8-83_第22张图片
然而需要的不仅仅是三级菜单,还需要二,一级菜单,否则太单调
javaweb-青橙项目-8-83_第23张图片
当然,也需要一级菜单,不断套娃
javaweb-青橙项目-8-83_第24张图片

我们需要用户所对应的菜单,包括一,二,三级菜单
但是上面的数据库代码只能分开获得,鱼和熊掌不能兼得。
因此要分别写三个语句,然后union联合一下,就相当于一条语句,可以同时得出三个菜单
javaweb-青橙项目-8-83_第25张图片
javaweb-青橙项目-8-83_第26张图片
不得不说嵌套查询真的慢

SELECT * FROM tb_menu WHERE id IN(
        SELECT menu_id FROM tb_resource_menu WHERE resource_id IN (
        SELECT resource_id FROM tb_role_resource WHERE role_id IN (
        SELECT role_id FROM tb_admin_role WHERE admin_id IN (
        SELECT id FROM tb_admin WHERE login_name='admin'
        )
        )
        )
        )

注意通过上述语句,获取的菜单列表只包含三级菜单,而我们需要返回包括一级菜单、
二级菜单和三级菜单的菜单列表。只要三级菜单存在,就要有它的二级菜单;只要有一
个二级菜单就要有它的一级菜单。
查询二级菜单列表:

SELECT * FROM tb_menu WHERE id IN(
        SELECT parent_id FROM tb_menu WHERE id IN(
        SELECT menu_id FROM tb_resource_menu WHERE resource_id IN (
        SELECT resource_id FROM tb_role_resource WHERE role_id IN (
        SELECT role_id FROM tb_admin_role WHERE admin_id IN (
        SELECT id FROM tb_admin WHERE login_name='admin'
        )
        )
        )
        )
        )

查询一级菜单列表:

SELECT * FROM tb_menu WHERE id IN (
        SELECT parent_id FROM tb_menu WHERE id IN(
        SELECT parent_id FROM tb_menu WHERE id IN(
        SELECT menu_id FROM tb_resource_menu WHERE resource_id IN (
        SELECT resource_id FROM tb_role_resource WHERE role_id IN (
        SELECT role_id FROM tb_admin_role WHERE admin_id IN (
        SELECT id FROM tb_admin WHERE login_name='admin'
        )
        )
        )
        )
        )
        )

最后我们通过UNION运算符将三个语句连接成一条语句

SELECT * FROM tb_menu WHERE id IN(
        SELECT menu_id FROM tb_resource_menu WHERE resource_id IN (
        SELECT resource_id FROM tb_role_resource WHERE role_id IN (
        SELECT role_id FROM tb_admin_role WHERE admin_id IN (
        SELECT id FROM tb_admin WHERE login_name='admin'
        )
        )
        )
        )
        UNION
        SELECT * FROM tb_menu WHERE id IN(
        SELECT parent_id FROM tb_menu WHERE id IN(
        SELECT menu_id FROM tb_resource_menu WHERE resource_id IN (
        SELECT resource_id FROM tb_role_resource WHERE role_id IN (
        SELECT role_id FROM tb_admin_role WHERE admin_id IN (
        SELECT id FROM tb_admin WHERE login_name='admin'
        )
        )
        )
        )
        )
        UNION
        SELECT * FROM tb_menu WHERE id IN (
        SELECT parent_id FROM tb_menu WHERE id IN(
        SELECT parent_id FROM tb_menu WHERE id IN(
        SELECT menu_id FROM tb_resource_menu WHERE resource_id IN (
        SELECT resource_id FROM tb_role_resource WHERE role_id IN (
        SELECT role_id FROM tb_admin_role WHERE admin_id IN (
        SELECT id FROM tb_admin WHERE login_name='admin'
        )
        )
        )
        )
        )
        )

联合后sql


/*三级菜单*/
SELECT * FROM tb_menu  WHERE id IN(
			SELECT menu_id FROM tb_resource_menu WHERE resource_id IN(
				SELECT resource_id FROM tb_role_resource WHERE role_id IN(
					SELECT role_id FROM tb_admin_role WHERE admin_id IN(
						SELECT id FROM tb_admin WHERE login_name='admin'))))
UNION
/*二级菜单*/
SELECT * FROM tb_menu  WHERE id IN(
		SELECT parent_id FROM  tb_menu WHERE id IN(
			SELECT menu_id FROM tb_resource_menu WHERE resource_id IN(
				SELECT resource_id FROM tb_role_resource WHERE role_id IN(
					SELECT role_id FROM tb_admin_role WHERE admin_id IN(
						SELECT id FROM tb_admin WHERE login_name='admin')))))
UNION
/*一级菜单*/
SELECT * FROM tb_menu  WHERE id IN(
	SELECT parent_id FROM  tb_menu WHERE id =(
		SELECT parent_id FROM  tb_menu WHERE id IN(
			SELECT menu_id FROM tb_resource_menu WHERE resource_id IN(
				SELECT resource_id FROM tb_role_resource WHERE role_id IN(
					SELECT role_id FROM tb_admin_role WHERE admin_id IN(
						SELECT id FROM tb_admin WHERE login_name='admin'))))))

(2)将上述SQL封装为查询方法
(3)在controller获取当前用户名,调用上述查询方法。

你可能感兴趣的:(javaweb,java)