大话设计,没有模式—通用权限设计与实现

当代码写多了,总有些是经验,但经验是什么呢?if…else用的次数比别人多?显然不是。有些超棒的设计可以谓之经验!

功能权限
网络上流行的经典的权限设计是【主体】- 【领域】 - 【权限】( who、what、how问题原型 ) 的设计思想,其中:

【主体】可以是用户,可以是角色,也可以是一个部门

【领域】可以是一个模块,可以是一个页面,也可以是页面上的按钮

【权限】可以是“可见”,可以是“只读”,也可以是“可用”(如按钮可以点击)

为了简化程序开发,在OpenAuth.Core中去掉了权限的控制,简化为【主体】- 【领域】的模式,且【主体】限定为角色。即只能给角色分配模块和按钮,不能直接分配给用户账号或部门。且一旦分配即表示该角色拥有操作该模块(按钮)的权限。

大道至简,标准的RBAC规范比这个还要简洁,所以它的生命力才最顽强。在此基础上,如果进行二次开发,进行更详细的功能权限控制,可以按照上面的思想进行改造。

数据权限
数据权限是在功能权限的基础上面进一步的扩展,比如可以查看订单属于【功能权限】的范围,但是可以查看哪些订单就是【数据权限】的工作了。

这里面的几个概念:

【资源】:数据权限的控制对象,业务系统中的各种资源。比如订单单据、销售单等。

【数据规则】:用于数据权限的条件规则 。

应用场景

销售单,可以由本人查看
销售单,测试角色能看到自己的订单
销售单,测试角色能看到自己的订单,管理员角色可以看到所有订单
销售单,测试角色能看到自己的订单,管理员角色可以看到所有订单,账号test3可以看到应用名称为"xxx管理系统"的订单
我们能想到直接的方法,在访问数据的入口加入SQL Where条件来实现,以上4种情况对应的sql语句:

 1 where CreateUserID = {loginUser}
 2 where CreateUserID = {loginUser} and {loginRole} in (测试)
 3 where CreateUserID = ({loginUser} and {loginRole} in (测试)) or {loginRole} in (管理员)
 4 where CreateUserID = ({loginUser} and {loginRole} in (测试)) or {loginRole} in (管理员)or ({loginUser}==test3 and 应用名称==XXX管理系统)

这些一个一个的"条件",简单理解为一个【数据规则】,通常会与原来我们前台的业务过滤条件合并再检索出数据。

每一个角色有不一样的【数据规则】,那么数据权限设计就变成

【资源】 - 【数据规则】

在数据库记录中存放为资源ID+规则的形式,如下:

为了简化程序开发,在开发过程中可以做出以下约定:

所有需要进行数据权限控制功能的表,在设计时必须包含字段CreateUserId(创建用户Id)。
【资源】的名称限定为模块名称。如部门管理,用户管理,那么资源就是对应的部门列表,用户列表。
如果【资源】没有设置数据规则,那么视为该资源允许被任何主体查看。
数据规则中授权的对象限定为角色、用户。即不能设定为某个部门所有,如果想实现类似的功能,通过角色间接实现。例如:资源属于角色“开发组成员”,“开发组组长”。

核心实现--查询对象模式
权限控制总离不开一些条件的限制,如果没有完善的查询机制,那么在做权限条件过滤的时候你会觉得很别扭。马丁在《企业应用架构模式》第13章对象-关系元数据映射模式中提出查询对象模式(Query Object Pattern)。该模式核心结构如下:

该结构为【数据规则】的建立提供了理论基础。在设计数据权限的时候,可以照搬该思想。上面例子中的规则,数据规则展开如下:

 1 {
 2  "Operation": "or",
 3  "Filters": [{
 4      "Key": "{loginRole}",
 5      "Value": "09ee2ffa-7463-4938-ae0b-1cb4e80c7c13",
 6      "Contrast": "contains",
 7      "Text": "管理员"
 8  }],
 9  "Children": [{
 10         "Operation": "and",
 11         "Filters": [{
 12             "Key": "{loginRole}",
 13             "Value": "0a7ebd0c-78d6-4fbc-8fbe-6fc25c3a932d",
 14             "Contrast": "contains",
 15             "Text": "测试"
 16         }, {
 17             "Key": "CreateUserId",
 18             "Value": "{loginUser}",
 19             "Contrast": "==",
 20             "Text": ""
 21         }]
 22     }, {
 23         "Operation": "and",
 24         "Filters": [{
 25             "Key": "AppName",
 26             "Value": "XXX管理平台",
 27             "Contrast": "==",
 28             "Text": ""
 29         }, {
 30             "Key": "{loginUser}",
 31             "Value": "229f3a49-ab27-49ce-b383-9f10ca23a9d5,1df68dfd-3b6d-4491-872f-00a0fc6c5a64",
 32             "Contrast": "in",
 33             "Text": "test3,test4"
 34         }]
 35     }]
 36 }

