在本节中,补充下角色继承的知识点。角色继承其实是一个十分常见的需求,因为一般系统中角色权限呈金字塔型,高层用户拥有底层用户的权限。

例如存在以下角色:普通用户、VIP 用户、SVIP 用户、星悦会员,那么对应的权限可以是“星悦会员 > SVIP 用户 > VIP 用户 > 普通用户”。那么如何在 Spring Security 中实现这样的功能呢?

引言

为了简便起见,我直接使用《SpringBoot 集成 Spring Security(1)——入门程序》 的代码。

在该章中,我们存在两个角色,ROLE_ADMINROLE_USER,并且经过我们的实验,/admin 接口只有 ROLE_ADMIN 有权限,/user 接口只有 ROLE_USER 有权限。

但是如果我想让 ROLE_ADMIN 用户继承 ROLE_USER 用户的所有权限,该如何做呢?

RoleHierarchy

这里就需要引入 RoleHierarch了,我们只需要自定义一个 RoleHierarchy,并将其注入容器即可。修改 WebSecurityConfig,在其中注入 RoleHierarchy:

@Bean
public RoleHierarchy roleHierarchy(){
    RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
    String hierarchy = "ROLE_ADMIN > ROLE_USER";
    roleHierarchy.setHierarchy(hierarchy);
    return roleHierarchy;
}

roleHierarchy.setHierarchy() 指定了角色的继承关系,参数就是一个字符串,比大小即可,是不是非常简单?

让我们使用 ROLE_ADMIN 账号登陆,发现原本无法访问的 /user 接口也可以访问了:

源码

角色关系的实现也比较简单,本质就是将字符串使用正则切分,并将角色关系存放进一个 Map 中,map 的 key 是大的角色,value 是一个 Set,存放所有比它小的角色。然后交由后续处理,有兴趣的可以继续阅读源码。

buildRolesReachableInOneStepMap()

如果有多个继承关系,在 SpringBoot 2.1 中,就应该改写为:

@Bean
public RoleHierarchy roleHierarchy() {
    String separator = System.lineSeparator();
    
    RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
    String hierarchy = "ROLE_ADMIN > ROLE_USER " + separator + " ROLE_USER > ROLE_TOURISTS";
    roleHierarchy.setHierarchy(hierarchy);
    return roleHierarchy;
}

另外换行符大家都知道在不同系统中表示不一样,例如 Windows 中为 \r\n,Mac 为 \r,Linux 为 \n,因此以上代码我是用的 java.lang 包的 System 类中封装的方法,不用判断当前操作系统。