5-SpringSecurity:RBAC及方法授权

简介: 5-SpringSecurity:RBAC及方法授权

背景


本系列教程,是作为团队内部的培训资料准备的。主要以实验的方式来体验 SpringSecurity 的各项Feature。


RBAC是什么?Role Based Access Control,关于RBAC的介绍,网上资源很多,这里仅简单描述下。


  • 用户-权限:user, user-permission, permission


  • 用户-角色-权限:user, user-role, role, role-permission, permission

新建一个 SpringBoot 项目,起名 springboot-security-rbac ,核心依赖为 WebSpringSecurity


<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>


实验0:基本授权


创建资源接口: /user/add/user/query ,以及默认的home路径 / ,用以展示登录用户信息,若未登录,则展示匿名用户信息。

@RestController
@Slf4j
public class RoleAccessController {
    @GetMapping(value = "/user/add")
    public String accessResource1() {
        return " Access Resource 1: Add User";
    }
    @GetMapping(value = "/user/query")
    public String accessResource2() {
        return " Access Resource 2: Query User";
    }
    @GetMapping(value = "/")
    public String index() {
        log.info(SecurityContextHolder.getContext().getAuthentication().toString());
        return "Welcome " + SecurityContextHolder.getContext().getAuthentication();
    }
}
  • 在内存中创建两个用户:


  • dev用户具有dev与test角色;
  • test用户仅具有test角色;


  • 配置资源授权:


  • /user/add 需要有dev角色才可访问;
  • /user/query 需要有test角色才可访问;
@Configuration
@Slf4j
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // There is no PasswordEncoder mapped for the id "null"
        PasswordEncoder encoder = passwordEncoder();
        String yourPassword = "123";
        System.out.println("Encoded password: " + encoder.encode(yourPassword));
        // Config account info and permissions
        auth.inMemoryAuthentication()
                .withUser("dev").password(encoder.encode(yourPassword)).roles("dev", "test")
                .and()
                .withUser("test").password(encoder.encode(yourPassword)).authorities("ROLE_test");
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/user/add").hasRole("dev")
                .antMatchers("/user/query").hasAuthority("ROLE_test")
                .antMatchers("/user/**").authenticated()
                .anyRequest().permitAll() // Let other request pass
                .and()
                .formLogin();
    }
}


实验01:匿名用户


image.png


实验02:dev用户



image.png

dev 用户同时具备 ROLE_devROLE_test 两个角色,此时访问 /user/add/user/query 都会成功;


实验03:test用户


image.png

test 用户仅具有 ROLE_test 角色,此时访问 /user/add 失败,访问 /user/query 成功;


Note:


  • 上述代码中,在分配角色或授权时使用了 roles()authorities() ,两者区别如下:


  1. 使用 roles() 时,参数不可加 ROLE_ 前缀,否则报错。因为 roles() 自动会加这个前缀,参考源码 User类


  1. 使用 authorities() 时,则没有限制,配合 hasAuthority 使用时,权限命名一致即可。


public UserBuilder roles(String... roles) {
  List<GrantedAuthority> authorities = new ArrayList<>(roles.length);
  for (String role : roles) {
    Assert.isTrue(!role.startsWith("ROLE_"),
        () -> role + " cannot start with ROLE_ (it is automatically added)");
    authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
  }
  return authorities(authorities);
}


  • 另外, roles()authorities() 不可在一个用户上同时使用,否则会发生覆盖,仅有最后一个会生效,参考源码 User类

image.png

image.png


实验1:方法授权-securedEnabled


在启动类或配置类上添加注解: @EnableWebSecurity@EnableGlobalMethodSecurity(securedEnabled=true) ,表示启用 SpringSecurity 默认的方法授权。

@SpringBootApplication
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled=true)
public class SpringbootSecurityRbacApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootSecurityRbacApplication.class, args);
    }
}

为了方便演示,这里新建一个Controller: SecuredAccessController ,资源请求以及用户授权都与上一个实验相同,这里可以删除 SecurityConfig 类中关于 http 请求权限的配置。

@RestController
@RequestMapping("/user0")
@Slf4j
public class SecuredAccessController {
    @GetMapping(value = "/add")
    @Secured({"ROLE_dev", "ROLE_test"}) // only support OR relation
    public String accessResource1() {
        return " Access Resource 1: Add User";
    }
    @GetMapping(value = "/query")
    @Secured({"ROLE_test"})
    public String accessResource2() {
        return " Access Resource 2: Query User";
    }
    @GetMapping(value = "/")
    public String index() {
        log.info(SecurityContextHolder.getContext().getAuthentication().toString());
        return "Welcome " + SecurityContextHolder.getContext().getAuthentication();
    }
}

结果如下:


  • dev用户可以访问 /user0/add/user0/query
  • test用户也可以访问 /user0/add/user0/query


Note:


  1. @Secured({"ROLE_dev", "ROLE_test"}) 这里采用了角色组合,是“或”的关系。


  1. 鉴于 SpringSecurity 默认的方法授权的局限性,实际中更多地会使用 PreAuthorize ,可以实现“或”、“与”的关系,支持 Spring EL表达式 ,看下个实验。


实验2:方法授权-prePostEnabled


配置类的注解增加属性配置 prePostEnabled=true ,变成: @EnableGlobalMethodSecurity(securedEnabled=true, prePostEnabled=true)


为了方便演示,这里新建一个Controller: PrePostAccessController ,资源请求以及用户授权都与上一个实验相同,这里可以删除 SecurityConfig 类中关于 http 请求权限的配置。

