基于SpringBoot学习RBAC(角色-权限-控制)

前言

本博客旨在记录个人的一个RBAC学习过程,因为刚学SpringBoot,RBAC也涉及到数据库的知识,本文亦可作为SpringBoot及数据库的一个入门参考。

1.RBAC

用过linux系统的应该都知道在linux系统中每个文件都有着r可读w可写x可执行三种权限,不同的用户对同一个文件也有着不同的权限,这和RBAC是类似的,RBAC全称Role-based access control(基于用户的权限控制),为了描述RBAC,本文将建立如下5张表,
user、role、permission、user_role、role_permission
RBAC数据表
从图可以看出有5个实体类,用户,角色,权限,用户角色,角色权限,最终我们要达到的目的是输入一个用户,输出此用户的所有权限。看到这有些人可能会想为什么不直接建立一个用户-权限表,在用户少的时候当然是可以的,但若有成千上万个用户,这个表建立及修改就比较麻烦了,但加上一个角色后,耦合性就大大降低,建立修改方便多了。
为了进行测试,在每个表中插入一些数据。

user
id username username_zh
1 user1 用户1
2 user2 用户2
3 user3 用户3
role
id rolename rolename_zh
1 admin 管理员
2 assistant 助理
3 consumer 普通用户
permission
id permissionname permissionname_zh
1 read
2 write
3 delete 删除
user_role
id uid rid
1 1 1
2 2 2
3 3 3
role_permission
id rid pid
1 1 1
2 1 2
3 1 3
4 2 1
5 2 2
6 3 1
从表中可以明显看出用户1角色为管理员,用户2角色为助理,用户3角色为普通用户 而管理员的权限有读写删除,助理的权限有读写,普通用户的权限有读,所以用户1权限为读写删除,用户2权限为读写,用户3权限为读,到此RBAC的原理基本就讲清楚了。下面我们将通过SpringBoot来实现我们预期的功能:输入一个用户,输出此用户的所有权限。 # 2.Springboot 本文将基于idea建立SpringBoot工程 File-->new-->project

springboot工程建立图解
springboot工程建立图解
springboot工程建立图解
springboot工程建立图解
这样一个简单的SpringBoot工程就建立好了。
接着我们在数据库中建立上文提过的5张表。
本博客中我们将采用Spring-data-jpa实现mysql数据库的读写,为此在maven库中添加以下两个依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>8.0.17</scope>
    </dependency>

接着在SpringBoot工程的java子路径下建立如下四个目录
springboot工程目录
这几个目录对应着SpringMvc模型,Controller为控制层,dao为持久层,services为业务逻辑层,pojo为持久层的实体类,下面开始编写具体类。
为了方便,本文将使用全注解的方式进行配置,所以在编写具体类之前,首先了解一下相关注解。

Spring相关注解

Spring框架相关注解
@Autowired
@Autowired顾名思义,就是自动装配,其作用是为了消除代码Java代码里面的getter/setter与bean属性中的property。当然,getter看个人需求,如果私有属性需要对外提供的话,应当予以保留。
@Autowired默认按类型匹配的方式,在容器查找匹配的Bean,当有且仅有一个匹配的Bean时,Spring将其注入@Autowired标注的变量中。

@Qualifier
如果容器中有一个以上匹配的Bean,则可以通过@Qualifier注解限定Bean的名称

@Resource
@Resource注解与@Autowired注解作用非常相似
这是详细一些的用法,说一下@Resource的装配顺序:
(1)、@Resource后面没有任何内容,默认通过name属性去匹配bean,找不到再按type去匹配
(2)、指定了name或者type则根据指定的类型去匹配bean
(3)、指定了name和type则根据指定的name和type去匹配bean,任何一个不匹配都将报错
然后,区分一下@Autowired和@Resource两个注解的区别:
(1)、@Autowired默认按照byType方式进行bean匹配,@Resource默认按照byName方式进行bean匹配
(2)、@Autowired是Spring的注解,@Resource是J2EE的注解,这个看一下导入注解的时候这两个注解的包名就一清二楚了
Spring属于第三方的,J2EE是Java自己的东西,因此,建议使用@Resource注解,以减少代码和Spring之间的耦合。

