RBAC简单的前后端分离权限管理思路
RBAC
是基于角色的访问控制(Role-Based Access Control
)在 RBAC
中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。
用户拥有当前角色下的对应权限,一个用户可以有多个角色,每个角色都可以有多个菜单。
一、数据库设计
- 用户表
CREATE TABLE `admin_user` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名',
`mobile` char(11) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',
`avatar` varchar(512) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '头像',
`email` varchar(128) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '邮箱',
`password` varchar(128) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '密码',
`status` int unsigned NOT NULL DEFAULT '1' COMMENT '状态 1正常 2黑名单',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`delete_time` datetime DEFAULT NULL COMMENT '是否删除',
`last_login_time` datetime DEFAULT NULL COMMENT '最后登录时间',
PRIMARY KEY (`id`),
UNIQUE KEY `unique_mobile` (`username`,`mobile`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
- 角色表
CREATE TABLE `admin_role` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '角色名称',
`remarks` varchar(512) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '备注',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
`delete_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-
菜单表
每条记录保存
parent_id
,当parent_id
= 0 就是根节点(一级菜单)。type
标记菜单的类型,区分是菜单还是权限控制。渲染菜单的时候只需要type
= 1 的数据。route_name
前后端分离菜单是由前端控制,前端通过route_name
匹配菜单是否动态添加到路由表中。api_route_name
后端接口标识,当请求来时后端根据api_route_name
判断是否有接口权限。icon
前端渲染菜单展示的图标。cache
前端页面是否使用keep-alive
缓存。hidden
是否在菜单栏显示,有的路由不需要在菜单栏显示,但是需要权限控制的时候使用。
CREATE TABLE `admin_menu` (
`id` int NOT NULL AUTO_INCREMENT,
`parent_id` int unsigned NOT NULL DEFAULT '0' COMMENT '父级菜单id 0是一级菜单',
`type` int unsigned NOT NULL DEFAULT '1' COMMENT '1菜单 2按钮或操作权限',
`route_name` varchar(128) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '前端路由名称',
`api_route_name` varchar(128) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '接口路由名称',
`title` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '菜单名称',
`icon` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '菜单图标',
`cache` int NOT NULL DEFAULT '0' COMMENT '0不缓存 1缓存',
`affix` int NOT NULL DEFAULT '0' COMMENT '0不固定到标签栏 1固定',
`breadcrumb` int NOT NULL DEFAULT '1' COMMENT '0不显示在面包屑 1显示',
`hidden` int NOT NULL DEFAULT '1' COMMENT '0菜单显示 1菜单隐藏',
`remarks` varchar(512) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '备注信息',
`sort` int NOT NULL DEFAULT '0' COMMENT '排序',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
`delete_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
- 角色与用户绑定关系表
CREATE TABLE `admin_role_user` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`user_id` int unsigned NOT NULL COMMENT '用户id',
`role_id` int unsigned NOT NULL COMMENT '角色id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
- 角色与菜单绑定关系表
CREATE TABLE `admin_role_menu` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`menu_id` int unsigned NOT NULL DEFAULT '0' COMMENT '菜单id',
`role_id` int unsigned NOT NULL DEFAULT '0' COMMENT '角色id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=234 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
二、后端实现权限管理思路
基本上大多数框架都有请求拦截功能,例如 nodejs(Eggjs/Koa)
、 PHP(ThinkPHP/Laravel)
的中间件、 SpringBoot
的请求拦截或者AOP
。
-
每个请求都一个唯一名称。中间件传参,可以在路由定义时候传递到中间件;springboot可以用注解参数。对应菜单中字段
api_route_name
。 -
通过登录凭证获取用户ID,通过用户ID在
admin_role_user
表中查看用户所有的角色 -
通过
api_route_name
获取请求是否在admin_menu
中,不存在则放行。 -
如果菜单存在就在
admin_role_menu
表中查看用户所拥有的的角色下是否有菜单权限,有权限则放行。
三、前端实现思路
Vue
等框架前后端分离前端独立控制路由,菜单可以使用路由动态生成,通过获取当前用户的权限去比对路由表,生成当前用户具有的权限可访问的路由表,菜单表 admin_menu
中其他字段可以用在前端路由生成的参数。菜单中区分类型菜单/权限;菜单是用作前端路由表生成,权限用作页面按钮或者操作的控制。
- 用户登录成功获取用户信息,后端返回用户角色下所有的菜单。
- 通过返回的列表中的菜单类型动态生成前端路由,并将菜单下对应权限
type=2
放入对应路由meta
中;编写自定义指令通过获取当前路由中meta
权限可以实现按钮级别的权限控制。
转载自:https://juejin.cn/post/6993235736057249806