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:

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.
                                          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.
         -->

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;
}

以上就是

[收藏]Spring Security中的ACL的更多相关文章

  1. Spring Security中html页面设置hasRole无效的问题

    Spring Security中html页面设置hasRole无效的问题 一.前言 学了几天的spring Security,偶然发现的hasRole和hasAnyAuthority的区别.当然,可能 ...

  2. Spring Security 中的过滤器

    本文基于 spring-security-core-5.1.1 和 tomcat-embed-core-9.0.12. Spring Security 的本质是一个过滤器链(filter chain) ...

  3. Spring Security 中的 Bcrypt

    最近在写用户管理相关的微服务,其中比较重要的问题是如何保存用户的密码,加盐哈希是一种常见的做法.知乎上有个问题大家可以先读一下: 加盐密码保存的最通用方法是? 对于每个用户的密码,都应该使用独一无二的 ...

  4. 浅谈使用spring security中的BCryptPasswordEncoder方法对密码进行加密与密码匹配

    浅谈使用springsecurity中的BCryptPasswordEncoder方法对密码进行加密(encode)与密码匹配(matches) spring security中的BCryptPass ...

  5. 看源码,重新审视Spring Security中的角色(roles)是怎么回事

    在网上看见不少的博客.技术文章,发现大家对于Spring Security中的角色(roles)存在较大的误解,最大的误解就是没有搞清楚其中角色和权限的差别(好多人在学习Spring Security ...

  6. 六:Spring Security 中使用 JWT

    Spring Security 中使用 JWT 1.无状态登录 1.1 什么是有状态? 1.2 什么是无状态 1.3 如何实现无状态 2.JWT 2.1 JWT数据格式 2.2 JWT交互流程 2.3 ...

  7. 五:Spring Security 中的角色继承问题

    Spring Security 中的角色继承问题 以前的写法 现在的写法 源码分析 SpringSecurity 在角色继承上有两种不同的写法,在 Spring Boot2.0.8(对应 Spring ...

  8. Spring Security中实现微信网页授权

    微信公众号提供了微信支付.微信优惠券.微信H5红包.微信红包封面等等促销工具来帮助我们的应用拉新保活.但是这些福利要想正确地发放到用户的手里就必须拿到用户特定的(微信应用)微信标识openid甚至是用 ...

  9. 干货|一个案例学会Spring Security 中使用 JWT

    在前后端分离的项目中,登录策略也有不少,不过 JWT 算是目前比较流行的一种解决方案了,本文就和大家来分享一下如何将 Spring Security 和 JWT 结合在一起使用,进而实现前后端分离时的 ...

随机推荐

  1. openSUSE之SSH登录

    环境: 1:操作系统:openSUSE 2:SSH工具:Xshell 3:virtualbox 解决问题:Xshell通过ssh登录openSUSE进行操作. 1:virtualbox安装好openS ...

  2. 第六章_PHP数组(二)

    这篇随笔是对预定义数组变量的总结.通过预定义数组变量,我们可以获得系统环境.用户对话.表单数据等信息. 1.服务器变量:$_SERVER 利用foreach语句打印$_SERVER中的所有元素: &l ...

  3. WinForm中为按钮添加键盘快捷键,超简单,亲测有效

    博主并没有多少Windows平台下程序的开发经验,一年前使用过MFC设计过一个指纹识别系统的页面,后来就没有使用过几次.现在C#课程实验要求实现一个简易计算器,为了便于快速录入数据,使用键盘会比使用鼠 ...

  4. 反编译工具(.NET Reflector )

    1.下载安装原程序,然后运行 Reflector.exe 2.运行注册机,点击 Generate 获得序列号 3.把序列号填写到 Reflector 软件上4.断开网络,点击 Activate 激活( ...

  5. 史上最全Vim快捷键键位图(入门到进阶)

    经典版 下面这个键位图应该是大家最常看见的经典版了. 对应的简体中文版 其实经典版是一系列的入门教程键位图的组合结果,下面是不同编辑模式下的键位图. 入门版 基本操作的入门版. 进阶版 增强版 下图是 ...

  6. C++十进制转换为二进制

    题目内容:将十进制整数转换成二进制数. 输入描述:输入数据中含有不多于50个的整数n(-231<n<231). 输出描述:对于每个n,以11位的宽度右对齐输入n值,然后输出“-->” ...

  7. web app 开发

    去除手机浏览器标签默认高亮边框 -webkit-tap-highlight-color 属性 属性描述:这个属性可以指设置透明度.如果未设置透明度,iOS上的Safari会给予颜色一个默认的透明度.把 ...

  8. 新浪博客如何显示高亮代码,DIY

    新浪博客对代码的支持功能不尽完美,或者说一点都不好,可是对于一个追求完美的技术痴而言,代码不能够完美的显示,心里总有那么一些不爽,那么如何在新浪中显示那些带颜色的代码呢?经过探究,可以如下设置:   ...

  9. IOS学习2

    1. #import,#include 和@class的区别 都引用一个类,根本定义区别:#include ,#import会把所有的copy一份到该文件 #import比#include的优势,im ...

  10. 【摘抄】Application.StartupPath和System.Environment.CurrentDirectory的区别

    System.Environment.CurrentDirectory的含义是获取或设置当前工作路径,而Application.StartupPath是获取程序启动路径,表面上看二者没什么区别,但实际 ...