先看看總的配置:
<bean?id="contactManagerSecurity"?class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
??????<property?name="authenticationManager"><ref?bean="authenticationManager"/></property>
??????<property?name="accessDecisionManager"><ref?local="businessAccessDecisionManager"/></property>
??????<property?name="afterInvocationManager"><ref?local="afterInvocationManager"/></property>
??????<property?name="objectDefinitionSource">
?????????<value>
????????????sample.contact.ContactManager.getAll=AFTER_ACL_COLLECTION_READ
????????????sample.contact.ContactManager.getById=AFTER_ACL_READ
????????????sample.contact.ContactManager.delete=ACL_CONTACT_DELETE
????????????sample.contact.ContactManager.deletePermission=ACL_CONTACT_ADMIN
????????????sample.contact.ContactManager.addPermission=ACL_CONTACT_ADMIN
?????????</value>
??????</property>
???</bean>
該攔截器實(shí)現(xiàn)了org.aopalliance.intercept.MethodInterceptor接口。在方法被調(diào)用之前,攔截器會(huì)先調(diào)用 AuthenticationManager判斷用戶(hù)身份是否已驗(yàn)證,然后從objectDefinitionSource中獲取方法所對(duì)應(yīng)的權(quán)限,再調(diào)用AccessDecisionManager來(lái)匹配用戶(hù)權(quán)限和方法對(duì)應(yīng)的權(quán)限。如果用戶(hù)沒(méi)有足夠權(quán)限調(diào)用當(dāng)前方法,則拋出 AccessDeniedException使方法不能被調(diào)用。方法調(diào)用后會(huì)調(diào)用AfterInvocationManager對(duì)返回的結(jié)果進(jìn)行再次處理。下面依次說(shuō)明。??????<property?name="authenticationManager"><ref?bean="authenticationManager"/></property>
??????<property?name="accessDecisionManager"><ref?local="businessAccessDecisionManager"/></property>
??????<property?name="afterInvocationManager"><ref?local="afterInvocationManager"/></property>
??????<property?name="objectDefinitionSource">
?????????<value>
????????????sample.contact.ContactManager.getAll=AFTER_ACL_COLLECTION_READ
????????????sample.contact.ContactManager.getById=AFTER_ACL_READ
????????????sample.contact.ContactManager.delete=ACL_CONTACT_DELETE
????????????sample.contact.ContactManager.deletePermission=ACL_CONTACT_ADMIN
????????????sample.contact.ContactManager.addPermission=ACL_CONTACT_ADMIN
?????????</value>
??????</property>
???</bean>
AccessDecisionManager的配置:
<bean?id="businessAccessDecisionManager"?class="org.acegisecurity.vote.AffirmativeBased">
??????<property?name="allowIfAllAbstainDecisions"><value>false</value></property>
??????<property?name="decisionVoters">
?????????<list>
????????????<ref?local="roleVoter"/>
????????????<ref?local="aclContactReadVoter"/>
????????????<ref?local="aclContactDeleteVoter"/>
????????????<ref?local="aclContactAdminVoter"/>
?????????</list>
??????</property>
???</bean>
AccessDecisionManager接口有decide()和support()方法。decide()方法決策是否批準(zhǔn)通過(guò)即方法是否容許調(diào)用,如果沒(méi)拋出AccessDeniedException則為允許訪問(wèn)資源,否則拒絕訪問(wèn)。support()方法是根據(jù)配置屬性和受保護(hù)資源的類(lèi)來(lái)判斷是否需要對(duì)該資源作出決策判斷。??????<property?name="allowIfAllAbstainDecisions"><value>false</value></property>
??????<property?name="decisionVoters">
?????????<list>
????????????<ref?local="roleVoter"/>
????????????<ref?local="aclContactReadVoter"/>
????????????<ref?local="aclContactDeleteVoter"/>
????????????<ref?local="aclContactAdminVoter"/>
?????????</list>
??????</property>
???</bean>
AccessDecisionManager的 decisionVoters屬性需要一個(gè)或多個(gè)Voter(投票者),Voter必須實(shí)現(xiàn)AccessDecisionVoter 接口。Voter的工作是去匹配用戶(hù)已擁有的權(quán)限和受保護(hù)的資源要求的權(quán)限,在該資源有相應(yīng)權(quán)限的情況下,如果匹配則投允許票,否則投反對(duì)票。
allowIfAllAbstainDecisions屬性表示是否允許所有都棄權(quán)時(shí)就通過(guò)。Voter的實(shí)現(xiàn)類(lèi)RoleVoter在當(dāng)受保護(hù)資源的名字由ROLE_開(kāi)始時(shí)才參與投票。
AccessDecisionManager有三個(gè)實(shí)現(xiàn)類(lèi),功能各不相同:
AffirmativeBased: 當(dāng)至少有一個(gè)Voter投允許票時(shí)才通過(guò)
UnanimousBased: 沒(méi)有Voter投反對(duì)票時(shí)才通過(guò)
ConsensusBased: 當(dāng)所有Voter都投允許票時(shí)才通過(guò)
下面列出一個(gè)Voter的配置:
<bean?id="aclContactDeleteVoter"?class="org.acegisecurity.vote.BasicAclEntryVoter">
??????<property?name="processConfigAttribute"><value>ACL_CONTACT_DELETE</value></property>
??????<property?name="processDomainObjectClass"><value>sample.contact.Contact</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.DELETE"/>
????????</list>
??????</property>
???</bean>
上面第一個(gè)配置里有這么一行:sample.contact.ContactManager.delete=ACL_CONTACT_DELETE??????<property?name="processConfigAttribute"><value>ACL_CONTACT_DELETE</value></property>
??????<property?name="processDomainObjectClass"><value>sample.contact.Contact</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.DELETE"/>
????????</list>
??????</property>
???</bean>
所以在調(diào)用sample.contact.ContactManager.delete這個(gè)方法時(shí)aclContactDeleteVoter會(huì)參與投票,它會(huì)獲得sample.contact.Contact這個(gè)對(duì)象(這個(gè)對(duì)象從delete方法的參數(shù)中獲得),然后通過(guò)aclManager去獲得當(dāng)前用戶(hù)對(duì)該Contact實(shí)例的ACL權(quán)限,最后拿這個(gè)權(quán)限與我們需要的權(quán)限比對(duì),我們配置需要的權(quán)限是org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION和org.acegisecurity.acl.basic.SimpleAclEntry.DELETE。如果我們通過(guò)aclManager獲得的權(quán)限包括這兩個(gè)配置的權(quán)限之一,Voter就投容許票,方法容許調(diào)用。如果不包括,那對(duì)不起,反對(duì)票,AccessDecisionManager就會(huì)拋出AccessDeniedException。方法拒絕調(diào)用。
AclManager的配置:
<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是整個(gè)ACL中一個(gè)很核心的概念,它包含了兩個(gè)方法AclEntry[] getAcls(Object domainInstance)和??????<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>
AclEntry[] getAcls(Object domainInstance, Authentication authentication)。在了解這兩個(gè)方法前,我們先了解AclEntry這個(gè)對(duì)象。AclEntry只是一個(gè)接口,系統(tǒng)中一般都是造型為BasicAclEntry。它包括了這個(gè)Entry所保護(hù)的domainObject instance(這里是Contact),實(shí)際它實(shí)現(xiàn)上是以AclObjectIdentity來(lái)替代這個(gè)domainObject的(domainClass+domainObjectId);它包括了誰(shuí)(Recipient)擁有這個(gè)domainObject instance以及他所對(duì)這個(gè)domainObject instance的操作權(quán)限(mask)。
一個(gè)domainObject instance對(duì)應(yīng)了多個(gè)AclEntry,比如一條通訊錄張三可以查看,而李四可以管理,一個(gè)Contact instance就對(duì)應(yīng)了兩個(gè)AclEntry,第一個(gè)AclEntry包含信息:所保護(hù)的domainObject(Contact),誰(shuí)(張三),權(quán)限(查看);第二個(gè)AclEntry包含信息:所保護(hù)的domainObject(Contact),誰(shuí)(李四),權(quán)限(管理)。
這樣AclManager的兩個(gè)方法就很好理解了getAcls(Object domainInstance)返回所有這個(gè)domainInstance所對(duì)應(yīng)的權(quán)限信息,
getAcls(Object domainInstance, Authentication authentication)在第一個(gè)方法返回結(jié)果的基礎(chǔ)上做了過(guò)濾,過(guò)濾出和authentication(當(dāng)前用戶(hù))相關(guān)的權(quán)限信息。如果當(dāng)前用戶(hù)是張三,則返回與張三對(duì)應(yīng)的記錄。
這樣Acegi就會(huì)攔截業(yè)務(wù)方法發(fā)揮相應(yīng)的作用,但是在業(yè)務(wù)方法返回一個(gè)List或是單個(gè)domainObject instance的時(shí)候,同樣也是需要把用戶(hù)沒(méi)有權(quán)限查看的domainObject instance過(guò)濾掉的,這時(shí)就要用afterInvocationManager了,看配置:
<bean?id="afterInvocationManager"?class="org.acegisecurity.afterinvocation.AfterInvocationProviderManager">
??????<property?name="providers">
?????????<list>
????????????<ref?local="afterAclRead"/>
????????????<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>
???
???<!--?Processes?AFTER_ACL_READ?configuration?settings?-->
???<bean?id="afterAclRead"?class="org.acegisecurity.afterinvocation.BasicAclEntryAfterInvocationProvider">
??????<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ì)對(duì)配置AFTER_ACL_COLLECTION_READ的方法進(jìn)行攔截,這里是sample.contact.ContactManager.getAll方法,它會(huì)遍歷方法返回的domainObject,然后挨個(gè)通過(guò)aclManager判斷當(dāng)前用戶(hù)對(duì)domainObject的權(quán)限,如果和需要的權(quán)限不和,則過(guò)濾掉。呵呵,傳說(shuō)中的虎牙子就在此時(shí)產(chǎn)生了!??????<property?name="providers">
?????????<list>
????????????<ref?local="afterAclRead"/>
????????????<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>
???
???<!--?Processes?AFTER_ACL_READ?configuration?settings?-->
???<bean?id="afterAclRead"?class="org.acegisecurity.afterinvocation.BasicAclEntryAfterInvocationProvider">
??????<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>
afterAclRead則依次類(lèi)推。
參考了ss wiki里相關(guān)的文檔,特別是差沙和cac的文檔,寫(xiě)的相當(dāng)好。另外acegi的代碼也是相當(dāng)?shù)囊鬃x。
http://www.aygfsteel.com/ronghao 榮浩原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處:)