Acegi好早就實(shí)現(xiàn)了ACL(好像是0.5),但是使用起來(lái)確實(shí)有點(diǎn)麻煩,所以用的不是太廣泛。這里簡(jiǎn)單的說(shuō)明一下使用方法,希望有更多的朋友來(lái)試試。
首先要理解Acegi里面Voter的概念,ACL正是在一個(gè)Voter上擴(kuò)展起來(lái)的。現(xiàn)來(lái)看一下AclVoter的配置。
<bean id="aclBeanReadVoter" class="org.acegisecurity.vote.BasicAclEntryVoter">
<property name="processConfigAttribute">
<value>ACL_READ</value>
</property>
<property name="processDomainObjectClass">
<value>org.springside.modules.security.acl.domain.AclDomainAware</value>
</property>
<property name="aclManager">
<ref local="aclManager"/>
</property>
<property name="requirePermission">
<list>
<ref local="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"/>
<ref local="org.acegisecurity.acl.basic.SimpleAclEntry.READ"/>
</list>
</property>
</bean>
- ACL_READ指的是這個(gè)Voter對(duì)哪些SecurityConfig起作用,我們可以把ACL_READ配置在想要攔截的Method上。比方說(shuō)我們要攔截readOrder這個(gè)方法,以實(shí)現(xiàn)ACL控制,可以這樣配置。
orderManager.readOrder=ACL_READ
- processDomainObjectClass指出哪些DomainObject是要進(jìn)行ACL校驗(yàn)的。
- aclManager是一個(gè)比較重要的概念,主要負(fù)責(zé)在權(quán)限列表中根據(jù)用戶和DomainObject取得acl列表。
- requirePermission指出要進(jìn)行這個(gè)操作必須具備的acl權(quán)限,比方說(shuō)read操作就必須有ADMINISTRATION或READ兩個(gè)權(quán)限。
其實(shí)整個(gè)過(guò)程看下來(lái)比較清晰,下面來(lái)看一下AclManager如何配置。
<!-- ========= ACCESS CONTROL LIST LOOKUP MANAGER DEFINITIONS ========= -->

<bean id="aclManager" class="org.acegisecurity.acl.AclProviderManager">
<property name="providers">
<list>
<ref local="basicAclProvider"/>
</list>
</property>
</bean>

<bean id="basicAclProvider" class="org.acegisecurity.acl.basic.BasicAclProvider">
<property name="basicAclDao">
<ref local="basicAclExtendedDao"/>
</property>
</bean>

<bean id="basicAclExtendedDao" class="org.acegisecurity.acl.basic.jdbc.JdbcExtendedDaoImpl">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
很明顯ACLManager繼承了Acegi的一貫風(fēng)格,Provider可以提供多種取得ACL訪問(wèn)列表的途徑,默認(rèn)的是用
basicAclProvider在數(shù)據(jù)庫(kù)中取得。既然提到了數(shù)據(jù)庫(kù),那我們就來(lái)看一下Acegi默認(rèn)提供的ACL在數(shù)據(jù)庫(kù)里的保存表結(jié)構(gòu):
CREATE TABLE acl_object_identity (
id IDENTITY NOT NULL,
object_identity VARCHAR_IGNORECASE(250) NOT NULL,
parent_object INTEGER,
acl_class VARCHAR_IGNORECASE(250) NOT NULL,
CONSTRAINT unique_object_identity UNIQUE(object_identity),
FOREIGN KEY (parent_object) REFERENCES acl_object_identity(id)
);
CREATE TABLE acl_permission (
id IDENTITY NOT NULL,
acl_object_identity INTEGER NOT NULL,
recipient VARCHAR_IGNORECASE(100) NOT NULL,
mask INTEGER NOT NULL,
CONSTRAINT unique_recipient UNIQUE(acl_object_identity, recipient),
FOREIGN KEY (acl_object_identity) REFERENCES acl_object_identity(id)
);
- acl_object_identity表存放了所有受保護(hù)的domainObject的信息。其中object_identity字段保存了domainObject的class和id,默認(rèn)的保存格式是:domainClass:domainObjectId。
- acl_permission 就是ACL權(quán)限列表了,recipient 是用戶或角色信息,mask表示了這個(gè)用戶或角色對(duì)這個(gè)domainObject的訪問(wèn)權(quán)限。注意這些信息的保存格式都是可以根據(jù)自己的需要改變的。
這樣讀取和刪除的時(shí)候Acegi就能很好的完成攔截工作,但是讀取一個(gè)List的時(shí)候,如何才能把該用戶不能操作的domainObject剔除掉呢?這就需要afterInvocationManager來(lái)完成這個(gè)工作。下面來(lái)看下配置:
<!-- ============== "AFTER INTERCEPTION" AUTHORIZATION DEFINITIONS =========== -->

<bean id="afterInvocationManager" class="org.acegisecurity.afterinvocation.AfterInvocationProviderManager">
<property name="providers">
<list>
<ref local="afterAclCollectionRead"/>
</list>
</property>
</bean>
<!-- Processes AFTER_ACL_COLLECTION_READ configuration settings -->
<bean id="afterAclCollectionRead" class="org.acegisecurity.afterinvocation.BasicAclEntryAfterInvocationCollectionFilteringProvider">
<property name="aclManager">
<ref local="aclManager"/>
</property>
<property name="requirePermission">
<list>
<ref local="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"/>
<ref local="org.acegisecurity.acl.basic.SimpleAclEntry.READ"/>
</list>
</property>
</bean>
afterAclCollectionRead會(huì)在攔截的方法執(zhí)行結(jié)束的時(shí)候執(zhí)行。主要的作用就是在返回的List中挨個(gè)檢查domainObject的操作權(quán)限,然后根據(jù)requirePermission來(lái)剔除不符合的domainObject。