[收藏]Spring Security中的ACL

ACL即訪問控制列表(Access Controller List),它是用來作細粒度權限控制所用的一種權限模型。對ACL最簡單的描述就是兩個業務員,每一個人只能查看操做本身籤的合同,而不能看到對方的合同信息。
下面咱們會介紹Spring Security中是如何實現ACL的。

23.1. 準備數據庫和aclService

ACL所需的四張表,表結構見附錄: 附錄 E, 數據庫表結構
而後咱們須要配置aclService,它負責與數據庫進行交互。

23.1.1. 爲acl配置cache

默認使用ehcache,spring security提供了一些默認的實現類。
<bean id="aclCache" class="org.springframework.security.acls.jdbc.EhCacheBasedAclCache">
    <constructor-arg ref="aclEhCache"/>
</bean>
<bean id="aclEhCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
    <property name="cacheManager" ref="cacheManager"/>
    <property name="cacheName" value="aclCache"/>
</bean>
            
在ehcache.xml中配置對應的aclCache緩存策略。
<cache
    name="aclCache"
    maxElementsInMemory="1000"
    eternal="false"
    timeToIdleSeconds="600"
    timeToLiveSeconds="3600"
    overflowToDisk="true"
/>
 <!--Default Cache configuration. These will applied to caches programmatically created through
         the CacheManager.

         The following attributes are required:html

         maxElementsInMemory             - Sets the maximum number of objects that will be created in memory
         eternal                         - Sets whether elements are eternal. If eternal,   timeouts are ignored and the
                                          element is never expired.
         overflowToDisk                  - Sets whether elements can overflow to disk when the in-memory cache
                                          has reached the maxInMemory limit.java

         The following attributes are optional:
         timeToIdleSeconds               - Sets the time to idle for an element before it expires.
                                          i.e. The maximum amount of time between accesses before an element expires
                                          Is only used if the element is not eternal.
                                          Optional attribute. A value of 0 means that an Element can idle for infinity.
                                          The default value is 0.
         timeToLiveSeconds               - Sets the time to live for an element before it expires.
                                          i.e. The maximum time between creation time and when an element expires.
                                          Is only used if the element is not eternal.
                                          Optional attribute. A value of 0 means that and Element can live for infinity.
                                          The default value is 0.
         diskPersistent                  - Whether the disk store persists between restarts of the Virtual Machine.
                                          The default value is false.
         diskExpiryThreadIntervalSeconds- The number of seconds between runs of the disk expiry thread. The default value
                                          is 120 seconds.
         -->spring

23.1.2. 配置lookupStrategy

簡單來講,lookupStrategy的做用就是從數據庫中讀取信息,把這些信息提供給aclService使用,因此咱們要爲它配置一個dataSource,配置中還能夠看到一個aclCache,這就是上面咱們配置的緩存,它會把資源最大限度的利用起來。
<bean id="lookupStrategy" class="org.springframework.security.acls.jdbc.BasicLookupStrategy">
    <constructor-arg ref="dataSource"/>
    <constructor-arg ref="aclCache"/>
    <constructor-arg>
        <bean class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl">
            <constructor-arg>
                <list>
                    <ref local="adminRole"/>
                    <ref local="adminRole"/>
                    <ref local="adminRole"/>
                </list>
            </constructor-arg>
        </bean>
    </constructor-arg>
    <constructor-arg>
        <bean class="org.springframework.security.acls.domain.ConsoleAuditLogger"/>
    </constructor-arg>
</bean>
<bean id="adminRole" class="org.springframework.security.GrantedAuthorityImpl">
    <constructor-arg value="ROLE_ADMIN"/>
</bean>
            
中間一部分可能會讓人感到困惑,爲什麼一次定義了三個adminRole呢?這是由於一旦acl信息被保存到數據庫中,不管是修改它的從屬者,仍是變動授 權,抑或是修改其餘的ace信息,都須要控制操做者的權限,這裏配置的三個權限將對應於上述的三種修改操做,咱們把它配置成,只有ROLE_ADMIN才 能執行這三種修改操做。

23.1.3. 配置aclService

當咱們已經擁有了dataSource, lookupStrategy和aclCache的時候,就能夠用它們來組裝aclService了,以後全部的acl操做都是基於aclService展開的。
<bean id="aclService" class="org.springframework.security.acls.jdbc.JdbcMutableAclService">
    <constructor-arg ref="dataSource"/>
    <constructor-arg ref="lookupStrategy"/>
    <constructor-arg ref="aclCache"/>
</bean>
            

23.2. 使用aclService管理acl信息

當咱們添加了一條信息,要在acl中記錄這條信息的ID,全部者,以及對應的受權信息。下列代碼在添加信息後執行,用於添加對應的acl信息。
ObjectIdentity oid = new ObjectIdentityImpl(Message.class, message.getId());
MutableAcl acl = mutableAclService.createAcl(oid);
acl.insertAce(0, BasePermission.ADMINISTRATION,
    new PrincipalSid(owner), true);
acl.insertAce(1, BasePermission.DELETE,
    new GrantedAuthoritySid("ROLE_ADMIN"), true);
acl.insertAce(2, BasePermission.READ,
    new GrantedAuthoritySid("ROLE_USER"), true);
mutableAclService.updateAcl(acl);
        
