续:关于权限系统的设计
Note:
首先,向版主致歉。这原是关于权限系统设计问题的回帖,本不应该另开新贴。而在下的另开新贴,一方面是因为本人的观点中与很多人的观点差别较大,另一方面也担心其会被“埋没”在原贴的大量回帖中。此外,本贴的内容整理与编排耗费了我周末接近一整个晚上的时间,包含了我对近期项目中权限系统的思考与总结,希望引来大家足够的目光和指导。请版主体谅。
权限系统,其重要性当然不言自明。看见大家的方案,有相当多优秀的创意与经验,我的项目也有所涉及,以下说明的是我最近在一个企业 EJB 项目中初步实现的方案(以及设计考量),望与诸位共商榷。
前言:
权限往往是一个极其复杂的问题,但也可简单表述为这样的逻辑表达式:判断“who 对 what(which) 进行 how 的操作”的逻辑表达式是否为真。
针对不同的应用,需要根据项目的实际情况和具体架构,在维护性、灵活性、完整性等N多个方案之间比较权衡,选择符合的方案。
目标:
这个权限系统的设计,我主要考虑了这么几个目标:
直观,因为系统最终会由最终用户来维护,权限分配的直观和容易理解,显得比较重要,系统不辞劳苦的实现了组的继承,除了功能的必须,更主要的就是因为它足够直观。
简单,包括概念数量上的简单和意义上的简单还有功能上的简单。想用一个权限系统解决所有的权限问题是不现实的。设计中将常常变化的“定制”特点比较强的部分判断为业务逻辑,而将常常相同的“通用”特点比较强的部分判断为权限逻辑就是基于这样的思路。此外,同时具备 Role 和 Group 的系统难于理解,权衡之下,摒弃了 Role 概念。
扩展,我在以前的项目中也实现过基于 Role 概念的权限系统,但效果不太理想。之所以在这里摒弃 Role 的概念,另一个原因就是因为它不易扩展。通常 Role 的设计方式意味着预先已经定好了一组权限,这样的“预先设计”,常常会鼓励程序员 hardcode 这些权限相关的部分,而,如果这么做的话,当需要重新定义 Role 时,扩展就会变得极为困难。而采用可继承的 Group 概念在支持权限以组方式定义的同时有效避免了重定义时在扩展上的困难。
名词:
下面两个名词极其重要,是整个设计问题边界定义的关键,或许我的理解与通常的理解不同,在此有必要特别澄清。
粗粒度:表示类别级,即,仅考虑对象的类别,不考虑对象的某个特定实例。比方,用户管理中,创建、删除,对所有的用户都一视同仁,并不区分操作的具体对象实例。
细粒度:表示实例级,即,需要考虑具体对象的实例,当然,细粒度是在考虑粗粒度的对象类别之后才再考虑特定实例。比方,合同管理中,列表、删除,需要区分该合同实例是否为当前用户所创建。
原则:
权限逻辑配合业务逻辑。即,权限系统以为业务逻辑提供服务为目标。纯粹纸面意义的极其复杂和精巧的权限系统,这里不作讨论。相当多细粒度的权限问题因其极其独特而不具通用意义,它们也能被理解为是“业务逻辑”的一部分。比方,要求:“合同资源只能被它的创建者删除,与创建者同组的用户可以修改,所有的用户能够浏览”。这既可以认为是一个细粒度的权限问题,也可以认为是一个业务逻辑问题。在这里我认为它是业务逻辑问题,在整个权限系统的架构设计之中不予过多考虑。当然,权限系统的架构也必须要能支持这样的控制判断。或者说,系统提供足够多但不是完全的控制能力。即,设计原则归结为:“系统只提供粗粒度的权限,细粒度的权限被认为是业务逻辑的职责”。
需要再次强调的是,这里表述的权限系统仅是一个“不完全”的权限系统,即,它不提供所有关于权限的问题的解决方法。它提供一个基础,并解决那些具有“共性”的(或者说粗粒度的)部分。在这个基础之上,根据“业务逻辑”的独特权限需求,编码实现剩余部分(或者说细粒度的)部分,才算完整。回到权限的问题公式,我的设计仅解决了 who + what + how 的问题,which 的权限问题留给业务逻辑解决。
概念:
User:用户。解决 who 的问题。
Group:组。权限分配的单位与载体。权限不考虑分配给特定的用户。组可以包括组(以实现权限的继承)。
Operate:操作。表明对 what 的 how 操作。
说明:
User
与大家的都一样,没什么好说的。
Group
与大家的类似,所不同的是,Group 要实现继承。即,在创建时必须要指定该 Group 的 Parent 是什么 Group 。在粗粒度控制上,可以认为,只要某用户直接或者间接的属于某个 Group 那么它就具备这个 Group 的所有操作许可。细粒度控制上,在业务逻辑的判断中,User 仅应关注其直接属于的 Group ,用来判断是否“同组”,间接的 Group 对权限的控制意义不大,试设想存在一个 All User 的 Group 是所有 Group 的祖先,这样的情形下,判断的结果不具备实际意义。
User 与 Group 是多对多的关系。即,一个 User 可以属于多个 Group 之中,一个 Group 可以包括多个 User 。
子 Group 与 父 Group 是多对一的关系。即,一个子 Group 只能有一个父 Group ,一个父 Group 可以包括多个子 Group 。
Operate
某种意义上类似于大家的 Resource + Privilege 概念,但,这里的 Resource 仅包括 Resource Type 不表示 Resource Instance。Operate 概念上与大家的观点区别比较,后面有详细的解释。
Group 与 Operate 是多对多的关系。
各概念的关系图示如下:
|
解释:
Operate 的定义包括了 Resource Type 和 Method 概念。即, what 和 how 的概念。之所以将 what 和 how 绑定在一起作为一个 Operate 概念而不是分开建模再建立关联,这是因为很多的 how 对于某 what 才有意义。比方,发布操作对新闻对象才有意义,对用户对象则没有意义。
how 本身的意义也有所不同,这里并非仅定义类 UNIX 的 RWX 三种操作,这样的定义对于文件系统是合理的,但,对于其他的应用领域或许就不是那么足够了。具体来说,对于每一个 what 可以定义 N 种操作。比方,对于合同这类对象,可以定义创建操作、提交操作、检查冲突操作等。可以认为,how 概念对应于每一个商业方法。
其中,与具体用户身份相关的操作既可以定义在操作的业务逻辑之中,也可以定义在操作级别。比方,创建者的浏览视图与普通用户的浏览视图要求内容不同。你既可以在外部定义两个操作方法,也可以在一个操作方法的内部根据具体逻辑进行处理。具体应用哪一种方式应依据实际情况进行处理。
这样的架构,应能在易于理解和管理的情况下,满足绝大部分粗粒度权限控制的功能需要。但是,除了粗粒度权限,无可否认,系统中必然还会包括无数对具体 Instance 的细粒度权限。这些问题,被留给业务逻辑来解决,这样的考虑基于以下两点。
一方面,细粒度的权限判断必须要在资源上建模权限分配的支持信息才可能得以实现。比方,如果要求创建者和普通用户看到不同的信息内容,那么,资源本身应该有其创建者的信息。如同 Unix 的每一个文件(资源),都定义了对 Owner, Group, All 的不同操作属性。
另一方面,细粒度的权限常常具有相当大的业务逻辑相关性。对不同的业务逻辑,常常意味着完全不同的权限判定原则和策略。相比之下,粗粒度的权限更具通用性,将其实现为一个架构,更有重用价值;而将细粒度的权限判断实现为一个架构级别的东西就显得繁琐,而且不是那么的有必要,用定制的代码来实现就更简洁,更灵活。
结语:
希望我的文字表述清楚,也希望我的方案能激起大家讨论研究的兴趣。
欢迎讨论。