@RestController
@RequestMapping("/user1")
@Slf4j
public class PrePostAccessController {
    @GetMapping(value = "/add")
//    @PreAuthorize("hasRole('ROLE_dev')")
    @PreAuthorize("hasRole('dev')")
//    @PreAuthorize("hasAnyRole('ROLE_dev', 'ROLE_test')")
//    @PreAuthorize("hasRole('ROLE_dev') and hasRole('ROLE_test')")
    public String accessResource1() {
        return " Access Resource 1: Add User";
    }
    @GetMapping(value = "/query")
    @PreAuthorize("hasAuthority('ROLE_test')")
    public String accessResource2() {
        return " Access Resource 2: Query User";
    }
    @GetMapping(value = "/")
//    @PreAuthorize("authenticated")
    public String index() {
        log.info(SecurityContextHolder.getContext().getAuthentication().toString());
        return "Welcome " + SecurityContextHolder.getContext().getAuthentication();
    }
    @GetMapping(value = "/res")
    @PostAuthorize("returnObject==true")
    public boolean response() {
        int i = new Random().nextInt();
        log.info("Response, {}", i);
        return i > 0;
    }
}

分别演示 PreAuthorize 本身及多个权限组合与 PostAuthorize ,可打开注释进行测试,具体结果:略。


实验3:方法授权-jsr250Enabled


其实,除了 securedEnabledprePostEnabled@EnableGlobalMethodSecurity 还有第三个选项: jsr250Enabled

image.png

具体使用方法,此处不作演示了。


Note:虽然 SpringSecurity@EnableGlobalMethodSecurity 注解提供了三种选项来使用方法级别的授权,但是实际使用时不建议混合使用,参考官方文档关于这一点的说明:

image.png

目录
相关文章
|
Java API 数据安全/隐私保护
(工作经验)优雅实现接口权限校验控制:基于自定义注解、AOP与@ConditionalOnProperty配置开关的通用解决方案
(工作经验)优雅实现接口权限校验控制:基于自定义注解、AOP与@ConditionalOnProperty配置开关的通用解决方案
497 1
|
安全 Java API
Spring Security实现RBAC权限管理
Spring Security实现RBAC权限管理 一简介 在企业应用中,认证和授权是非常重要的一部分内容,业界最出名的两个框架就是大名鼎鼎的 Shiro和Spring Security。
7034 0
|
安全 Java Linux
docker阿里云镜像加速
我们都知道因为某些原因我们访问外网都是比较慢的,比如我们使用maven下载依赖时是一个道理,同样的使用docker从docker.hub上下载镜像也是比较慢的。针对这种访问官网比较慢的情况有两种方案,第一种就是使用国内的仓库,第二种就是使用一个加速器。这里我们配置docker的镜像加速从来来实现提速。
14034 1
docker阿里云镜像加速
|
弹性计算 Ubuntu Linux
Ubuntu操作系统配置阿里云镜像方法一
Ubuntu操作系统配置阿里云镜像方法一
Ubuntu操作系统配置阿里云镜像方法一
|
安全 Java 数据安全/隐私保护
用Spring Security快速实现 RBAC模型案例
RBAC模型是一种常见的权限管理机制,它将权限赋予角色而非用户,用户通过角色获取权限。主要组件包括用户、角色、权限、会话、角色分配和权限分配。RBAC简化了权限管理,方便权限变更,常用于大型组织。
789 0
|
存储 Java Maven
Maven依赖全爆红,一刷新就JAVA_HOME environment variable is not defined correctlyThis environment variable is
Maven依赖全爆红,一刷新就JAVA_HOME environment variable is not defined correctlyThis environment variable is
|
关系型数据库 PostgreSQL
【一文搞懂PGSQL】5. 流复制
PostgreSQL流复制架构支持多种常见配置,包括基本的主从复制、结合PGPool-II的读写分离以及使用repmgr实现高可用性。基础环境中,主节点与备用节点分别位于不同IP。配置涵盖创建复制用户、调整核心参数以支持流复制,并确保归档与日志功能正常工作。从节点需通过备份恢复并配置为待机模式,以实现数据同步。此外,还介绍了如何验证复制状态及手动切换主从节点的方法,以及同步复制参数的配置细节。
|
安全 网络协议 NoSQL
SSRF漏洞深入利用与防御方案绕过技巧
SSRF漏洞深入利用与防御方案绕过技巧
951 0
|
消息中间件 Arthas Java
RocketMQ—一次连接namesvr失败的案例分析
项目组在使用RocketMQ时遇到Consumer连接Name Server失败的问题,异常显示连接特定地址失败。通过Arthas工具逐步分析代码执行路径,定位到创建Channel返回空值导致异常。进一步跟踪发现,问题源于Netty组件在初始化`ByteBufAllocator`时出现错误。分析依赖后确认存在Netty版本冲突。解决方法为排除冲突的Netty包,仅保留兼容版本。
993 0
RocketMQ—一次连接namesvr失败的案例分析
|
安全 Java 数据安全/隐私保护
SpringSecurity2 --- 通用权限管理模型
本文介绍了两种常见的权限管理模型:ACL和RBAC。ACL(访问控制列表)是一种传统的模型,允许将权限直接赋予用户或角色,形成多对多的关系。开发者可以独立实现ACL,Spring Security也提供了支持。相比之下,RBAC(基于角色的访问控制)更现代,不直接给用户分配权限,而是通过角色来间接控制。RBAC强调最小权限、职责分离和数据抽象,分为RBAC0、RBAC1、RBAC2和RBAC3四个级别,其中RBAC1引入角色继承,RBAC2和RBAC3则涉及职责分离的概念,包括静态和动态的角色互斥。