规则分为三个部分:【分组】(Children)、【规则】(Filter)、【操作符】(and or),而规则自身就是一个分组。这种简单的结构就可以满足全部的情况。看不懂啥意思?

这样理解起来就比较简单了。

还不懂??我们从简单的开始:

某一角色只能看到自己创建的信息。如:针对资源列表,我们设置了【测试】角色可以看到自己创建的资源。

这时如果用一个拥有测试角色的账号(test)登录,那么他只能看到自己创建的资源:

其他角色的账号登录时,会出现无法看到任何信息。我们稍做调整:【管理员】角色可以看到所有资源,【测试】角色只能看到自己创建的资源。

这时如果用一个拥有管理员角色的账号(admin)登录,那么他就可以看到全部资源:

以此为基础,我们可以扩展非常复杂的数据权限控制。最终形成最后的复杂规则:【管理员】角色可以看到所有资源,【测试】角色只能看到自己创建的资源。账号test3/test4只能看到应用名称等于“XXX管理平台”的资源。

代码解析--不要BB,秀出你的代码
可以在应用层基类BaseApp中,定义一个通用函数,根据当前即将访问的资源Id获取相应的访问【数据规则】,并把当前登录的用户信息注入到该规则中,最终转换成针对数据库的查询,如下:(不想看这个?直接跳过吧,看看如何使用也没有问题)

 1 protected IQueryable<T> GetDataPrivilege(string parametername)
 2 {
 3 var loginUser = _auth.GetCurrentUser();
 4 if (loginUser.User.Account == Define.SYSTEM_USERNAME) return UnitWork.Find<T>(null); //超级管理员特权
 5
 6 var moduleName = typeof(T).Name;
 7 var rule = UnitWork.FindSingle<DataPrivilegeRule>(u => u.SourceCode == moduleName);
 8 if (rule == null) return UnitWork.Find<T>(null); //没有设置数据规则,那么视为该资源允许被任何主体查看
 9 if (rule.PrivilegeRules.Contains(Define.DATAPRIVILEGE_LOGINUSER) ||
 10 rule.PrivilegeRules.Contains(Define.DATAPRIVILEGE_LOGINROLE))
 11 {
 12
 13 //即把{loginUser} ==‘xxxxxxx‘换为 loginUser.User.Id ==‘xxxxxxx‘,从而把当前登录的用户名与当时设计规则时选定的用户id对比
 14 rule.PrivilegeRules = rule.PrivilegeRules.Replace(Define.DATAPRIVILEGE_LOGINUSER, loginUser.User.Id);
 15 var roles = loginUser.Roles.Select(u => u.Id).ToList();
 16 roles.Sort(); //按字母排序,这样可以进行like操作
 17 rule.PrivilegeRules = rule.PrivilegeRules.Replace(Define.DATAPRIVILEGE_LOGINROLE,
 18 string.Join(‘,‘,roles));
 19 }
 20 return UnitWork.Find<T>(null).GenerateFilter(parametername,
 21 JsonHelper.Instance.Deserialize<FilterGroup>(rule.PrivilegeRules));
 22 }
这样在我们自己的应用中,使用上面的函数创建一个基础查询,再加上自定义的查询条件即可轻松实现数据权限的控制。

 1 var result = new TableData();
 2 var objs = GetDataPrivilege("u");
 3 if (!string.IsNullOrEmpty(request.key))
 4 {
 5 objs = objs.Where(u => u.Id.Contains(request.key));
 6 }
 7
 8 result.data = objs.OrderBy(u => u.Id)
 9 .Skip((request.page - 1) * request.limit)
 10 .Take(request.limit);

最后
以上所有代码均已实现并开源(配置数据规则的界面可以自己画,layui的前端画起来实在太费力),OpenAuth.Core秉承代码之美,为.net core添砖加瓦,喜欢的star一下!

觉得不错的话点个关注哦

原文地址:https://blog.51cto.com/14528283/2454558

时间: 2024-07-29 11:54:41

大话设计,没有模式—通用权限设计与实现的相关文章

通用权限设计(springmvc + mybatis + ligerui)

最近一直在思考,如何设计一个毕业论文呢?后台就回想起自己以前大学做的项目,每次都需要开发一个后台网站,及其繁琐,后来就慢慢有了个想法,开发一个网站固件,可以动态配置,于是就动手设计了起来... 通用权限设计 每一个网站,都有着相似的后台界面,现在比较流行的DWZ.LIGERUI.EXTJS等js框架,都提供了相似的界面.同时,后台网站的安全也是非常重要的.那么,为了开发上的方便,很有必要将一个后台网站做成一个通用的网站固件.这样子带来的好处:可以动态配置后台网站,极大简便了重复造轮子的功夫. 主

通用权限设计

