[收藏]SpringSecurity中的ACL
ACL即访问控制列表(Access Controller List),它是⽤来做细粒度权限控制所⽤的⼀种权限模型。对ACL最简单的描述就是两个业务员,每个⼈只能查看操作⾃⼰签的合同,⽽不能看到对⽅的合同信息。
下⾯我们会介绍Spring Security中是如何实现ACL的。
23.1. 准备数据库和aclService
ACL所需的四张表,表结构见附录:。
然后我们需要配置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>
在l中配置对应的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:
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.
The following attributes are optional:
timeToIdleSeconds              - Sets the time to idle for an element before it expires.
< 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.
< 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.
-->
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, Id());
MutableAcl acl = ateAcl(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 = (id);
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.AclEntryAfterInvocationP
rovider">
<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>
springframework作用<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则⽤来过滤集合中哪些对象可以显⽰。[]
对这两个bean都是⽤了custom-after-invocation-provider标签,将它们加⼊的后置判断的⾏列,下⾯我们为MessageService.java中的对应⽅法添加Secured注解,之后它们就可以发挥效果了。
@Secured({"ROLE_USER", "AFTER_ACL_READ"})
public Message get(Long id) {
for (Message message : list) {
if (Id().equals(id)) {
return message;
}
}
return null;
}
@Secured({"ROLE_USER", "AFTER_ACL_COLLECTION_READ"}) public List getAll() {
return list;
}
以上就是

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。