@Service
@Service注解,其实做了两件事情:
(1)、声明此类是一个业务层Bean,其他的类可以使用@Autowired自动入。
(2)、bean中的id是类名且首字母小写。
若想改bean的名字可以在@service里加参数@Service(“name”)

@Scope
@Scope(“singleton”)表示bean为单例@Scope("prototype ")表示bean原型即每次都会new一个新的出来

@Controller
@Controller用于标注控制层组件,也就是Action

@ Repository
@Repository对应数据访问层Bean

@Component
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注,此注解不建议使用。

Spring data jpa相关注解
@Entity
@Entity表明该类是一个实体类 它默认对应数据库中的user(class字段都转为小写,驼峰命名转换为_连接)

@Table
@Table 当实体类与其数据库不同名时需要使用@Table注解说明,该标注与@Entity 注解并列使用。@Table注解的常用选项是 name,用于指明数据库的表名

@JsonIgnoreProperties
@JsonIgnoreProperties 标记不生成json对象的属性,由于jpa底层是hibernate,hibernate会给被管理的pojo加入一个hibernateLazyInitializer属性,jsonplugin会把hibernateLazyInitializer也拿出来操作,并读取里面一个不能被反射操作的属性会产生异常,所以在生成json对象时要忽略这个属性

@Id
@Id 用于声明一个实体类的属性映射为数据库的主键列

@GeneratedValue
@GeneratedValue用于标注主键的生成策略,通过strategy属性指定。默认情况下,JPA自动选择一个最合适底层数据库的主键生成策略:SqlServer对应identity,MySql对饮auto increment。
在javax.persistence.GenerationType中定义了以下几种可供选择的策略:
  — IDENTITY:采用数据库ID自增长的方式来自增主键字段,Oracle不支持这种方式(oracle12g后,应该支持了。);
  — AUTO:JPA自动选择合适的策略,是默认选项;
  — SEQUENCE:通过序列产生主键,通过@SequenceGenerator注解指定序列名,MySql不支持这种方式。
  — TABLE:通过表产生键,框架借助由表模拟序列产生主键,使用该策略可以使应用更易于数据库移植。
  
@Column
当实体的属性与其映射的数据库表的列不同名时需要使用@Column标注说明,该属性通常置于实体的属性声明语句之前,还可与@Id标注一起使用。
@Column标注的常量属性是name,用于设置映射数据库表的列名。

@Transient
表示该属性并非一个到数据库表的字段的映射,ORM框架将忽略该属性

@Temporal
在核心的JAVA API中并没有定义Date类型的精度(temporal precision)。而在数据库中,表示Date类型的数据类型有DATE,TIME和TEIMSTAMP三种精度(即单纯的日期,时间,或者两者兼备)。在运行属性映射是可使用@Temporal注解来调整Date精度。

下面开始创建相关类,首先是pojo实体类

@Entity
@Table(name = "user")
@JsonIgnoreProperties({"handler","hibernateLazyInitializer"})
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    int id;
    String username;
    @Column(name = "username_zh")
    String usernameZh;

@Entity
@Table(name = "role")
@JsonIgnoreProperties({"handler", "hibernateLazyInitializer"})
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    int id;

    String rolename;
    @Column(name = "rolename_zh")
    String rolenameZh;

