Spring Security 中如何细化权限粒度?

有小伙伴表示微人事(https://github.com/lenve/vhr)的权限粒度不够细 。不过松哥想说的是,技术都是相通的,明白了 vhr 中权限管理的原理,在此基础上就可以去细化权限管理粒度,细化过程和还是用的 vhr 中用的技术,只不过设计层面重新规划而已 。
当然今天我想说的并不是这个话题,主要是想和大家聊一聊 Spring Security 中权限管理粒度细化的问题 。因为这个问题会涉及到不同的权限管理模型,今天和小伙伴们聊一聊~
1.权限管理模型要想将细化权限粒度,我们不可避免会涉及到一些权限模型,例如 ACL、RBAC、DAC、mac 以及 ABAC、PBAC 等 。
在这些众多的权限模型中,我们使用较多的是 RBAC,ACL 也有一些项目在使用,另外几种则使用相对较少 。因此松哥这里重点和大家介绍 ACL 和 RBAC 。
1.1 ACLACL 是一种比较古老的权限控制模型 。英文全称是 Access Control List,中文称作访问控制列表,这是一种面向资源的访问控制模型,所有的权限配置都是针对资源的 。
它的原理是这样:
对于系统中的每一个资源,都会配置一个访问列表,这个列表中记录了用户/角色对于资源的 CURD 权限,当系统需要访问这些资源时,会首先检查列表中是否存在当前用户的访问权限,进而确定当前用户是否可以执行相应的操作 。
ACL 的使用非常简单,搞明白它的原理自己分分钟就能实现 。但是 ACL 有一个明显的缺点,就是需要维护大量的访问权限列表 。大量的访问控制列表带来的问题就是性能下降以及维护复杂 。
1.2 RBACRBAC(Role-based access control)是一种以角色为基础的访问控制,也是目前使用较多的一种权限模型,它有多种不同的变体,松哥后面会专门写一篇文章来介绍 RBAC,这里仅简单科普下 。
RBAC 权限模型将用户按角色进行归类,通过用户的角色来确定用户对某项资源是否具备操作权限 。RBAC 简化了用户与权限的管理,它将用户与角色关联、角色与权限管理、权限与资源关联,这种模式使得用户的授权管理变得非常简单和易于维护 。
1.3 其他下面这些使用常见较少,小伙伴们做一个了解即可,感兴趣的小伙伴也可以自行研究下 。

  • ABAC:这是一种基于属性的访问控制 。
  • PBAC:这是一种基于策略的访问控制 。
  • DAC:除了权限控制,主体也可以将权限授予其他主体 。
  • MAC:资源可以被哪些类别的主体进行哪些操作,主体可以对哪些等级的资源进行哪些操作,这两个条件同时满足时,允许访问 。
2.ACL接下来松哥要和大家仔细介绍一下 ACL 这种权限模型,RBAC 我后面专门写文章介绍,本文先不做讨论 。
Acl 的全称是 Access Control List,也就是我们所说的访问控制列表,是用以控制对象的访问权限的 。Acl 的一个核心思路就是将某个对象的某种权限授予某个用户或某种角色,它们之间的关系是多对多,即一个用户/角色可以具备某个对象的多种权限,某个对象的权限也可以被多个用户/角色所持有 。
举个简单例子:
现在有一个 User 对象,针对该对象有查询、修改、删除等权限,可以将这些权限赋值给某一个用户,也可以将这些权限赋值给某一个角色,当用户具备这些角色时就具有执行相应操作的权限 。
从这个角度看,Acl 是一种粒度非常细的权限控制,它就是专门控制某一个对象的操作权限 。所有的这些权限都记录在数据库中,这带来了另外一个问题就是需要维护的权限数据量非常庞大,不利于后期扩展 。当然,对于一个简单的系统,使用 Acl 还是可以的,没有任何问题 。
2.1 核心概念接下来我们来看看 Acl 中一些核心概念 。
Sid
Sid 代表了用户和角色,它有两种:GrantedAuthoritySid 和 PrincipalSid,前者代表角色,后者代表用户 。在 Spring Security 中,用户和角色信息都是保存在 Authentication 对象中的,即 Sid 是从 Authentication 对象中提取出来的,提取出来的值是 GrantedAuthoritySid+PrincipalSid,而不是其中某一项,具体的提取方法是 SidRetrievalStrategyImpl#getSids,相关源码如下:
public List<Sid> getSids(Authentication authentication) { Collection<? extends GrantedAuthority> authorities = roleHierarchy   .getReachableGrantedAuthorities(authentication.getAuthorities()); List<Sid> sids = new ArrayList<>(authorities.size() + 1); sids.add(new PrincipalSid(authentication)); for (GrantedAuthority authority : authorities) {  sids.add(new GrantedAuthoritySid(authority)); } return sids;}


推荐阅读