14.1 Acegi眼中的领域对象
大部分开发者都应该熟悉基于Windows NT内核的Windows操作系统,比如Windows 2000、2003、XP。事实上,Windows操作系统本身充当了管理千万个领域对象的角色,这里的领域对象就是那些文件夹和文件。各个文件夹可以含有子文件夹,也可以含有大量的文件。文件是叶节点,它不再包含任何子元素。从领域对象的角度出发,文件夹和文件自身都持有各自的访问控制列表(Access Control List,ACL),ACL用于给定操控这些领域对象的权限信息,比如marissa用户可以操作shared目录,而scott用户不能够操作shared目录等。
为了同基于角色授权区分开,对于领域对象而言,访问控制列表(Access Control List,ACL)成为了各个领域对象的“专有名词”。这意味着,角色授权适用于Web资源和业务方法,而ACL授权适用于领域对象。各个ACL可能持有若干个ACE,即Access Control Entry(访问控制项)。总之,各个领域对象都会存在对应它的ACL,而各个ACL会持有若干个ACE,ACE真正给出了操控当前领域对象的具体权限信息。
关于org.acegisecurity.acl与org.acegisecurity.acls包
|
早期的Acegi就提供了较好的领域对象支持,即org.acegisecurity.acl包。随着企业用户的日益使用,他们逐渐认识到org.acegisecurity.acl的不足之处,比如他们不能够将特定数据库提供的一些高级特性应用到自身的应用中、acl包操作数据库的效率较低、对内存的使用不是非常合理等。后来,Acegi开发团队便也在逐渐改进现有的不足之处。
自从Acegi 1.0.3开始,基于新基代码的org.acegisecurity.acls包取代了org.acegisecurity.acl的地位,也就是说新开发的企业应用最好使用org.acegisecurity.acls提供的领域对象支持。本书仅仅专注org.acegisecurity.acls的使用,这两个包的使用存在的差别很大。可以看出,acls包不仅克服了原有acl包的一切缺陷,而且Acegi开发团队一直在改进acls包。Acegi开发团队可能会在某个特定时刻将org.acegisecurity.acl包丢弃掉。
|
14.1.1 保护领域对象概述
Acegi于org.acegisecurity.acls.domain包内置了表示ACL的如下Acl接口。通过这一接口,我们能够获得ACL持有的ACE集合、当前ACL对应的领域对象、这一ACL的持有人(主人)、当前ACL的父ACL等。
public interface Acl extends Serializable {
//获得当前ACL持有的ACE集合
public AccessControlEntry[] getEntries();
//当前ACL对应的领域对象
public ObjectIdentity getObjectIdentity();
//持有这一ACL的主人
public Sid getOwner();
//获得当前ACL的父ACL
public Acl getParentAcl();
//父ACL是否允许被当前ACL继承
public boolean isEntriesInheriting();
//用于ACL授权决定
public boolean isGranted(Permission[] permission, Sid[] sids,
boolean administrativeMode)
throws NotFoundException, UnloadedSidException;
//判断当前ACL是否已持有传入的sids
public boolean isSidLoaded(Sid[] sids);
}
图14-1展示了Acegi内置的Acl继承链,处于继承链中的各个接口及类各有各的用途。比如,借助于MutableAcl能够修改当前的AclImpl实例,借助于AuditableAcl能够完成ACL中ACE的审计,借助于OwnershipAcl能够修改当前AclImpl实例的主人。
图14-1 Acl继承链
下面给出了Acegi内置的用于表示ACE的AccessControlEntry策略接口。通常,Acl同AccessControlEntry具有1:N的关系,单个Acl持有若干个AccessControlEntry。
public interface AccessControlEntry {
//获得其所在的ACL
public Acl getAcl();
//标识当前的ACE
public Serializable getId();
//表示的ACL(ACE)权限信息
public Permission getPermission();
//当前ACL(ACE)权限信息的持有人,比如marissa用户
public Sid getSid();
//当前ACL(ACE)权限信息是否已经授给了Sid
public boolean isGranting();
}
图14-2展示了Acegi内置的AccessControlEntry继承链。
图14-2 AccessControlEntry继承链
在借助Acegi保护领域对象期间,开发者几乎不用同Acl和AccessControlEntry打交道,至少不用同AclImpl和AccessControlEntryImpl实现类交互。Acl和AccessControlEntry都使用到Sid接口,而Acl还使用到ObjectIdentity接口。Sid用于表示ACL授权过程中的授权对象,比如marissa用户、ROLE_USER角色都可以成为Sid的表示对象。图14-3展示了Acegi内置的Sid继承链。开发者将用户名、Authentication对象传入PrincipalSid构建器便能够构建出PrincipalSid对象;同理,将角色、GrantedAuthority对象传入GrantedAuthoritySid构建器便能够构建出GrantedAuthoritySid对象。ObjectIdentity用于标识单个领域对象,这一对象的存在使得目标企业应用同Acegi间的耦合得到降低,ObjectIdentityImpl是Acegi内置的唯一ObjectIdentity实现类。
图14-3 Sid继承链
Acegi提供的ACL子系统正是围绕Acl、AccessControlEntry、Sid、ObjectIdentity展开的,这些对象存活于业务系统与RDBMS间。也正是这些接口的存在,我们才能够将Acegi提供的ACL子系统作用到任何RDBMS中。也就是说,Acegi提供的领域对象支持适合于所有的RDBMS、O/R Mapping技术。为了能够从RDBMS装载到相关对象,我们需要使用Acegi内置的如下AclService接口。
//获得ACL
public interface AclService {
//查找到parentIdentity的所有子ObjectIdentity,ACL管理工具经常要使用到它
public ObjectIdentity[] findChildren(ObjectIdentity parentIdentity);
//通过ObjectIdentity获得单个Acl对象
public Acl readAclById(ObjectIdentity object) throws NotFoundException;
//通过ObjectIdentity获得仅适合于sids的单个Acl对象
public Acl readAclById(ObjectIdentity object, Sid[] sids)
throws NotFoundException;
//获得ObjectIdentity[]对应的Acl对象集合
public Map readAclsById(ObjectIdentity[] objects) throws NotFoundException;
//通过ObjectIdentity[]获得仅适合于sids的Acl对象集合
public Map readAclsById(ObjectIdentity[] objects, Sid[] sids)
throws NotFoundException;
}
图14-4展示了Acegi内置的AclService继承链,处于这一继承链中的各个接口及实现类各有各的用途。比如,JdbcAclService用于实现AclService策略接口。
图14-4 AclService继承链
MutableAclService用于维护Acl实例,开发者经常要同这一策略接口打交道,其定义如下。JdbcMutableAclService实现了MutableAclService。注意,开发者也可以提供O/R Mapping技术对应的AclService实现,比如Hibernate、JPA。Acegi仅仅实现了JDBC版本的AclService,开发者可以在自身的应用中同时使用O/R Mapping技术和JDBC。
//维护Acl实例
public interface MutableAclService extends AclService {
//在RDBMS中创建不含ACE的ACL,即Acl对象
public MutableAcl createAcl(ObjectIdentity objectIdentity)
throws AlreadyExistsException;
//从RDBMS中删除objectIdentity对应的ACL
public void deleteAcl(ObjectIdentity objectIdentity, boolean deleteChildren)
throws ChildrenExistException;
//将现有的acl实例同步到RDBMS中
public MutableAcl updateAcl(MutableAcl acl) throws NotFoundException;
}
通过本节内容,我们大致了解了Acegi提供的用于ACL子系统的各主要接口。本章后续内容将围绕这些接口展开论述。
14.1.3 ACL权限的定义
我们可以对领域对象进行各种操作,比如新增、删除、修改、浏览、管理等。Acegi将各种ACL权限信息建模在BasePermission对象中,相关的ACL权限信息摘录如下。开发者经常需要同READ、WRITE、CREATE、DELETE、ADMINISTRATION等ACL权限打交道,这些权限的含义非常容易理解。借助于FieldRetrievingFactoryBean,开发者能够在DI容器中配置它们。
public static final Permission READ =
new BasePermission(1 << 0, 'R'); // 1
public static final Permission WRITE =
new BasePermission(1 << 1, 'W'); // 2
public static final Permission CREATE =
new BasePermission(1 << 2, 'C'); // 4
public static final Permission DELETE =
new BasePermission(1 << 3, 'D'); // 8
public static final Permission ADMINISTRATION =
new BasePermission(1 << 4, 'A'); // 16
图14-7展示了Acegi内置的Permission继承链。BasePermission将基本的权限建模好了,而借助于CumulativePermission,开发者能够建构复合权限。比如,我们可以对READ、WRITE等权限进行逻辑或运算,进而在RDBMS中存储复合权限,从而简化了ACL授权操作。
图14-7 Permission继承链
下面摘录了ADMINISTRATION、READ、DELETE权限的定义示例,这些配置信息同样摘自于applicationContext-common-authorization.xml配置文件。各个AclEntryVoter投票器和AbstractAclProvider子类需要引用到这些ACL权限定义。
<bean id="BasePermission.ADMINISTRATION"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
<property name="staticField">
<value>org.acegisecurity.acls.domain.BasePermission.ADMINISTRATION</value>
</property>
</bean>
<bean id="BasePermission.READ"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
<property name="staticField">
<value>org.acegisecurity.acls.domain.BasePermission.READ</value>
</property>
</bean>
<bean id="BasePermission.DELETE"
class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean">
<property name="staticField">
<value>org.acegisecurity.acls.domain.BasePermission.DELETE</value>
</property>
</bean>