[七年技术总结系列][理论篇]-RBAC权限模型由浅入深
权限部分将分两章介绍,第一章由浅入深介绍权限理论知识及应用,第二章介绍具体实现。后期再讲述中间件的使用时,还会插入一些权限内容,本质上属于中间件的应用。
权限模块是业务系统最常见、最基本的子集。本章假定了一个系统从最初简单的需求到逐渐成熟且完善的权限体系的实现过程。
阅读本章预计花费20分钟。
1. 最简单的权限模型
业务系统初期,需求简单,对于权限的内容本身并不复杂,我们假设权限部分仅有这样简单的需求:
能给用户赋予数据的增、删、改、查四种权限
分析此需求,权限的主体为用户,权限的内容有多种,关系为M - M,具体为:
用户模型:
public class User
{
public int UserId{ get; set; }
public string UserName { get; set; }
}
用户表Auth_User
| 字段 | 类型 | 说明 |
|---|---|---|
| *UserId | int | 用户ID |
| UserName | varchar | 用户名 |
| ... | ... | ... |
权限枚举:
[Flags]
public enum Permission
{
Add = 1,
Update = 2,
Delete = 4,
Select = 8
}
权限表 Auth_Permission
| 字段 | 类型 | 说明 |
|---|---|---|
| *PermissionId | int | 权限ID |
| Permission | varchar | 权限内容 |
用户-权限关系表 Auth_UserPermission
| 字段 | 类型 | 说明 |
|---|---|---|
| *UserId | int | 用户ID |
| *PermissionId | int | 权限ID |
假如一个用户有增、改两种权限,那么关系表(Auth_UserPermission)可以存储为:
| UserId | Permission |
|---|---|
| 1 | 1 |
| 1 | 2 |
于是对于权限的基本操作我们可以进行归纳:
- 授权:INSERT 权限表 (用户ID,权限的具体值)
- 校权:EXISTS 权限表 UserID==用户ID AND Permission==要判断权限的具体值
我们留意到对于Permission的枚举定义,值使用了对2的幂运算的值:
| 幂运算 | 十进制 | 二进制 | 十六进制 |
|---|---|---|---|
| 2^0 | 1 | 0001 | 0x01 |
| 2^1 | 2 | 0010 | 0x02 |
| 2^2 | 4 | 0100 | 0x04 |
| 2^3 | 8 | 1000 | 0x08 |
这么定义是有好处的,对于Auth_UserPermission的表存储可以节省存储空间,并且程序便于处理,譬如:
如果UserId=1的用户拥有Add、Select权限,Auth_UserPermission表原本应该存储两条记录:
- (1,1)
- (1,8)
现在,可以考虑更简单的存储方式
- (1,9)
这表示:
Permission.Add | Permission.Select
等价于
1 按位或 8 ( 1 | 8 )
等价于
9
而对于权限的判断,则使用存储的权限值按位与要进行校权的值是否等于要进行校权的值来判断
譬如判断用户是否拥有Delete权限,则使用9按位与4是否等于4来进行判断,用C#的三目运算来表示为:
9 & 4 == 4 ? "有权限":"无权限"
这样被标记有Flags特性的枚举在.Net框架中遍布各种基础类库,譬如反射中的BindFlags枚举。本身属于基础知识,由于不常应用所以容易被忽视,在权限中属于应用小技巧。还有人质疑这么存储会有性能问题,在后面章节讲到优化时,再行讨论。
于是我们对使用了小技巧的新的权限基本控制再次进行归纳:
- 授权:INSERT 权限表(用户ID,所有拥有权限的按位或值)
- 校权:EXISTS 权限表(UserID == 用户ID AND Permission & 要判断权限的具体值 == 要判断权限的具体值)
2. 基于角色的基本权限控制
随着业务系统的发展,业务系统有了第一次升级机会,并附带了一个新的权限需求:
系统需要满足一类职位的人拥有相同的权限
按照第一节的内容,这个需求其实不用做任何变化一样可以满足,但是问题在于负责授权的人“太累了”,对于每一个用户,我们可能都要做一遍授权的操作。
为了解决这个问题,我们引入角色这一基本单元,角色是一种抽象,可以具体到业务场景的类似职位、身份等概念。
角色模型设计:
public class Role
{
public int RoleId { get;set; }
public string RoleName { get;set; }
}
角色表设计Auth_Role:
| 字段 | 类型 | 说明 |
|---|---|---|
| *RoleId | int | 角色ID |
| RoleName | varchar | 角色名称 |
基于角色的基本权限控制的原则是:
- 简化用户权限的操作;
- 权限操作的对象从用户变更为角色;
- 不能对单一用户做权限操作,仅对角色做权限操作,每个需要权限的用户,都拥有至少一个角色;
角色与用户的抽象关系表现为M-M,这表示:
- 一个用户可以拥有多个角色;
- 一个角色下有多个用户;
具体到业务可以是一个人可以有多个职位;一个职位下有多个人;
针对此设计,我们需要做以下操作:
- 从系统中删除掉原来的Auth_UserPermission关系;
- 新增Auth_UserRole(UserId,RoleId)的关系;
- 新增Auth_RolePermission(RoleId,Permission)的关系;
假定业务系统有这样的职位列表:
| RoleId | RoleName |
|---|---|
| 1 | 总裁 |
| 2 | 开发总监 |
假设用户ID等于1001的用户职位为总裁兼开发总监,那么关系表Auth_UserRole可以存储为:
| UserId | RoleId |
|---|---|
| 1001 | 1 |
| 1001 | 2 |
业务约定:总裁有增、删、改、查四个权限,开发总监则有增、查两个权限,那么关系表Auth_RolePermission可以存储为:
| RoleId | Permission |
|---|---|
| 1 | 15( = 1 | 2 | 4 | 8 ) |
| 2 | 9 |
我们对给予角色的基本权限控制操作再次归纳为:
- 授权:给角色添加权限(INSERT Auth_RolePermission),给用户添加角色(INSERT Auth_UserRole)
- 校权:应当是拿出用户所有的角色,并再次拿出这些角色的权限做并集,并DISTINCT 权限并集为权限集合,判断权限集合是否含有需要校权的权限
3. 基于角色并含有用户组概念的权限控制
春去秋来,业务系统迎来了第二次升级机会,并包含以下新的权限需求:
所有部门的开发岗位拥有相同的增、查权限
基于第二节的系统升级,解决此需求我们会有临时的做法:做一个角色,给所有开发岗的同事赋予这个角色。
这样的临时做法的确解决了我们的问题,但这里有几个问题,函待解决:
- 系统没有部门的对应抽象;
- 一旦其中一个部门的开发岗同事拥有的权限有变动,我们需要新建角色,并重新授权;
针对此两个问题,我们引入一个新的模型:用户组(UserGroup),用户组的概念在业务系统中,可以具体为:部门、小组、团队等
用户组模型设计:
public class UserGroup
{
public int UserGroupId { get; set; }
public int ParentId { get;set; } //留意此字段,将在本节末尾阐述
public string UserGroupName { get; set; }
}
用户组表Auth_UserGroup设计:
| 字段 | 类型 | 说明 |
|---|---|---|
| *UserGroupId | int | 部门ID |
| ParentId | int | 上级部门ID |
| UserGroupName | varchar | 部门名称 |
基于角色并含有用户组概念的权限控制有以下特点:
- 再次简化了用户权限的操作;
- 用户可以拥有角色;用户组也可以拥有角色;
- 权限的操作对象依旧为角色,不可对用户、用户组进行权限操作;
用户与用户组的关系表现为多对多,这表示一个用户可以属于多个用户组,一个用户组下可以有多个用户,具体到业务可以描述为:一个人可以在多个部门,一个部门下可以有多个人;
用户组与角色的关系表现为多对多,这表示一个用户组的所有用户可以拥有相同的多个角色,一个角色下有多个用户组,具体到业务可以描述为:同一个部门的人可以拥有多个相同的职位;
为了实现此设计,我们需要做以下新的操作:
- 新增Auth_UserUserGroup关系;
- 新增Auth_UserGroupRole关系;
假设系统拥有这样的部门列表:
| UserGroupId | UserGroupName |
|---|---|
| 1 | 总裁办 |
| 2 | 前端开发部 |
| 3 | 中台开发部 |
| 4 | 人力资源部 |
| 5 | 保安部 |
假设用户ID为1101的用户既是前端开发部的开发总监,又是中台开发部的开发总监;中台开发部、前端开发部的所有同事本质都是开发,且所有开发部的同事都有增、查的权限,那么:
用户-用户组Auth_UserUserGroup关系表可以存储为:
| UserId | UserGroupId |
|---|---|
| 1101 | 2 |
| 1101 | 3 |
新增角色:开发
| RoleId | RoleName |
|---|---|
| 6 | 开发 |
Auth_RolePermission新增记录:
| RoleId | Permission |
|---|---|
| 6 | 9 |
Auth_UserGroupRole关系表可以存储为:
| UserGroupId | RoleId |
|---|---|
| 2 | 6 |
| 3 | 6 |
这样,我们就满足了本节提出的需求。
另外要注意到的是用户组的ParentId字段,不要轻视这个简单的树状设计,实际应用中根据业务场景会有各种不同的问题,譬如不良的SQL导致DB层面做了递归查询、上级部门权限与下级部门权限的继承关系,但这本质属于业务需求,不再赘述
4. RBAC权限模型
现在,系统经过3次升级,已经有了较为完备的权限体系,我们解决了大部分问题。
但是我们也注意到,所有的有关于权限的定义仅仅围绕着增删改查这一类权限控制。假如系统现在需要多控制一部分权限内容,我们就有些捉襟见肘了。
简单来说,我们的权限模型设计对于扩展支持不够
譬如,业务系统初期对系统的菜单可见性有权限控制,随着系统迭代,可能出现对文件的可操作性也需要有权限控制,这是很正常的事,显然,依照我们的设计,系统无法满足。
回顾1、2、3节的升级内容,我们的问题其实是由单一权限元素变更为多元权限元素,如果我们能重新将被控制元素变更为单一元素,我们之前的设计则不用变更。
为了解决这个问题,我们对各种权限元素进行抽象,譬如文件访问权限和菜单访问权限。抽象为如下图内容:

