如何使用先进的ACL概念
本文的目的,是深入了解ACL系统,并对其背后的一些设计决策进行解释。
设计理念 ¶
Symfony的对象实例,其安全能力是建立在 Access Control List(ACL)这一概念基础之上的。每一个DOMain object(域对象)都有它自己的ACL。ACL实例,持有一个Access Control Entries(ACEs)明细列表,用于做出访问决定(access decisions)。Symfony的ACL系统专注于两个主要目标:
为你的域对象提供一种“高效地取出大量ACLs/ACEs”的方式,然后修改它们;
提供一种“可以轻易判断某人是否可以对域对象进行操作”的方式。
如第一点所述,Symfony中ACL系统的主要能力之一,就是取出ACLs/ACEs时的极高性能。这极端重要,因为每个ACL可以有多个ACEs,还可以通过一种“类树状方式”(tree-like fashion)从别的ACL中进行继承。因此我们不借助ORM,默认的实现是直接用Doctrine的DBAL来与你的连接(connection)展开互动(译注:就是指ACL/ACEs的一切存取操作都是基于DBAL)。
Object Identities ¶
ACL系统是从你的域对象中完全解耦的。它们甚至不需要被保存在同一个数据库中或同一台服务器上。为了实现这种解耦,在ACL系统中,你的对象将通过object identity对象来呈现。每当你要取出一个域对象的ACL时,ACL系统首先要创建一个object identity(对象识别),然后将这个object identity对象,传入ACL provider作进一步处理。
Security Identities ¶
与对象识别相类似,但是它在程序中呈现的是一个用户(user),或是一个role(角色)。每一个role或用户,有其自己的安全识别(security identity)。
对于用户来说,security identity基于用户名。这意味着,无论什么原因,只要一个用户的username发生了改变,你必须确保security identity也得到更新。MutableAclProvider::updateUserSecurityIdentity()
方法专门用来处理更新。
数据库的表结构 ¶
(ACL系统)的默认实现是使用以下五个数据表。这些表(在本文中)的排列顺序,是按照“在一个典型程序中,每个表的记录行数由少到多”来进行的。
acl_security_identities:该表记录了全部的security identity(SID)。默认的实现自带了两个security identity:
RoleSecurityIdentity
和UserSecurityIdentity
。acl_classes:该表将类名映射成唯一ID(而存储下来),该ID可以被其他数据表引用。
acl_object_identities:数据表中的每一条记录,呈现的一个单独的域对象实例。
acl_object_identity_ancestors:该表能够以一种极为高效的方式来确定出一个ACL的“所有父ACL”(ancestors)。
acl_entries:这个表包含了全部ACEs。这也是通常情况下“行数最多”的表。在千万级数据的条件下,不会显著影响性能。
ACE的SCOPE ¶
Access control entries可以应用到不同的scope(范围)。在Symfony中,基本上有两种不同的范围:
Class-Scope(类范围):这些ACEs适用于同一个类的所有对象。
Object-Scope(对象范围):在前面章节中我们用过一次这个scope,它只适用于一个特定的对象。
有时,你会发现有 “只把ACE应用到某个对象的特定字段中” 这样的需求。假设,你希望只有管理员能看到ID,而不能被客服看到。要解决这种常见问题,需要额外添加两个sub-scopes(子范围):
Class-Field-Scope(类字段范围):这些ACEs适用于同一个类的所有对象,但却只能作用于对象中的某个特定字段。
Object-Field-Scope(对象字段范围):这些ACEs适用于一个特定的对象,同时只能作用于该对象的某个特定字段。
Pre-Authorization Decisions ¶
所谓Pre-Authorization Decisions是指,在任何安全方法(security method,或安全动作[security action])调用之前就已经做出的决断,这用到了正统的AccessDecisionManager服务。AccessDecisionManager服务也用在“基于角色(based on roles)”的reaching authorization decisions(达成授权决断)。和roles一样,ACL系统添加了若干新的属性,用于检查不同的权限。
内建的Permission Map(权限映射) ¶
Attribute (属性) |
Intended Meaning (意图) |
Integer Bitmasks (整型位掩码) |
---|---|---|
VIEW | Whether someone is allowed to view the domain object. 是否允许某人查看域对象 |
VIEW, EDIT, OPERATOR, MASTER, or OWNER |
EDIT | Whether someone is allowed to make changes to the domain object. 是否允许某人修改域对象 |
EDIT, OPERATOR, MASTER, or OWNER |
CREATE | Whether someone is allowed to create the domain object. 是否允许某人创建域对象 |
CREATE, OPERATOR, MASTER, or OWNER |
DELETE | Whether someone is allowed to delete the domain object. 某人是否被允许删除域对象 |
DELETE, OPERATOR, MASTER, or OWNER |
UNDELETE | Whether someone is allowed to restore a previously deleted domain object. 是否允许某人恢复先前被删除的域对象 |
UNDELETE, OPERATOR, MASTER, or OWNER |
OPERATOR | Whether someone is allowed to perform all of the above actions. 是否允许某人执行上述全部操作。 |
OPERATOR, MASTER, or OWNER |
MASTER | Whether someone is allowed to perform all of the above actions, and in addition is allowed to grAnt any of the above permissions to others. 是否允许某人执行上述全部操作,同时他还能将以上任何一个权限授权给他人。 |
MASTER, or OWNER |
OWNER | Whether someone owns the domain object. An owner can perform any of the above actions andgrant master and owner permissions. 某人是否拥有该域对象。owner能执行上述任何一个操作 并且 能够(向他人)授予master和owner权限。 |
OWNER |
Permission Attributes vs. Permission Bitmasks(权限属性 vs 权限位掩码) ¶
AccessDecisionManager在使用属性(attributes)时类似于它使用roles(角色)。通常,这些属性的实际呈现方式是“一系列聚合起来的整型位掩码(integer bitmasks)”。而另一方面,整型位掩码在ACL系统内部,被用来在数据库中高效地存储用户权限,并且使用超快的位掩码操作来执行访问检查(access checks)。
扩展性 ¶
上述权限映射并非一成不变,理论上完全可以随需替换之。然而,它(译注:指由integer bitmasks所构成permission maps)将涵盖你所遇到的绝大多数问题,并且可以与其他Bundles实现互用,建议你遵照其设计本意来使用之。
Post Authorization Decisions ¶
Post Authorization Decisions在(程序中的)安全(相关)方法(security method)被调用之后确立,一般还会包含这些方法所返回的domain object。在调用providers之后,域对象仍然可以在被返回之前接受修改和过滤。
由于PHP语言的限制,框架核心的安全组件还不具备后授权(post-authorization)的能力。尽管如此,实验性质的 JMSSecurityExtraBundle 已经添加了这些功能。参考它的文档以便进一步了解它是如何实现的。
达成Authorization Decisions的过程 ¶
ACL类提供两种方法去来决断一个security identity是否拥有所需的bitmasks,即 isGranted
和 isFieldGranted
。当ACL通过其中的一种方法接收到授权请求(authorization request)时,它把这个请求委托给 PermissionGrantingStrategy
的一个实现。这就令你能够替换access dicisions的达成方式,而毋须真正修改ACL类本身。
PermissionGrantingStrategy
首先检查你所有的object-scope的ACEs,如果无有可用,则去检查object-scope的ACEs。如果仍无可用,进程将重复查找父ACL上的ACEs。若父ACL并不存在,一个异常会被抛出。