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

关键点:

  1. 角色本身会作为权限加入列表
  2. 角色关联的细粒度权限也会加入
  3. 最终返回所有 GrantedAuthority 集合

7. 运行测试验证

启动应用后分步验证:

7.1 测试角色控制

  1. 访问 http://localhost:8082/protectedbyrole
  2. 使用 user@example.com / user 登录
  3. ✅ 授权成功
  4. 访问 http://localhost:8082/protectedbyauthority
  5. ❌ 授权失败

7.2 测试权限控制

  1. 退出后访问 http://localhost:8082/protectedbyauthority
  2. 使用 admin@example.com / admin 登录
  3. ✅ 授权成功
  4. 访问 http://localhost:8082/protectedbyrole
  5. ❌ 授权失败

测试结果说明:不同用户拥有不同级别的权限/角色组合,验证了访问控制的正确性。

8. 总结

Spring Security 中角色与权限的核心区别在于:

维度 角色(Role) 权限(GrantedAuthority)
粒度 粗粒度(业务分组) 细粒度(具体操作)
命名 默认 ROLE_ 前缀 完全自定义
用途 分组控制 精确控制
本质 带前缀的权限字符串 基础权限单元

最佳实践:建议采用角色作为权限容器的设计模式,既保持业务友好性,又实现技术灵活性。


原始标题:Granted Authority Versus Role in Spring Security

» 下一篇: rxjava-jdbc 介绍