@Entity
@Table(name = "permission")
@JsonIgnoreProperties({"handler", "hibernateLazyInitializer"})
public class Permission {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    int id;
    String permissionname;
    @Column(name = "permissionname_zh")
    String permissionnameZh;

@Entity
@Table(name = "user_role")
@JsonIgnoreProperties({"handler", "hibernateLazyInitializer"})
public class UserRole {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    int id;
    int uid;
    int rid;

@Entity
@Table(name = "role_permission")
@JsonIgnoreProperties({"handler", "hibernateLazyInitializer"})
public class RolePermission {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    int id;
    int rid;
    int pid;

Dao层类

public interface UserDAO extends JpaRepository<User,Integer> {
    User findByUsername(String username);
}

public interface RoleDAO extends JpaRepository<Role, Integer> {
    Role findById(int id);
}

public interface PermissionDAO extends JpaRepository<Permission, Integer> {
     Permission findById(int id);
}

public interface UserRoleDAO extends JpaRepository<UserRole,Integer> {
    List<UserRole> findAllByUid(int uid);
}

public interface RolePermissionDAO extends JpaRepository<RolePermission,Integer> {
    List<RolePermission> findAllByRid(int rid);
}

service层类

@Service
public class UserService {
    @Autowired
    UserDAO userDAO;
    public User findByUserName(String username) {
        return userDAO.findByUsername(username);
    }
}

@Service
public class RoleService {
    @Autowired
    RoleDAO roleDAO;
    public Role findRoleById(int rid){
        return roleDAO.findById(rid);
    }
}

@Service
public class PermissionService {
    @Autowired
    PermissionDAO permissionDAO;
    public Permission findPermissionById(int mid){
        return permissionDAO.findById(mid);
    }
}

@Service
public class UserRoleService {
    @Autowired
    UserRoleDAO userRoleDAO;
    public List<UserRole> findRolesByUser(User user){
        int uid = user.getId();
        return userRoleDAO.findAllByUid(uid);
    }
}

@Service
public class RolePermissionService {
    @Autowired
    RolePermissionDAO rolePermissionDAO;
    public List<RolePermission> findAllByRole(Role role){

        return rolePermissionDAO.findAllByRid(role.getId());
    }
}

控制层类

@RestController
public class MenuController {
    @Autowired
    UserService userService;
    @Autowired
    UserRoleService userRoleService;
    @Autowired
    RolePermissionService rolePermissionService;
    @Autowired
    RoleService roleService;
    @Autowired
    PermissionService permissionService;
    @GetMapping("/api/permission")
    public List<Permission> permission(String username) {
        User user = userService.findByUserName(username);
        List<UserRole> userRoles = userRoleService.findRolesByUser(user);

        List<Permission> permissions = new ArrayList<>();
        for(UserRole userRole : userRoles){
            Role role = roleService.findRoleById(userRole.getRid());
            List<RolePermission> rolePermissions = rolePermissionService.findAllByRole(role);

            for(RolePermission rolePermission:rolePermissions){
                Permission permission = permissionService.findPermissionById(rolePermission.getPid());
                permissions.add(permission);
            }
        }
        return permissions;
    }
}

这边可以看到并没有用@controller注解,而是使用了@RestController注解
先讲讲@RestController的产生,从Spring 4.0以后产生的,用来将json/xml数据发送到前台页面,而不是返回视图页面。@RestController和@Controller的区别@RestController加在类上面的注解,使得类里面的每个方法都将json/xml返回数据加返回到前台页面中。

在Spring4.3版本以后,提供了@GetMapping注解更方便了开发
@GetMapping等价于@RequestMapping的GET请求方式
最后我们采用properties文件格式配置本地数据库的连接,及本项目的访问端口号

server.port=8443
spring.datasource.url=jdbc:mysql://localhost:3306/rdbc_study?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=yqf220150239
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto = none

下面我们将使用Postman进行测试
postman_user1
postman_user2
postman_user3
结果与预期的一致~~~

写在最后

本文demo下载地址https://download.csdn.net/download/sdnyqfyqf/12328534
点关注不迷路~~

发布了4 篇原创文章 · 获赞 4 · 访问量 57

猜你喜欢

转载自blog.csdn.net/sdnyqfyqf/article/details/105530277
今日推荐