之前写过一篇 大话权限中心的PHP架构之道 ,主要是从软件工程角度介绍,如何通过 编码规范、依赖管理、数据源架构、事务处理、单元测试 等技术,来保障权限系统的高可用,并未真正的涉及这套系统的架构。
今天准备从 设计细节 上分享一二。望各位看官,心有“空杯”,带着“问题”一探究竟。
这里还是尤为的重要,因为他是整套系统设计的 根基 。
所以残忍的从上一篇中复制了一遍。。。
RBAC认为权限授权实际上是Who、What、How的问题。在RBAC模型中,who、what、how构成了访问权限三元组,也就是“Who对What(Which)进行How的操作”。
Who:权限的拥用者或主体(如Principal、User、Group、Role、Actor等等)
What:权限针对的对象或资源(Resource、Class)。
How:具体的权限(Privilege,正向授权与负向授权)。
Operator:操作。表明对What的How操作。也就是Privilege+Resource
Role:角色,一定数量的权限的集合。权限分配的单位与载体,目的是隔离User与Privilege的逻辑关系.
权限系统的本质就是这个模型(个人观点勿喷)。我们在此核心思想上,抽象了两个新概念。
下面将从以下4个层面展开介绍。
应用建模
业务场景
权限管理
鉴权设计
系统架构上支撑权限系统灵活配置,不僵硬字段,不僵硬行为,基于各种业务权限管控的特征灵活设计。
公式一枚: 应用=资源+行为+角色
简单点说,就是 要管控资源的属性和行为 。
比如要管控菜单,属性就包括 菜单ID、菜单名称、菜单URL、菜单ICON、上级菜单ID 等,行为就包括 访问、授权 等。(菜单权限)
比如要管控城市,属性就包括 区域ID、区域名称、区域代码、所属区域ID 等,行为就包括 访问、授权 等。(区域权限)
比如要管控CMS,字段就包含 页面ID、页面名称 等,行为就包括 访问、授权、编辑、重置 等。(CMS数据权限)
按照惯性的思维,这里就是 3张资源表对应角色表,同时还有3张资源角色关系表 ,换而言之,就是有多少资源需要管控就有多少张表(又是个人观点,勿喷)。
惯性的思维, 两个痛点 。
每一种资源都要独立存储。
每一种资源都要有一套Form排版&交互&存储实现。
为了解决这个问题,我们做了以下的设计。
任意资源的字段,都可以抽象成如下几类。
资源映射字段(mapField)
资源节点字段(treeFiled)
资源系统字段(systemFields)
资源表单字段(columns)
资源行为字段(privileges)
举个栗子:菜单资源。
class menu extends app { public static $appMapField = 'url'; public static $appTreeField = 'name'; public static $columns = array( array( 'id' => 'id', 'label' => '菜单ID', 'type' => 'input', 'format' => 'int', 'option' => array() ), array( 'id' => 'name', 'label' => '菜单名称', 'type' => 'input', 'format' => 'string', 'option' => array() ), array( 'id' => 'icon', 'label' => '菜单ICON', 'type' => 'input', 'format' => 'string', 'option' => array() ), array( 'id' => 'url', 'label' => '菜单链接', 'type' => 'input', 'format' => 'string', 'option' => array() ), array( 'id' => 'parent_id', 'label' => '上级菜单ID', 'type' => 'input', 'format' => 'int', 'option' => array() ), ); public static $privileges = array( array( 'id' => 'access', 'name' => '访问' ), array( 'id' => 'op', 'name' => '授权' ) ); public static $systemFields = array('id', 'parent_id'); }
表单在网页中主要负责数据采集功能。一个表单有三个基本组成部分:
表单标签;
表单域,包含了文本框、密码框、隐藏域、多行文本框、复选框、单选框、下拉选择框和文件上传框等;
表单按钮。
设计动态表单模型,基本的思路应该是 数据和表现显示的分离 。抛开表现层,一个表单包含的若干个字段和填写的数据。所谓动态,就是这些字段名称可能改变,数量可能有增减。
通过 资源的columns字段 控制表现层。
通过 资源的数据字段 控制数据层。
通过这种模式,任意资源的 Form排版&交互&存储 都动态实现了。
资源表对应的Schema如下:
字段名称 | 字段备注 |
---|---|
应用ID | / |
资源ID | / |
资源父ID | / |
资源Data | JSON |
通过 反射API ,获取当前应用的资源存储插件(资源Data字段)。
app::find($params['app_id'])->getResoucePlugin()->save($params['data']);
通过 资源基类 做数据存储的统一封装。
public function save($attributes = array()) { $this->beforeSave($attributes); $json = array(); foreach (static::$columns as $column) { if (in_array($column['id'], static::$systemFields)) continue; if (isset($attributes[$column['id']])) { $json[$column['id']] = $column['format'] == 'int' ? intval($attributes[$column['id']]) : $attributes[$column['id']]; } } return $json; } protected function beforeSave($attributes = array()) { if (!isset($attributes[static::$appTreeField]) || !$attributes[static::$appTreeField]) { throw new /Exception('资源数据保存失败,请录入资源名称', '900404'); } }
很显然 资源Data 字段,不能很好的完成 单资源行为鉴权 的任务。
设计上引入了资源 映射字段 ,每当资源变更时,触发 addMap
,会 同步字段信息 到资源映射关系表中(资源的语义化索引)。
举几个栗子,方便大家理解这个字段:
比如菜单类应用,映射字段就是菜单链接。
比如数据类应用,映射字段就是数据ID。
public function addMap($mapFiledValue = null) { if ($this->map) { if ($this->map->f_value !== $mapFiledValue) { $this->map->f_value = $mapFiledValue; $this->map->save(); } } else { $this->create_map( array( 'f_app_id' => $this->f_app_id, 'f_value' => $mapFiledValue ) ); } }
应用建模,相对来说是一个较技术的话题,换个方向,下面来和大家聊一聊系统的业务场景。
系统架构上支撑多种业务形态的权限管控。
说到权限,大多数应用场景都会想到菜单权限,然而我们也不例外,菜单类的应用,就是生产的第一个实例。
举个栗子:A系统/菜单权限应用
字段:菜单ID、菜单名称、菜单链接、菜单上级ID
行为:访问、授权
数据类应用,相对于菜单类型来说,更关注的是, 对某一行数据的行为控制 ,行为是多样的。
举个栗子:A数据/数据权限应用
字段:数据ID、数据名称
行为:访问、授权、编辑、删除、新增、重置、导出、导入
表单类应用,在2B的系统中,权限设计尤为重要。
举个栗子:A确认单/表单权限应用
字段:模块ID、模块名称(行程模块、供应商模块、订单模块、财务模块)
行为:访问、授权、只读、可写、隐藏
总结一下权限的业务场景:
菜单类:单资源的单行为(能否”行为“当前“资源”)
数据类:单资源的多行为
表单类:多资源的多行为
小伙伴们,你们的业务场景能实现吗?(留言吧)
接下来说说权限管理中的一些设计之道。
设计上参考JIRA, 当前应用的管理者主导当前应用的权限生态 。
资源的增删改查
角色的增删改查
资源字段定义
资源行为定义
资源行为的角色赋予
系统中给每一种资源都天生赋予一种系统行为叫做“ 授权 ”。
任意资源节点的角色行为赋予,都由拥有该资源节点 授权 行为的角色去分配。
两个维度授权,方便检索,也方便配置。
基于角色+行为,选择资源。
基于资源+行为,选择角色。
^^^^^^^^
下一个阶段将会支持 默认权限 的配置,非应用内的角色,也同样可以享有应用内的部分资源的部分行为的权限。
最后简单说下,鉴权接口的设计。
U、R、P、S分别表示用户集合、角色集合、许可权集合和会话集合。
PA P×R表示许可权与角色之间多对多的指派关系。
UA U×R表示用户与角色之间多对多的指派关系。
公式一枚: S=UA ∩ PA
统称为 资源树 接口。
举个栗子:R角色在A应用中拥有P行为的资源。
统称为 行为鉴权 接口。
举个栗子:R角色在A应用中是否拥有R资源的P行为。
权限系统一直以来是我们应用系统不可缺少的一个部分,若每个应用系统都重新对系统的权限进行设计,以满足不同系统用户的需求,将会浪费我们不少宝贵时间,所以花时间来设计一个相对通用的权限系统是很有意义的。
设计一个相对通用的系统是很有意义的。(不仅仅是权限)
多交流,多分享,书写更好的代码。
享受编程和技术所带来的快乐。