14.2 实施保护领域对象的重要步骤
在熟悉Acegi中ACL子系统的各个术语及基本概念后,我们需要通过实际企业应用验证它们,从而加深对它们的理解。
14.2.1 RDBMS表的建立
为了实施基于Acegi ACL领域对象授权支持的企业应用,开发者必须建立好同ACL相关的4张RDBMS表。它们的名字和含义如下。
l acl_class:用于存储领域对象对应的全限定名,比如sample.contact.Contact。
l acl_sid:用于存储ACL授权对象,或者是用户名,或者是角色名。比如,marissa用户、ROLE_USER角色。
l acl_object_identity:用于存储领域对象对应的Acl信息。
l acl_entry:用于存储Acl(acl_object_identity)对应的AccessControlEntry信息。默认时,acl_object_identity同acl_entry构成了主从表关系,并以1:N关系存在。
图14-8展示了这4张表间的具体关系,这是采用Hibernate Tools获得的图形化表示,此外,图中还展示了Acegi contacts示例存储领域对象的contacts表、存储用户和角色信息的users和authorities表。值得开发者注意的是,contacts、users、authorities表与ACL对应的4张表并无直接联系。接下来,我们来一一研究这些表的设计和内容。由于Acegi contacts示例采用了HSQLDB RDBMS,因此这些表对应的SQL DDL语句不能够直接用于其他生产数据库,比如Oracle 10g、SQL Server 2005。在领会Acegi ACL的思想后,开发者能够很轻松地给出特定数据库版本的ACL表定义。
图14-8 contacts示例设计的RDBMS表
其一,users表的定义如下:username列用于存储用户名,password列用于存储密码,enabled列用于存储启用标志位。
create table users(
username varchar_ignorecase(50) not null primary key,
password varchar_ignorecase(50) not null,
enabled boolean not null
);
图14-9给出了users表中已存储的示例数据。这里的password列的内容经过了MD5加密处理。
图14-9 users表已存储的示例数据
其二,authorities表的定义如下:username列用于存储用户名,authority列用于存储角色名。
create table authorities(
username varchar_ignorecase(50) not null,
authority varchar_ignorecase(50) not null,
constraint fk_authorities_users foreign key(username) references users(username)
);
create unique index ix_auth_username on authorities(username,authority);
图14-10给出了authorities表中已存储的示例数据,其中的username列数据引用到users表中username列的数据。默认时,users和authorities表处于1:N的关系。
图14-10 authorities表已存储的示例数据
其三,contacts表的定义如下:id列用于标识领域对象,contact_name列存储联系人的姓名,而email列用于存储联系人的邮件地址。
create table contacts(
id bigint not null primary key,
contact_name varchar_ignorecase(50) not null,
email varchar_ignorecase(50) not null
);
图14-11给出了contacts表中已存储的示例数据。
图14-11 contacts表已存储的示例数据
在实际企业应用中,存在大量的领域对象,因而会存在大量的RDBMS表来存储它们。类似地,实际企业应用存储用户、角色信息的RDBMS表也不一定是users、authorities表,相信仔细阅读过本书相关章节的开发者都知道如何自定义它们。好了,再来研究4张ACL表的SQL DDL语句及相应的示例数据。
其四,acl_class表的定义如下:id列用于标识领域对象对应的全限定名,而class列用于存储领域对象对应的全限定名。
create table acl_class(
id bigint generated by default as identity(start with 100) not null primary key,
class varchar_ignorecase(100) not null,
constraint unique_uk_2 unique(class)
);
图14-12给出了acl_class表中已存储的示例数据。由于Acegi contacts示例仅仅存在单个Contact领域对象类型,因此这一表仅仅存在单条记录。
图14-12 acl_class表已存储的示例数据
其五,acl_sid表的定义如下:principal列用于给定sid列的类型,即同一记录中存储了用户名还是角色名。相比之下,sid列存储用户名或者角色名。如果sid列存储了用户名,则principal列取值为true;如果sid列存储了角色名,则principal列取值为false。
create table acl_sid(
id bigint generated by default as identity(start with 100) not null primary key,
principal boolean not null,
sid varchar_ignorecase(100) not null,
constraint unique_uk_1 unique(sid,principal)
);
图14-13给出了acl_sid表中已存储的示例数据。由于sid列存储的数据都是用户名,因此principal列取值都为true。在某种程度上,acl_sid对应于Sid对象。
图14-13 acl_sid表已存储的示例数据
其六,acl_object_identity表的定义如下:id列为主键。其中,object_id_class引用到acl_class中的id列取值,图14-14中的object_id_class列取值都是100,即Contact领域对象类型。类似地,object_id_identity列的内容起到绑定特定领域对象的作用。比如,在contacts示例应用中,object_id_identity列的内容同contacts表中id列的内容保持一致。注意,开发者也可以通过其他策略维护object_id_identity列同特定领域对象间的绑定关系。默认时,我们建议领域对象都能够含有id属性,因为ObjectIdentityImpl可能会通过反射机制调用到领域对象的getId()方法,并将返回结果存储到object_id_identity列。最后,parent_object列用于指定当前ACL的父ACL,而own_sid列用于存储当前ACL的主人,entries_inheriting列用于指定当前ACL是否继承父ACL(含有的ACE集合)。可以看出,owner_sid列取值引用到acl_sid中id列取值。
create table acl_object_identity(
id bigint generated by default as identity(start with 100) not null primary key,
object_id_class bigint not null,
object_id_identity bigint not null,
parent_object bigint,
owner_sid bigint,
entries_inheriting boolean not null,
constraint unique_uk_3 unique(object_id_class,object_id_identity),
constraint foreign_fk_1 foreign key(parent_object)references acl_object_identity(id),
constraint foreign_fk_2 foreign key(object_id_class)references acl_class(id),
constraint foreign_fk_3 foreign key(owner_sid)references acl_sid(id)
);
图14-14给出了RDBMS acl_object_identity表中已存储的示例数据。在某种程度上,acl_object_identity对应于Acl对象。
图14-14 acl_object_identity表已存储的示例数据
其七,acl_entry表的定义如下:id列是主键。其中,acl_object_identity表指定当前ACE所属的ACL,即对应到acl_object_identity表中的id列。类似地,sid列用于存储当前ACE所属的授权对象,它对应于acl_sid表中的id列;ace_order列对ACE集合进行排序;mask列存储ACE权限信息,具体情况请参考BasePermission和CumulativePermission类;granting列表明当前的ACE权限是否已经授给了同一记录持有的sid列(用户名或角色);audit_success和audit_failure列用于审计目的,供各种基于Acegi ACL的管理工具在审计ACL(ACE)时使用。开发者可以借助于Acegi暴露的API开发出相应的审计(管理)工具。
create table acl_entry(
id bigint generated by default as identity(start with 100) not null primary key,
acl_object_identity bigint not null,
ace_order int not null,
sid bigint not null,
mask integer not null,
granting boolean not null,
audit_success boolean not null,
audit_failure boolean not null,
constraint unique_uk_4 unique(acl_object_identity,ace_order),
constraint foreign_fk_4 foreign key(acl_object_identity) references acl_object_identity(id),
constraint foreign_fk_5 foreign key(sid) references acl_sid(id)
);
图14-15给出了RDBMS acl_entry表中已存储的示例数据。在某种程度上,acl_entry对应于AccessControlEntry对象。
图14-15 acl_entry表已存储的示例数据