现在,权限的Root节点变成了Permission这个抽象,它没有具体的意义,但他将各类权限集中在了一起,使得多种权限元素重新集中在单一Permission这个抽象元素上,再次揉入到我们的系统中,如下图:

这就是权限系统的RBAC完成模型。
至此,借助RBAC模型,我们完成了权限模块的理论设计,它能满足大量权限控制场景,也是业界惯用的手段,RBAC模型是一种权限模型的总结和归纳,市面上能见到的各种权限控制,都与RBAC沾边,也就是说,掌握RBAC,就掌握了阅读各种系统权限设计的基础,有了理论支持。
不过值得注意的是,虽然我们有了理论基础,但实际应用中,我们还要做一些扩展内容。
譬如说权限历史,权限模块属于敏感内容,是系统的中枢所在,严谨的权限模块肯定是不会对操作进行Delete的,而是Fake Delete以保留历史。上文中这样的设计为此提供了方便,当用户的权限发生变更时,我们只需要对关系做Fake Delete即可。当然,关系本身需具备IsFakeDeleted属性。
下一章节将介绍dotnet core的具体实现。
[七年技术总结系列][理论篇]-RBAC权限模型由浅入深的更多相关文章
- RBAC权限模型——项目实战(转)
一.前言 权限一句话来理解就是对资源的控制,对web应用来说就是对url的控制,关于权限可以毫不客气的说几乎每个系统都会包含,只不过不同系统关于权限的应用复杂程序不一样而已,现在我们在用的权限模型基本 ...
- (转)RBAC权限模型——项目实战
一.前言 权限一句话来理解就是对资源的控制,对web应用来说就是对url的控制,关于权限可以毫不客气的说几乎每个系统都会包含,只不过不同系统关于权限的应用复杂程序不一样而已,现在我们在用的权限模型基本 ...
- RBAC权限模型及数据权限扩展的实践
话说大家对RBAC权限模型应该是耳熟能详了.但真正用的好的并不多.并且原始的RBAC模型并不包括数据权限的管理,网上也差点儿没有相关的文章可以參考.本人经过几个项目的实战,在其基础上扩展出一套可行的. ...
- RBAC权限模型——项目实战
RBAC权限模型——项目实战
- JWT与RBAC权限模型
JWT JWT是什么? Json web token (JWT)是为了网络应用环境间传递声明而执行的一种基于JSON的开发标准(RFC7519),该token被设计为紧凑且安全的,特别适用于分布式站点 ...
- RBAC 权限模型
RBAC 0 模型 最基本的 MySQL 脚本,没有建立外键约束. /* Navicat Premium Data Transfer Source Server Type : MySQL Source ...
- 基于角色访问控制RBAC权限模型的动态资源访问权限管理实现
RBAC权限模型(Role-Based Access Control) 前面主要介绍了元数据管理和业务数据的处理,通常一个系统都会有多个用户,不同用户具有不同的权限,本文主要介绍基于RBAC动态权限管 ...
- 权限管理系统(四):RBAC权限模型分类介绍
RBAC是Role-BasedAccess Control的英文缩写,意思是基于角色的访问控制.RBAC认为权限授权实际上是Who.What.How的问题.在RBAC模型中,who.what.how构 ...
- .Net Core微服务系列--理论篇
微服务的由来 微服务最早由Martin Fowler与James Lewis于2014年共同提出来的,但是微服务也不是一个全新的概念,它是由一系列在实践中获得成功并流行起来的概念中总结出来的一种模式, ...
随机推荐
- 菜鸟 ssm 框架的学习之路
跟着老师学习了两个月的java语言,现在学习到了框架的部分,一直想在博客上写点东西的,只是自己一直没有时间,其实到底也是懒,鲁迅说过:"时间就像海绵里的水,只要愿意去挤还是有的", ...
- JNDI注入与反序列化学习总结
0x01.java RMI RMI(Remote Method Invocation)是专为Java环境设计的远程方法调用机制,远程服务器实现具体的Java方法并提供接口,客户端本地仅需根据接口类的定 ...
- tomcat下c3p0连接池配置问题
一.首先如果要使用这个连接池,就需要导入c3p0-0.9.2-pre1.jar架包和支持架包mchange-commons-0.2.jar, 我这里测试使用的是msql数据库 当然也需要导入mysql ...
- python连接数据库查询
import sqlite3 as db conn = db.connect(r'D:/data/test.db') print ('Opend database successfully \n') ...
- pycharm远程代码调试
1.pycharm连接linux服务器并上传功能 # Tools-->Deployment-->Configuration # 点击+号,选择SFTP类型,name填项目名字-->填 ...
- python习题作业合集(持续更新……)
作业: 1.简述位,字节关系 2.请写出“天才”分别用utf-8和gbk编码所占位数 3.如果有一个变量num = 14,请使用int的方法,得到改变量最少可以用多少个二进制位表示 4.写代码,有如下 ...
- 在创建activiti5..22所需的25张表时 ,所用的方法和遇到的问题。
最近在学习关于activiti流程设计的相关内容,首先第一步就需要了解25张activiti相关的表,具体的每张表的含义 请自行百度. 这里讲一下 用java代码生成所需要的25张表,很简单: pub ...
- Docker实战笔记命令篇
拉取一个镜像 docker pull ubuntu:14.04 查看系统中的镜像 docker images 运行镜像并进入 docker run -it ubuntu:14.04 查看运行的容器 d ...
- kotlin -- 可见性修饰符
puiblic Kotlin的可见修饰符与Java类似,但是默认可见性不同,Java默认包私有,kotlin默认public ### internal internal 只在模块内部可见.一个模块就是 ...
- Thinkphp5.0第三篇
批量插入数据 //新增一条数据的方法 public function add() { /*$user =new UserModel(); $user->id=1; $user->name= ...