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

目录
相关文章
|
4月前
|
安全 数据安全/隐私保护
SpringSecurity1 --- 什么是权限管理
让我们逐步了解什么是springSecurity
|
安全 Java 数据库
Security授权实现
Security授权实现
71 0
|
6月前
|
Java 数据安全/隐私保护
Shiro - 授权那些事
Shiro - 授权那些事
55 0
|
Java
08 Shrio 授权的三种方式
08 Shrio 授权的三种方式
41 1
|
6月前
|
前端开发 Java 数据库
基于RBAC的权限模型+shiro+springboot实现的系统登陆权限认证模块
基于RBAC的权限模型+shiro+springboot实现的系统登陆权限认证模块
102 0
|
JSON 前端开发 数据格式
六.SpringSecurity基础-认证授权结果处理
SpringSecurity基础-认证授权结果处理
|
JSON 前端开发 数据格式
SpringSecurity基础-认证授权结果处理
在传统的应用中,认证成功后页面需要跳转到认证成功页面或者跳转到个人中心页,但是在前后端分离的项目通常是使用Ajax请求完成认证,这时候我们需要返回一个JSON结果告知前端认证结果,然后前端自行跳转页面。 要做到上述功能,我们需要自定义认证成功处理器实现AuthenticationSuccessHandler接口复写 onAuthenticationSuccess方法,该方法其中一个参数是Authentication ,他里面封装了认证信息,用户信息UserDetails等,我们需要在这个方法中使用Response写出json数据即可
138 0
|
移动开发 安全 前端开发
SpringSecurity认证和授权
目前,我们的测试环境,是谁都可以访问的,我们使用 Spring Security 增加上认证和授权的功能
SpringSecurity认证和授权
|
前端开发 Java 数据安全/隐私保护
Servlet实现RBAC权限管理(二)中
Servlet实现RBAC权限管理(二)
319 0
Servlet实现RBAC权限管理(二)中
|
Java 数据安全/隐私保护
Servlet实现RBAC权限管理(二)下
Servlet实现RBAC权限管理(二)
261 0
Servlet实现RBAC权限管理(二)下