Appearance
第13章 权限管理
认证和授权是 Spring Security 中的两大核心功能,所谓授权也就是我们日常所说的权限管理。Spring Security 中对这两者做了很好的解耦,无论使用哪种认证方式,都不影响权限管理功能的使用,这也是 Spring Secuity 受欢迎的原因之一。同时 Spring Security 中对于 RBAC ACL,等不同权限模型也都有很好的支持,本章我们将学习 Spring Security 中的权限管理。
本章涉及的主要知识点有:
- 什么是权限管理。
- Spring Security 权限管理策略,
- 权限管理核心概念。
- 基于 URL 地址的权限管理。
- 基于方法的权限管理。
13.1 什么是权限管理
和其他安全管理框架类似,Spring Security 提供的功能主要包含两方面:认证和授权。在前面的章节中,我们对于认证已经做了很多介绍,SpringSecurity 支持多种不同的认证方式,但是无论开发者使用哪种认证方式,都不会影响授权功能的使用,SpringSecurity 很好地实现了认证和授权两大功能的解耦,这也是它受欢迎的原因之一。
认证就是确认用户身份,也就是我们常说的登录。授权则是根据系统提前设置好的规则,给用户分配可以访问某一资源的权限,用户根据自己所具有的权限,去执行相应的操作。在我们所见到的大部分系统中,无论是操作系统还是企业级应用系统,都能看到权限管理功能。
一个优秀的认证+授权系统可以为我们的应用系统提供强有力的安全保障功能。
13.2 Spring Security权限管理策略
从技术上来说,Spring Security 中提供的权限管理功能主要有两种类型:
- 基于过滤器的权限管理(FilterSecurityInterceptor )。
- 基于AOP 的权限管理(MethodSecurityInterceptor )。
基于过滤器的权限管理主要用来拦截 HTTP 请求,拦截下来之后,根据 HTTP 请求地址进行权限校验。
基于 AOP 的权限管理则主要用来处理方法级别的权限问题。
当需要调用某一个方法时,通过 AOP 将操作拦截下来,然后判断用户是否具备相关的权限,如果具备,则允许方法调用,否则禁止方法调用。
本章接下来的介绍都是基于这两种权限管理方式展开的。
13.3 核心概念
为了方便大家理解后面的内容,我们先对 Spring Security 中一些权限相关的核心概念做个简单介绍。
13.3.1 角色与权限
根据前面 1.3.1 小节的介绍,大家已经知道在 Spring Security 中,当用户登录成功后,当前登录用户信息将保存在 Authentication 对象中,Authentication 对象中有一个 getAuthorities 方法,用来返回当前对象所具备的权限信息,也就是已经授予当前登录用户的权限。
getAuthorities 方法返回值是 Collection<?extends GrantedAuthority>,即集合中存放的是GrantedAuthority 的子类,当需要进行权限判断的时候,就会调用该方法获取用户的权限,进而做出判断。
无论用户的认证方式是用户名/密码形式、RememberMe 形式,还是其他如 CAS、OAuth2 等认证方式,最终用户的权限信息都可以通过 getAuthorities 方法获取。这就是我们前面所讲的,无论用户采用何种认证方式,都不影响授权。
那么对于 Authentication#getAuthorities 方法的返回值,我们应该理解为用户的角色还是用户的权限呢?
从设计层面来讲,角色和权限是两个完全不同的东西:权限就是一些具体的操作,例如针对员工数据的读权限(READ EMPLOYEE)和针对员工数据的写权限 (WRITE EMPLOYEE);角色则是某些权限的集合,例如管理员角色 ROLE_ADMIN、普通用户角色 ROLE_USER。
从代码层面来讲,角色和权限并没有太大的不同,特别是在 Spring Security 中,角色和权限的处理的方式基本上是一样的,唯一的区别在于 Spring Security 在多个地方会自动给角色添加一个 ROLE 前缀,而权限则不会自动添加任何前缀。这里读者需要特别注意,我们在后面的讲解中,遇到自动添加 ROLE 前缀的地方也都会和大家重点说明。
至于 Authentication#getAuthorities 方法的返回值,则要分情况来对待:
(1)如果权限系统设计比较简单,就是用户<=>权限<->资源三者之间的关系,那么getAuthorities 方法的含义就很明确,就是返回用户的权限。
(2)如果权限系统设计比较复杂,同时存在角色和权限的概念,如用户<=>角色<=>权限<=>资源(用户关联角色、角色关联权限、权限关联资源),此时我们可以将 getAuthorities方法的返回值当作权限来理解。由于 Spring Security 并未提供相关的角色类,因此这个时候需要我们自定义角色类。
对于第一种情况,大家都好理解,这里不再赘述。对于第二种情况,我们简单介绍一下。
如果系统同时存在角色和权限,我们可以使用 GrantedAuthority 的实现类 SimpleGrantedAuthority 来表示一个权限,在 SimpleGrantedAuthority 类中,我们可以将权限描述为一个字符串,如READ_EMPLOYEE、WRITE_EMPLOYEE。据此,我们定义角色类如下:
java
class Role implements GrantedAuthority {
private String name;
private List<SimpleGrantedAuthority> allowedOperations = new ArrayList<>();
@Override
public String getAuthority() {
return name;
}
//省略 getter/setter
}
角色继承自 GrantedAuthority,一个角色对应多个权限。然后在定义用户类的时候,将角色转为权限即可:
java
class User implements UserDetails {
private List<Role> roles = new ArrayList<>();
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (Role role : roles) {
authorities.addAll(role.getAllowedOperations());
return authorities.stream().distinct().collect(Collectors.toList());
}
}
//省略 getter/setter
}
整体上来说,设计层面上,角色和权限是两个东西;代码层面上,角色和权限其实差别不大,注意区分即可。