- 如果没有权限控制,系统的功能完全不设防,全部暴露在所有用户面前,用户登录以后可以使用系统中的所有功能,这是实际运行中不能接受的。
- 所以权限控制系统的目标就是:管理用户行为,保护系统功能。
- 那么如何进行权限控制呢?
- 定义资源
- 创建权限
- 创建角色
- 管理用户
- 建立关联关系
- 定义资源:资源就是系统中需要保护起来的功能。具体形式很多:URL 地址、handler方法、service 方法、页面元素等等都可以定义为资源使用权限控制系统保护起来。
- 创建权限:一个功能复杂的项目会包含很多具体资源,成千上万都有可能。这么多资源逐个进行操作太麻烦了。为了简化操作,可以将相关的几个资源封装到一起,打包成一个“权限”同时分配给有需要的人。
- 创建角色:对于一个庞大系统来说,一方面需要保护的资源非常多,另一方面操作系统的人也非常多。把资源打包为权限是对操作的简化,同样把用户划分为不同角色也是对操作的简化。否则直接针对一个个用户进行管理就会很繁琐。所以角色就是用户的分组、分类。先给角色分配权限,然后再把角色分配给用户,用户以这个角色的身份操作系统就享有角色对应的权限了。
- 管理用户:系统中的用户其实是人操作系统时用来登录系统的账号、密码。
- 建立关联关系:
- 权限→资源:单向多对多
- Java 类之间单向:从权限实体类可以获取到资源对象的集合,但是通过资源获取不到权限
- 数据库表之间多对多:一个权限可以包含多个资源,一个资源可以被分配给多个不同权限
- 角色→权限:单向多对多
- Java 类之间单向:从角色实体类可以获取到权限对象的集合,但是通过权限获取不到角色
- 数据库表之间多对多:一个角色可以包含多个权限,一个权限可以被分配给多个不同角色
- 用户→角色:双向多对多
- Java 类之间双向:可以通过用户获取它具备的角色,也可以看一个角色下包含哪些用户
- 数据库表之间多对多:一个角色可以包含多个用户,一个用户可以身兼数职
- 权限→资源:单向多对多
2.1.没有中间表的情况
如果只能在一个外键列上存储关联关系数据,那么现在这情况无法使用 SQL 语句进行关联查询。
2.2. 有中间表
select t_studet.id,t_student.name from t_student left join t_inner on t_studen.id = t_inner.stuent_id left join t_subject on t_inner.subject_id=t_subject.id where t_subjct.id=1
2.3.中间表主键生成的方式
方式一:另外设置字段作为主键
方式二:使用联合主键(组合起来不能重复即可!)
鉴于权限控制的核心是用户通过角色与权限进行关联,所以前面描述的权限控制系统可以提炼为一个模型:RBAC(Role-Based Access Control ,基于角色的访问控制)。在 RBAC 模型中,一个用户可以对应多个角色,一个角色拥有多个权限,权限具体定义用户可以做哪些事情。
3.1 RBAC0~RBAC3:4种权限模型介绍
- RBAC0:最基本的RBAC模型,RBAC模型的核心部分,后面三种升级版 RBAC 模型也都是建立在 RBAC0的基础上。
- RBAC1:在 RBAC0 的基础上增加了角色之间的继承关系。角色 A 继承角色 B 之后将具备 B 的权限再增加自己独有的其他权限。比如:付费会员角色继承普通会员角色,那么付费会员除了普通会员的权限外还具备浏览付费内容的权限。
- RBAC2:在 RBAC0 的基础上进一步增加了角色责任分离关系。责任分离关系包含静态责任分离和动态责任分离两部分。
- 静态责任分离:给用户分配角色时生效
- 互斥角色:权限上相互制约的两个或多个角色就是互斥角色。用户只能被分配到一组互斥角色中的一个角色。例如:一个用户不能既有会计师角色又有审计师角色。
- 基数约束:
一个角色对应的访问权限数量应该是受限的;一个角色中用户的数量应该是受限的 ;一个用户拥有的角色数量应该是受限的 - 先决条件角色:用户想拥有A角色就必须先拥有B角色,从而保证用户拥有 X 权限的前提是拥有 Y 权限。例如:“金牌会员”角色只能授予拥有“银牌会员”角色的用户,不能直接授予普通用户
- 动态责任分离:用户 登录系统时生效
- 一个用户身兼数职,在特定场景下激活特定角色:马云在阿里巴巴内部激活创始人角色;马云在某企业级论坛上激活演讲嘉宾角色
- 静态责任分离:给用户分配角色时生效
- RBAC3:RBAC3 是在 RBAC0 的基础上同时添加 RBAC2 和 RBAC3 的约束,最全面、最复杂。
3.2 模型图解
3.2.1基本RBAC模型
3.2.2扩展RBAC模型
1.权限控制
2.给admin分配角色role
2.1目标:通过页面操作把 Admin 和 Role 之间的 关联关系保存到数据库。
2.2思路
2.3代码:前往分配页面
2.3.1创建保存 Admin-Role 关联关系的数据库表
这个表并不对应现实生活中或项目业务功能中的一个具体实体,所以没有对应的实体类,也不通过逆向工程做逆向生成
2.3.2修改 “ 分配 ”按钮
2.3.3创建 AssignHandler
2.3.4 RoleServiceImpl中的方法
2.3.5 SQL 语句
2.3.6 在页面上显示角色数据
2.3.7效果展示
2.3.8 调整表单让表单能够提交数据
jquery代码:
2.4 代码:执行分配
2.4.1handler方法
2.4.2Service 方法
2.4.3 SQL 语句
2.4.4 修正 Bug
3.给role分配权限(auth)
3.1目标:把角色和权限的关联关系保存到数据库
3.2思路:
3.3代码:前往分配权限页面
3.3.1创建权限表:t_auth表,填充假数据
name 字段:给资源分配权限或给角色分配权限时使用的具体值,将来做权限验证也是使用 name 字段的值来进行比对。建议使用英文。
title 字段:在页面上显示,让用户便于查看的值。建议使用中文。
category_id 字段:关联到当前权限所属的分类。这个关联不是到其他表关联,而是就在当前表内部进行关联,关联其他记录。所以说,t_auth 表中是依靠 category_id 字段建立了“节点”之间的父子关系。
name 字段中值的格式:中间的“:”没有任何特殊含义。不论是我们自己写的代码
还是将来使用的框架都不会解析“:”。如果不用“:”,用“%、@、&、*、-”等等这样
的符号也都是可以的。 模块:操作名:user:delete
3.3.2 逆向工程生成资源:Auth、AuthExample、AuthMapper、AuthMapper.xml
3.3.3 创建角色到权限之间关联关系的中间表
3.3.4 创建分配权限模态框文件并引入
3.3.5 给 "☑" 绑定单击响应函数,用以打开分配权限模态框
3.3.6 在role-page.html中加入zTree的环境
3.3.7 在my-role.js中编写函数: fillAuthTree()
3.3.8 后端代码:获取所有的权限信息
3.3.9 效果展示
3.3.10 效果修正
3.3.11 把已经分配的权限进行回显(完整的fillAuthTree()代码)
3.3.12 后端代码
3.4 勾选权限,执行分配
3.4.1给“执行分配”按钮绑定单击响应函数
3.4.2后端代码:执行分配权限
4.给menu分配权限(auth)(同role分配权限,略)
1.众筹项目加入 SpringSecurity 环境
1.1加入依赖
1.2在web.xml中加入Filter
1.3配置类CrowdfundingSecurityConfig
1.4自动扫描的包(谁来把 CrowdfundingSecurityConfig 扫描到 IOC 里?)
考虑到权限控制系统更多的需要控制 Web 请求,而且有些请求没有经过 Service 方法,所以在SpringMVC的IOC容器中扫描CrowdfundingSecurityConfig。但是,SpringSecurity是有管理 Service、Dao 方法的能力的。
1.5 多个 IOC 容器之间的关系
①.问题描述:项目启动时控制台抛异常说找不到“springSecurityFilterChain”的 bean。
②.问题分析:
Web 组件加载顺序:Listener→Filter→Servlet
Spring IOC 容器:ContextLoaderListener 创建
SpringSecurityFilterChain:从 IOC 容器中找到对应的 bean
SpringMVC IOC 容器:DispatcherServlet 创建
DelegatingFilterProxy 查找 IOC 容器然后查找 bean 的工作机制:
ContextLoaderListener 初始化后,springSecurityFilterChain就在 ContextLoaderListener创建的 IOC 容器中查找所需要的 bean,但是我们没有在 ContextLoaderListener 的 IOC 容器中扫描 SpringSecurity 的配置类,所以 springSecurityFilterChain 对应的 bean 找不到。
③.问题解决:把两个 IOC 容器合二为一
将 ContextLoaderListener 取消,原本由 ContextLoaderListener 读取的 Spring 配置文件交给 DispatcherServlet 负责读取。
- DelegatingFilterProxy 在初始化时查找 IOC 容器,找不到,放弃。
- 第一次请求时再次查找。
- 找到 SpringMVC 的 IOC 容器。
- 从这个 IOC 容器中找到所需要的 bean。
遗憾的是:会破坏现有程序的结构。原本是 ContextLoaderListener 和 DispatcherServlet
两个组件创建两个 IOC 容器,现在改成只有一个。还有一种解决方案是:改源码。
修改 DelegatingFilterProxy 的源码,修改两处:
①.初始化时直接跳过查找 IOC 容器的环节
②.第一次请求的时候直接找 SpringMVC 的 的 IOC 容器
1.6 SpringSecurity 初始设置
2.登录
2.1 SpringSecurity 开启表单登录功能并前往登录表单页面
①.登录表单
②.security设置
③.指定正确的账号和正确的密码(假数据)
④. 取消以前的自定义登录拦截器
2.2退出登录
2.3把内存登录(假数据)改成数据库登录
①.思路
②.代码体现
操作1:根据账号查询 Admin对象
操作2:根据 adminId 查询已分配的角色
操作3:根据 adminId 查询已分配权限
操作4 :创建 SecurityAdmin 类
操作5:MyUserDetailsService的完整代码
操作6:配置类CrowdfundingSecurityConfig中引入 UserDetailsService
操作7:密码加密
①MD5加密
②带盐值的加密
I.准备BCryptPasswordEncoder对象,放到IOC容器里面
操作8:使用BCryptPasswordEncoder在保存admin时加密
操作9:在页面上显示用户昵称
SpringSecurity处理完登录操作之后把登陆成功的User对象以principal属性名存入了UsernamePasswordAuthenticationToken对象。
2.4权限控制
操作1:设置测试数据(更改数据库)
操作2:给资源上锁
①.访问 Admin 分页功能时具备“经理”角色
效果:adminOperator可以访问,roleOperator不可以访问
②.访问 Role 分页功能时具备部长的角色
另一种方法:使用注解@PreAuthorize("hasRole('部长')")
注解生效的前提条件: 需要在配置类上加注解@EnableGlobalMethodSecurity(prePostEnabled = true),该注解表示启用全局方法权限控制功能,并且设置 prePostEnabled = true。保证@PreAuthority、@PostAuthority、@PreFilter、@PostFilter 生效
③.访问拒绝后的处理
这个结果为什么没有经过异常映射机制?
所以要在 SpringSecurity 的配置类中进行配置
④.访问 Admin 保存功能时具备 user:save 权限
⑤.访问 Admin 分页功能时具备“经理”角色或“user:get”权限二者之一
其他注解(了解)
@PostAuthorize:先执行方法然后根据方法返回值判断是否具备权限。
例如:查询一个 Admin 对象,在@PostAuthorize 注解中和当前登录的 Admin 对象进行比较,如果不一致,则判断为不能访问。实现“只能查自己”效果。@PostAuthorize("returnObject.data.loginAcct == principal.username")使用 returnObject 获取到方法返回值,使用 principal 获取到当前登录用户的主体对象
@PreFilter:在方法执行前对传入的参数进行过滤。只能对集合类型的数据进行过滤。
@PostFilter:在方法执行后对方法返回值进行过滤。只能对集合类型的数据进行过滤。
⑥. 页面元素的权限控制
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ri-ji/75587.html