2. 核心概念解析
在 Spring Security 中,角色(Role)和权限(GrantedAuthority) 的区别看似微小却至关重要。简单来说:
✅ 权限(GrantedAuthority) 代表细粒度的操作权限
✅ 角色(Role) 本质上是带前缀的粗粒度权限
⚠️ 框架对两者处理方式几乎相同,关键在于我们如何使用
2.1 权限(GrantedAuthority)
可以把每个 GrantedAuthority
理解为独立的操作权限,例如:
READ_AUTHORITY
(读取权限)WRITE_PRIVILEGE
(写入权限)CAN_EXECUTE_AS_ROOT
(超级用户权限)
关键点:权限名称是完全自定义的,没有任何命名限制。当使用
hasAuthority('READ_AUTHORITY')
这类表达式时,我们实现了细粒度的访问控制。
2.2 角色(Role)作为权限
在 Spring Security 中,角色本质上是一种特殊的权限:
- 自动添加
ROLE_
前缀(可配置但默认如此) - 用于实现粗粒度的访问控制
例如 hasRole("ADMIN")
实际等同于 hasAuthority('ROLE_ADMIN')
。
踩坑提醒:很多开发者误以为角色和权限是不同概念,但在框架层面,角色只是带前缀的权限字符串。
3. 角色作为权限容器
除了框架默认用法,我们还可以将角色设计为权限的容器:
graph LR
A[角色] --> B[权限1]
A --> C[权限2]
A --> D[权限3]
这种模式下:
- 角色变成业务导向的概念
- 权限仍是技术实现细节
- 一个角色可包含多个权限(如
ADMIN
角色包含READ
/WRITE
/DELETE
权限)
⚠️ Spring Security 本身不强制规定使用方式,具体实现完全取决于业务需求。
4. 安全配置实战
通过安全配置对比两种控制方式:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers("/protectedbyrole")
.hasRole("USER") // 粗粒度控制
.requestMatchers("/protectedbyauthority")
.hasAuthority("READ_PRIVILEGE") // 细粒度控制
// ...其他配置
}
控制方式 | 适用场景 | 示例 |
---|---|---|
角色(Role) | 业务级分组控制 | hasRole("ADMIN") |
权限(Authority) | 具体操作控制 | hasAuthority("ORDER_DELETE") |
5. 测试数据初始化
开发阶段快速创建测试数据(⚠️ 生产环境勿用):
@Override
@Transactional
public void onApplicationEvent(ContextRefreshedEvent event) {
// 创建基础权限
MyPrivilege readPrivilege = createPrivilegeIfNotFound("READ_PRIVILEGE");
MyPrivilege writePrivilege = createPrivilegeIfNotFound("WRITE_PRIVILEGE");
// 创建角色并关联权限
MyRole userRole = createRoleIfNotFound("ROLE_USER");
userRole.getPrivileges().add(readPrivilege);
}
简单粗暴说明:这里只是演示用,实际项目应通过数据库脚本或Flyway管理数据。
6. 权限映射实现
在 UserDetailsService
中完成权限映射:
private Collection<? extends GrantedAuthority> getAuthorities(Collection<Role> roles) {
List<GrantedAuthority> authorities = new ArrayList<>();
for (Role role : roles) {
// 添加角色本身作为权限
authorities.add(new SimpleGrantedAuthority(role.getName()));
// 添加角色包含的所有权限
authorities.addAll(
role.getPrivileges().stream()
.map(p -> new SimpleGrantedAuthority(p.getName()))
.collect(Collectors.toList())
);
}
return authorities;
}
关键点:
- 角色本身会作为权限加入列表
- 角色关联的细粒度权限也会加入
- 最终返回所有
GrantedAuthority
集合
7. 运行测试验证
启动应用后分步验证:
7.1 测试角色控制
- 访问
http://localhost:8082/protectedbyrole
- 使用
user@example.com
/user
登录 - ✅ 授权成功
- 访问
http://localhost:8082/protectedbyauthority
- ❌ 授权失败
7.2 测试权限控制
- 退出后访问
http://localhost:8082/protectedbyauthority
- 使用
admin@example.com
/admin
登录 - ✅ 授权成功
- 访问
http://localhost:8082/protectedbyrole
- ❌ 授权失败
测试结果说明:不同用户拥有不同级别的权限/角色组合,验证了访问控制的正确性。
8. 总结
Spring Security 中角色与权限的核心区别在于:
维度 | 角色(Role) | 权限(GrantedAuthority) |
---|---|---|
粒度 | 粗粒度(业务分组) | 细粒度(具体操作) |
命名 | 默认 ROLE_ 前缀 |
完全自定义 |
用途 | 分组控制 | 精确控制 |
本质 | 带前缀的权限字符串 | 基础权限单元 |
最佳实践:建议采用角色作为权限容器的设计模式,既保持业务友好性,又实现技术灵活性。