第一步,根據class和id生成object的惟一標示。
第二步,根據object的惟一標示,建立一個acl。
第三步,爲acl增長ace,這裏咱們讓對象的全部者擁有對這個對象的「管理」權限,讓「ROLE_ADMIN」擁有對這個對象的「刪除」權限,讓「ROLE_USER」擁有對這個對象的「讀取」權限。
最後,更新acl信息。
當咱們刪除對象時,也要刪除對應的acl信息。下列代碼在刪除信息後執行,用於刪除對應的acl信息。
ObjectIdentity oid = new ObjectIdentityImpl(Message.class, id);
mutableAclService.deleteAcl(oid, false);
        
使用class和id能夠惟一標示一個對象,而後使用deleteAcl()方法將對象對應的acl信息刪除。

23.3. 使用acl控制delete操做

上述代碼中,除了對象的擁有者以外,咱們還容許「ROLE_ADMIN」也能夠刪除對象,可是咱們不會容許除此以外的其餘用戶擁有刪除對象的權限,爲了限制對象的刪除操做,咱們須要修改Spring Security的默認配置。
首先要增長一個對delete操做起做用的表決器。
<bean id="aclMessageDeleteVoter" class="org.springframework.security.vote.AclEntryVoter">
    <constructor-arg ref="aclService"/>
    <constructor-arg value="ACL_MESSAGE_DELETE"/>
    <constructor-arg>
        <list>
            <util:constant static-field="org.springframework.security.acls.domain.BasePermission.ADMINISTRATION"/>
            <util:constant static-field="org.springframework.security.acls.domain.BasePermission.DELETE"/>
        </list>
    </constructor-arg>
    <property name="processDomainObjectClass" value="com.family168.springsecuritybook.ch12.Message"/>
</bean>
        
它只對Message這個類起做用,並且能夠限制只有管理和刪除權限的用戶能夠執行刪除操做。
而後要將這個表決器添加到AccessDecisionManager中。
<bean id="aclAccessDecisionManager" class="org.springframework.security.vote.AffirmativeBased">
    <property name="decisionVoters">
        <list>
            <bean class="org.springframework.security.vote.RoleVoter"/>
            <ref local="aclMessageDeleteVoter"/>
        </list>
    </property>
</bean>
        
如今AccessDecisionManager中有兩個表決器了,除了默認的RoleVoter以外,又多了一個咱們剛剛添加的aclMessageDeleteVoter。
如今能夠把新的AccessDecisionManager賦予全局方法權限管理器了。
<global-method-security secured-annotations="enabled"
    access-decision-manager-ref="aclAccessDecisionManager"/>
        
而後咱們就能夠在MessageService.java中使用Secured註解,控制刪除操做了。
@Transactional
@Secured("ACL_MESSAGE_DELETE")
public void remove(Long id) {
    Message message = this.get(id);
    list.remove(message);
    ObjectIdentity oid = new ObjectIdentityImpl(Message.class, id);
    mutableAclService.deleteAcl(oid, false);
}
        
實際上,咱們最好不要讓沒有權限的操做者看到remove這個連接,可使用taglib隱藏當前用戶無權看到的信息。
<sec:accesscontrollist domainObject="${item}" hasPermission="8,16">
      |
      <a href="message.do?action=remove&id=${item.id}">Remove</a>
</sec:accesscontrollist>
        
8, 16是acl默認使用的掩碼,8表示DELETE,16表示ADMINISTRATOR,當用戶不具備這些權限的時候,他在頁面上就看不到remove連接,也就沒法執行操做了。
這比讓用戶能夠執行remove操做,而後跑出異常,警告訪問被拒絕要友好得多。

23.4. 控制用戶能夠看到哪些信息

當用戶無權查看一些信息時,咱們須要配置afterInvocation,使用後置判斷的方式,將用戶無權查看的信息,從MessageService返回的結果集中過濾掉。
後置判斷有兩種形式,一種用來控制單個對象,另外一種能夠過濾集合。
<bean id="afterAclRead" class="org.springframework.security.afterinvocation.AclEntryAfterInvocationProvider">
    <sec:custom-after-invocation-provider/>
    <constructor-arg ref="aclService"/>
    <constructor-arg>
        <list>
            <util:constant static-field="org.springframework.security.acls.domain.BasePermission.ADMINISTRATION"/>
            <util:constant static-field="org.springframework.security.acls.domain.BasePermission.READ"/>
        </list>
    </constructor-arg>
</bean>
<bean id="afterAclCollectionRead" class="org.springframework.security.afterinvocation.AclEntryAfterInvocationCollectionFilteringProvider">
    <sec:custom-after-invocation-provider/>
    <constructor-arg ref="aclService"/>
    <constructor-arg>
        <list>
            <util:constant static-field="org.springframework.security.acls.domain.BasePermission.ADMINISTRATION"/>
            <util:constant static-field="org.springframework.security.acls.domain.BasePermission.READ"/>
        </list>
    </constructor-arg>
</bean>
        
afterAclRead能夠控制單個對象是否能夠顯示,afterAclCollectionRead則用來過濾集合中哪些對象能夠顯示。 [6]
對這兩個bean都是用了custom-after-invocation-provider標籤,將它們加入的後置判斷的行列,下面咱們爲MessageService.java中的對應方法添加Secured註解,以後它們就能夠發揮效果了。
@Secured({"ROLE_USER", "AFTER_ACL_READ"})
public Message get(Long id) {
    for (Message message : list) {
        if (message.getId().equals(id)) {
            return message;
        }
    }
    return null;
}
@Secured({"ROLE_USER", "AFTER_ACL_COLLECTION_READ"})
public List getAll() {
    return list;
}
        

以上就是數據庫

相關文章
相關標籤/搜索