一个简单的权限控制需求

在这里简单描述一下权限管理的需求:一个用户拥有若干角色,一个角色拥有若干权限,权限就是对某个资源(模块)的某种操作(增、删、改、查),这样就构成了 “用户-角色-权限” 的授权模型。在这种模型中,用户与角色之间、角色与权限之间,一般是多对多的关系,如图2-1所示。

image 2024 05 21 12 42 08 244
Figure 1. 图2-1 RBAC

创建数据库表

首先,要创建五个表:用户表、角色表、权限表、用户角色关系表和角色权限关系表。在已经创建好的 mybatis 数据库中执行如下 SQL 脚本。

# 创建用户表
CREATE TABLE sys_user
(
    id            BIGINT NOT NULL AUTO_INCREMENT COMMENT '用户ID',
    user_name     VARCHAR(50) COMMENT '用户名',
    user_password VARCHAR(50) COMMENT '密码',
    user_email    VARCHAR(50) COMMENT '邮箱',
    user_info     TEXT COMMENT '简介',
    head_img      BLOB COMMENT '头像',
    create_time   DATETIME COMMENT '创建时间',
    PRIMARY KEY (id)
);
ALTER TABLE sys_user COMMENT '用户表';

# 创建角色表
CREATE TABLE sys_role
(
    id          BIGINT NOT NULL AUTO_INCREMENT COMMENT '角色ID',
    role_name   VARCHAR(50) COMMENT '角色名',
    enabled     INT COMMENT '有效标志',
    create_by   BIGINT COMMENT '创建人',
    create_time DATETIME COMMENT '创建时间',
    PRIMARY KEY (id)
);
ALTER TABLE sys_role COMMENT '角色表';

# 创建权限表
CREATE TABLE sys_privilege
(
    id             BIGINT NOT NULL AUTO_INCREMENT COMMENT '权限ID',
    privilege_name VARCHAR(50) COMMENT '权限名称',
    privilege_url  VARCHAR(256) COMMENT '权限URL',
    PRIMARY KEY (id)
);
ALTER TABLE sys_privilege COMMENT '权限表';

# 创建用户角色关系表
CREATE TABLE sys_user_role
(
    user_id BIGINT COMMENT '用户ID',
    role_id BIGINT COMMENT '角色ID'
);
ALTER TABLE sys_user_role COMMENT '用户角色关系表';

# 创建角色权限关系表
CREATE TABLE sys_role_privilege
(
    role_id      BIGINT COMMENT '角色ID',
    privilege_id BIGINT COMMENT '权限ID'
);
ALTER TABLE sys_role_privilege COMMENT '角色权限关系表';

为了方便对表进行直接操作,此处没有创建表之间的外键关系。对于表之间的关系,会通过业务逻辑来进行限制。

为了方便后面的测试,先在表中插入一些测试数据,SQL 脚本如下。

# 初始化表数据
INSERT INTO `sys_user`(`id`, `user_name`, `user_password`, `user_email`, `user_info`, `head_img`, `create_time`)
VALUES (1, 'admin', '123456', 'admin@zccoder.com', '管理员', NULL, '2018-05-29 13:38:07');
INSERT INTO `sys_user`(`id`, `user_name`, `user_password`, `user_email`, `user_info`, `head_img`, `create_time`)
VALUES (1001, 'test', '123456', 'test@zccoder.com', '测试用户', NULL, '2018-05-29 13:38:35');

INSERT INTO `sys_role`(`id`, `role_name`, `enabled`, `create_by`, `create_time`)
VALUES (1, '管理员', 1, 1, '2018-05-29 13:39:41');
INSERT INTO `sys_role`(`id`, `role_name`, `enabled`, `create_by`, `create_time`)
VALUES (2, '普通用户', 1, 1, '2018-05-29 13:39:53');

INSERT INTO `sys_user_role`(`user_id`, `role_id`) VALUES (1, 1);
INSERT INTO `sys_user_role`(`user_id`, `role_id`) VALUES (1, 2);
INSERT INTO `sys_user_role`(`user_id`, `role_id`) VALUES (1001, 2);

