权限部分将分两章介绍,第一章由浅入深介绍权限理论知识及应用,第二章介绍具体实现。后期再讲述中间件的使用时,还会插入一些权限内容,本质上属于中间件的应用。

权限模块是业务系统最常见、最基本的子集。本章假定了一个系统从最初简单的需求到逐渐成熟且完善的权限体系的实现过程。

阅读本章预计花费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 角色名称

基于角色的基本权限控制的原则是:

  1. 简化用户权限的操作;
  2. 权限操作的对象从用户变更为角色
  3. 不能对单一用户做权限操作,仅对角色做权限操作,每个需要权限的用户,都拥有至少一个角色;

角色与用户的抽象关系表现为M-M,这表示:

  • 一个用户可以拥有多个角色;
  • 一个角色下有多个用户;

具体到业务可以是一个人可以有多个职位;一个职位下有多个人;

针对此设计,我们需要做以下操作:

  1. 从系统中删除掉原来的Auth_UserPermission关系;
  2. 新增Auth_UserRole(UserId,RoleId)的关系;
  3. 新增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. 基于角色并含有用户组概念的权限控制

春去秋来,业务系统迎来了第二次升级机会,并包含以下新的权限需求:

所有部门的开发岗位拥有相同的增、查权限

基于第二节的系统升级,解决此需求我们会有临时的做法:做一个角色,给所有开发岗的同事赋予这个角色。

这样的临时做法的确解决了我们的问题,但这里有几个问题,函待解决:

  1. 系统没有部门的对应抽象;
  2. 一旦其中一个部门的开发岗同事拥有的权限有变动,我们需要新建角色,并重新授权;

针对此两个问题,我们引入一个新的模型:用户组(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 部门名称

基于角色并含有用户组概念的权限控制有以下特点:

  1. 再次简化了用户权限的操作;
  2. 用户可以拥有角色;用户组也可以拥有角色;
  3. 权限的操作对象依旧为角色,不可对用户、用户组进行权限操作;

用户与用户组的关系表现为多对多,这表示一个用户可以属于多个用户组,一个用户组下可以有多个用户,具体到业务可以描述为:一个人可以在多个部门,一个部门下可以有多个人;

用户组与角色的关系表现为多对多,这表示一个用户组的所有用户可以拥有相同的多个角色,一个角色下有多个用户组,具体到业务可以描述为:同一个部门的人可以拥有多个相同的职位;

为了实现此设计,我们需要做以下新的操作:

  1. 新增Auth_UserUserGroup关系;
  2. 新增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权限模型由浅入深的更多相关文章

  1. RBAC权限模型——项目实战(转)

    一.前言 权限一句话来理解就是对资源的控制,对web应用来说就是对url的控制,关于权限可以毫不客气的说几乎每个系统都会包含,只不过不同系统关于权限的应用复杂程序不一样而已,现在我们在用的权限模型基本 ...

  2. (转)RBAC权限模型——项目实战

    一.前言 权限一句话来理解就是对资源的控制,对web应用来说就是对url的控制,关于权限可以毫不客气的说几乎每个系统都会包含,只不过不同系统关于权限的应用复杂程序不一样而已,现在我们在用的权限模型基本 ...

  3. RBAC权限模型及数据权限扩展的实践

    话说大家对RBAC权限模型应该是耳熟能详了.但真正用的好的并不多.并且原始的RBAC模型并不包括数据权限的管理,网上也差点儿没有相关的文章可以參考.本人经过几个项目的实战,在其基础上扩展出一套可行的. ...

  4. RBAC权限模型——项目实战

    RBAC权限模型——项目实战

  5. JWT与RBAC权限模型

    JWT JWT是什么? Json web token (JWT)是为了网络应用环境间传递声明而执行的一种基于JSON的开发标准(RFC7519),该token被设计为紧凑且安全的,特别适用于分布式站点 ...

  6. RBAC 权限模型

    RBAC 0 模型 最基本的 MySQL 脚本,没有建立外键约束. /* Navicat Premium Data Transfer Source Server Type : MySQL Source ...

  7. 基于角色访问控制RBAC权限模型的动态资源访问权限管理实现

    RBAC权限模型(Role-Based Access Control) 前面主要介绍了元数据管理和业务数据的处理,通常一个系统都会有多个用户,不同用户具有不同的权限,本文主要介绍基于RBAC动态权限管 ...

  8. 权限管理系统(四):RBAC权限模型分类介绍

    RBAC是Role-BasedAccess Control的英文缩写,意思是基于角色的访问控制.RBAC认为权限授权实际上是Who.What.How的问题.在RBAC模型中,who.what.how构 ...

  9. .Net Core微服务系列--理论篇

    微服务的由来 微服务最早由Martin Fowler与James Lewis于2014年共同提出来的,但是微服务也不是一个全新的概念,它是由一系列在实践中获得成功并流行起来的概念中总结出来的一种模式, ...

随机推荐

  1. charles 客户端进程

    本文参考:charles 客户端进程 客户端进程工具/client_process 显示使每个请求的本地客户端进程; 客户端进程工具显示负责进行每个请求的本地客户端进程的名称. 客户端进程通常是您的W ...

  2. 详细的App推广前的准备工作

    App开发完成后,推广App自然就成为下一步工作的重点.兵马未动,粮草先行,这里为大家整理了一份App推广前需要准备一些事项,希望能给正在准备开展App推广的小伙伴们一些帮助. 众所周知,App推广的 ...

  3. (七十二)c#Winform自定义控件-雷达图

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:ht ...

  4. Windows(Win7)搭建RabbitMQ服务器

    首先安装Erlang环境,RabbitMQ的运行依赖于Erlang.可以在官网链接http://www.erlang.org/downloads 页面找到对应的开发环境安装包.例如64位Windows ...

  5. 最新2019Pycharm破解教程,附激活码!

    本教程仅用作个人学习,请勿用于商业获利,造成后果自负!!! Pycharm安装 在这插一个小话题哈,Pycharm只是一个编译器,并不能代替Python,如果要使用Python,还是需要安装Pytho ...

  6. html常见的块元素与内联(行内)元素用法说明(一)

    html平时常见的块元素有:div, p, h1, h2, h3等,内联元素有:span, a, img等. 块元素的属性:无论内容是什么,都会独占一整行.主要用于页面布局. 内联元素的属性:只占自身 ...

  7. 使用Storm实现累加求和操作

    package com.csylh; import org.apache.storm.Config; import org.apache.storm.LocalCluster; import org. ...

  8. Spring MVC-从零开始-@RequestMapping结合@RequestParam (从HTTP键值对中取值,作用于函数参数)

    1.@RequestParam 注解使用的时候可以有一个值,也可以没有值:如果请求参数和处理方法参数的名称一样的话,@RequestParam 注解的 value 这个参数就可省掉了:@Request ...

  9. Linux 安装CentOS7操作系统

    作者:小啊博 QQ:762641008 转载请声明:https://www.cnblogs.com/-bobo 1.安装操作系统 开启虚拟机后会出现以下界面 Install CentOS 7 安装Ce ...

  10. 使用.net core3.0 正式版创建Winform程序

    前阵子一直期待.net core3.0正式版本的出来,以为这个版本出来,Winform程序又迎来一次新生了,不过9.23日出来的马上下载更新VS,创建新的.net core Winform项目,发现并 ...