[收藏]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;
}
        

以上就是数据库

相关文章
相关标签/搜索