INSERT INTO `sys_privilege`(`id`, `privilege_name`, `privilege_url`) VALUES (1, '用户管理', '/users');
INSERT INTO `sys_privilege`(`id`, `privilege_name`, `privilege_url`) VALUES (2, '角色管理', '/roles');
INSERT INTO `sys_privilege`(`id`, `privilege_name`, `privilege_url`) VALUES (3, '系统日志', '/logs');
INSERT INTO `sys_privilege`(`id`, `privilege_name`, `privilege_url`) VALUES (4, '人员维护', '/persons');
INSERT INTO `sys_privilege`(`id`, `privilege_name`, `privilege_url`) VALUES (5, '单位维护', '/companies');

INSERT INTO `sys_role_privilege`(`role_id`, `privilege_id`) VALUES (1, 1);
INSERT INTO `sys_role_privilege`(`role_id`, `privilege_id`) VALUES (1, 2);
INSERT INTO `sys_role_privilege`(`role_id`, `privilege_id`) VALUES (1, 3);
INSERT INTO `sys_role_privilege`(`role_id`, `privilege_id`) VALUES (2, 4);
INSERT INTO `sys_role_privilege`(`role_id`, `privilege_id`) VALUES (2, 5);

当创建好基本的 5 个表,并且准备好上述测试数据之后,就可以开始进行简单编码了。

创建实体类

MyBatis 默认是遵循 “下画线转驼峰” 命名方式的,所以在创建实体类时一般都按照这种方式进行创建。由于上面 5 个表比较类似,因此这里给出用户表和用户角色关联表所对应的实体,另外 3 个表大家按照相同的规则编写即可。

先看第一个,用户表对应的实体类 SysUser 的代码如下。

/**
* 用户表
*/
public class SysUser {

    /**
     * 用户ID
     */
    private Long id;
    /**
     * 用户名
     */
    private String userName;
    /**
     * 密码
     */
    private String userPassword;
    /**
     * 邮箱
     */
    private String userEmail;
    /**
     * 简介
     */
    private String userInfo;
    /**
     * 头像
     */
    private byte[] headImg;
    /**
     * 创建时间
     */
    private Date createTime;

    // 省略setter和getter方法
}

对于 SysUser 实体类,首先需要注意的就是命名方式,它的类名和字段名都采用了 “下画线转驼峰” 方式。具体采用什么样的命名方式并不重要(方式一致即可),在后面使用这些对象的时候,可以通过 resultMap 对数据库的列和类的字段配置映射关系。

在 MyBatis 中,关于数据库字段和 Java 类型的对应关系,不需要刻意去记,但需要注意一个特殊的类型 “byte[]”。这个类型一般对应数据库中的 BLOB、LONGVARBINARY 以及一些和二进制流有关的字段类型,其他类型详细的对应关系可以查看本书附录的内容。

由于 Java 中的基本类型会有默认值,例如当某个类中存在 private int age; 字段时,创建这个类时,age 会有默认值 0。当使用 age 属性时,它总会有值。因此在某些情况下,便无法实现使 agenull。并且在动态 SQL 的部分,如果使用 age!=null 进行判断,结果总会为 true,因而会导致很多隐藏的问题。

所以,在实体类中不要使用基本类型。基本类型包括 byteintshortlongfloatdoublecharboolean

再来看另一个实体类 SysUserRole,代码如下。

/**
* 用户角色关联表
*/
public class SysUserRole {

    /**
     * 用户ID
     */
    private Long userId;
    /**
     * 角色ID
     */
    private Long roleId;

    // 省略setter和getter方法
}

参考上面两个实体类的代码,请大家依次完成 SysRoleSysPrivilegeSysRolePrivilege 三个类的代码。

创建实体类的过程比较枯燥。后面可以通过 MyBatis 官方提供的工具 MyBatis Generator(MyBatis 代码生成器,简称 MBG)根据数据库表的信息自动生成这些类,以减少工作量。有关这个工具的使用方法将会在第 5 章详细介绍。

在完成上面 5 个实体类的创建之后,下面一起来学习 MyBatis XML 方式的基本用法。