1.  可以跟角色给用户权限,用户权限字段存的数据格式为:ID_菜单英文名称; ID_菜单英文名称;……; ID_功能英文名称;……. 2.  用户登录时,权限存在session里: 3.  初始化菜单,判断用户权限是否存在该菜单,如果存在就出现,不存在就隐藏. 4.  页面功能java在拦截器了判断权限,PHP在同一入口了拦截,.net就在页面的基类里判断或者在Page_Init里设置控件隐藏或显示. 通用权限设计

多租户通用权限设计(基于casbin)

多租户通用权限设计(基于 casbin) 所谓权限控制, 概念并不复杂, 就是确认某个操作是否能做, 本质上仅仅就是个bool判断. 权限几乎是每个系统必不可少的功能, 和具体业务结合之后, 在系统中往往表现的非常复杂和难于控制, 很大部分原因是把权限和具体业务结合的太过紧密, 把业务的复杂度也加入到权限控制中来了. 一直以来, 都有个想法, 想做一套简单好用的通用权限系统, 和任何业务都没有关系, 仅仅就是权限本身的功能. 对此, 做过很多尝试, 由于设计能力有限, 最后都不了了之, 没能坚持

.net通用权限设计源码

经过一年多的设计编码和测试,公司通用权限系统对业务部门提供了比较稳定.便捷和灵活的支撑. 该系统分为两部分:配置界面和WCF服务. 界面:权限系统管理员可以通过配置界面的方式对各部门或者组织机构的人员进行业务权限分配操作. WCF服务:业务开发人员可以通过权限系统提供的接口服务,获取业务权限数据,对业务系统进行开发. 该设计的好处就是权限系统的可移植性更高,权限系统可以单独部署,发布对外的服务接口即可,和业务系统之间耦合性更低,从而达到设计模式的核心价值高内聚,低耦合. IIS部署示例如下: 权

一个基于RBAC0的通用权限设计清单

注:RBAC0与RBAC1不同在于权限继承.关于RBAC1的权限设计,敬请关注作者后续CSDN博客.1,用户表 保存系统用户信息,如张三.李四,字段可以有id.name.fullname.email.phone.……2,角色表 保存角色信息,如学生.管理员,字段有id.name.……3,权限表 保存系统的权限信息,可定义系统哪些模块公开,或者什么时段可访问,字段有id,权限名4,用户角色表 关联用户和角色的关系表,如张三-学生,李四-管理员,字段有id.用户id.角色id,根据用户就知道所属的角

一个基于RBAC的通用权限设计清单

RBAC即角色访问控制(Role Based Access Control) RBAC认为权限授权实际上是Who.What.How的问题.在RBAC模型中,who.what.how构成了访问权限三元组,也就是"Who对What(Which)进行How的操作".Who:权限的拥用者或主体(如Principal.User.Group.Role.Actor等等)What:权限针对的对象或资源(Resource.Class).How:具体的权限(Privilege,正向授权与负向授权). 基于

【数据库设计-2】权限设计-系统登录用户权限设计

需求分析---场景 假设需要为公司设计一个人员管理系统,并为各级领导及全体员工分配系统登录账号.有如下几个要求: 1. 权限等级不同:公司领导登录后可查看所有员工信息,部门领导登录后只可查看本部门员工的信息,员工登录后只可查看自己的信息: 2. 访问权限不同:如公司领导登录后,可查看员工薪水分布界面,而员工则不能看到: 3. 操作权限不同:如系统管理员可以在信息发布界面进行增删改查发布信息,而普通员工只可以在信息发布界面进行查看,不能修改.删除和新增. 功能分析 1. 登录一个系统,基本都需要用

权限设计-系统登录用户权限设计

需求分析-场景 假设需要为公司设计一个人员管理系统,并为各级领导及全体员工分配系统登录账号.有如下几个要求: 1. 权限等级不同:公司领导登录后可查看所有员工信息,部门领导登录后只可查看本部门员工的信息,员工登录后只可查看自己的信息: 2. 访问权限不同:如公司领导登录后,可查看员工薪水分布界面,而员工则不能看到: 3. 操作权限不同:如系统管理员可以在信息发布界面进行增删改查发布信息,而普通员工只可以在信息发布界面进行查看,不能修改.删除和新增. 功能分析 1. 登录一个系统,基本都需要用户输

通用权限管理设计

权限设计(初稿)     1. 前言:     权限管理往往是一个极其复杂的问题,但也可简单表述为这样的逻辑表达式:判断"Who对What(Which)进行How的操作"的逻辑表达式是否为真.针对不同的应用,需要根据项目的实际情况和具体架构,在维护性.灵活性.完整性等N多个方案之间比较权衡,选择符合的方案.     2. 目标:     直观,因为系统最终会由最终用户来维护,权限分配的直观和容易理解,显得比较重要简单,包括概念数量上的简单和意义上的简单还有功能上的简单.想用一个权限系统