动态角色权限和动态权限到底API是怎么做的你懂了吗
1、背景
这部分我也将其归类为RBAC权限之中的一部分
在做RBAC权限管理系统的时候,都会遇到这么一个需求
每个用户的权限是不一样的,他可以访问的页面,可以操作的菜单,包括可能操作的按钮都是不一样的
这个时候就需要由后端控制用户返回的页面以及菜单按钮权限,前端去实现动态路由,动态渲染侧边菜单栏
2、实现
接下来我们就看一下如何实现不同角色不同菜单的功能
👉现有返回
我们先要改造的就是/getInfo
这个接口,现在我们返回内容如下
javascript
{
"code": 200,
"data": {
"userId": 71,
"username": "admin",
"password": "xxxxxx",
"name": "admin",
"age": null,
"sex": null,
"createTime": "1970-01-01 00:00:00",
"address": null,
"state": 1,
"phone": null,
"avatar": null,
"updateTime": "1970-01-01 00:00:00",
"userHeight": null,
"userWeight": null,
"disease": null,
"isDeleted": null
},
"message": "欢迎你的访问!"
}
👉我们想要的返回信息
JSON 响应的表示操作成功的内容,具体信息如下:
**msg**
:"操作成功"
--- 这表示操作已成功完成。**code**
:200
--- 这是成功的 HTTP 状态码,表示请求已成功处理。**permissions**
:["*: *:*"]
--- 这表示该用户拥有的权限,这里的*: *:*
通常表示具有全局权限。**roles**
:["admin"]
--- 该用户的角色为admin
,意味着该用户是管理员。**data**
: 包含一些用户相关的信息:**roleIds**
:null
--- 角色 ID 为null
,可能表示该字段目前没有关联的角色。**postIds**
:null
--- 岗位 ID 为null
,没有关联的岗位。**roleId**
:null
--- 单一角色 ID 为null
,可能表示没有指定具体的角色 ID。**admin**
:true
--- 表示该用户是管理员(true
表示是,false
表示不是)。
javascript
{
"msg": "操作成功",
"code": 200,
"permissions": [
"*:*:*"
],
"roles": [
"admin"
],
"data": {
"createBy": "admin",
"createTime": "2025-05-26 10:07:46",
"roleIds": null,
"postIds": null,
"roleId": null,
"admin": true
}
}
👉现有接口
现在我们模块如下,首先添加的就是角色模块
javascript
const express = require('express');
const router = express.Router();
const connectionPool = require('../db'); // 引入数据库连接池模块
const {addCondition,addDateRangeCondition,addPagination,convertKeysToSnakeCase,convertToCamelCase} = require('../methods.js'); // 引入封装方法
//获取用户信息时,要使用 req.auth.username 获取用户名
router.get('/', (req, res) => {
// console.log(req,'req');
// console.log(res,'res');
if (!req.auth || !req.auth.username) {
return res.status(401).send({
code: 401,
message: '未认证用户或缺少用户名!',
});
}
const values = [req.auth.username];
let query = 'SELECT * FROM sys_user WHERE username = ?';
connectionPool.query(query, [req.auth.username], (err, results) => {
if (err) {
return res.status(500).send({
code: 500,
message: '数据库查询错误!',
});
}
if (results.length === 0) {
return res.status(404).send({
code: 404,
message: '用户不存在!',
});
}
res.send({
code: 200,
data: results[0]?convertToCamelCase(results[0]):[], // 返回用户信息
message: '欢迎你的访问!',
});
});
})
module.exports = router;
👉新增的返回如下
javascript
roles: ["admin"]
👉建立用户-角色对应表sys_user_role
需要返回不同用户对应的角色,我们就需要建立一个用户-角色对应表
javascript
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
`user_id` bigint(0) NOT NULL COMMENT '用户ID',
`role_id` bigint(0) NOT NULL COMMENT '角色ID',
PRIMARY KEY (`user_id`, `role_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_bin COMMENT = '用户和角色关联表' ROW_FORMAT = Dynamic;
接下来我们根据已经有的用户ID去拿对应角色表中的角色,Mysql语句根据user_id
查询到sys_user_role
对应的role_id
,在根据role_id
查询到sys_role对应role_id
的 role_key
这里我们可以先写一个mysql测试一下
javascript
SELECT r.role_key
FROM sys_user_role ur
JOIN sys_role r ON ur.role_id = r.role_id
WHERE ur.user_id = 1
// 直接返回信息
role_key
common
证明我们sql语句没问题,接下来完善我们的接口返回信息
👉接口返回信息
javascript
let userroles= []; //普通角色
let permissions= ["*:*:*"];
// 查询用户角色
// SQL 查询,获取用户角色的 role_key
let queryrole = `
SELECT r.role_key
FROM sys_user_role ur
JOIN sys_role r ON ur.role_id = r.role_id
WHERE ur.user_id = ?
`;
let queryroleuserid=results[0].user_id;
connectionPool.query(queryrole, [queryroleuserid], (err, roleresult) => {
// console.log(roleresult,'roleresult');
if(roleresult.length === 0){
res.send({
code: 404,
message: '用户角色不完整,请尽快完善!',
});
}else{
userroles=[];
userroles.push(roleresult[0].role_key);
}
res.send({
code: 200,
data: results[0]?convertToCamelCase(results[0]):[], // 返回用户信息
msg: '欢迎你的访问!',
roles:userroles,
permissions,
isDefaultModifyPwd: false,
isPasswordExpired: false,
});
})
👉默认角色
接下来我们测试一下新用户,这个时候新用户为
javascript
{
"code": 404,
"message": "用户角色不完整,请尽快完善!"
}
这就需要我们默认一个用户角色
javascript
let userroles= ["common"]; //普通角色
//返回信息如下--ok
roles: ["common"]
3、完善
接下来我们要完善角色对应的菜单以及按钮权限,这就需要我们建立一个对应的菜单对应表sys_role_menu
👉建立角色-菜单对应表sys_role_menu
需要返回不同角色对应的菜单,我们就需要建立一个角色-菜单对应表
javascript
DROP TABLE IF EXISTS `sys_role_menu`;
CREATE TABLE `sys_role_menu` (
`role_id` bigint(0) NOT NULL COMMENT '角色ID',
`menu_id` bigint(0) NOT NULL COMMENT '菜单ID',
PRIMARY KEY (`role_id`, `menu_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE =
utf8mb3_bin COMMENT = '角色和菜单关联表' ROW_FORMAT = Dynamic;
👉逻辑编写
思考一下逻辑:
也就是我们需要使用role_id
去查出对应的
接下来我们根据已经有的用户ID去拿对应角色表中的角色,Mysql语句根据user_id
查询到sys_user_role
对应的role_id
,在根据role_id
查询到sys_role_menu
对应role_id
的所有 menu_id
,再根据所有的menu_id
去拿到sys_menu
中对应的所有menu_id
的 perms
权限标识
👉根据用户ID查询角色ID
javascript
// 根据用户ID查询角色ID
const roleQuery = `
SELECT role_id
FROM sys_user_role
WHERE user_id = ?
`;
connectionPool.query(roleQuery, [queryroleuserid], (err, roleResults) => {
console.log(roleResults[0].role_id,'roleResults');
})
//测试结果
1 roleResults
测试一下,ok
👉接下来我们就根据角色ID查询菜单ID
javascript
// 根据用户ID查询角色ID
const roleQuery = `
SELECT role_id
FROM sys_user_role
WHERE user_id = ?
`;
connectionPool.query(roleQuery, [queryroleuserid], (err, roleResults) => {
// console.log(roleResults[0].role_id,'roleResults');
let role_id = roleResults[0].role_id;
console.log(role_id,'role_id')
// 根据角色ID查询菜单ID
const menuQuery = `
SELECT menu_id
FROM sys_role_menu
WHERE role_id IN (?)
`;
connectionPool.query(menuQuery, [role_id], (err, menuResults) => {
console.log(menuResults, 'menuResults');
if (err) {
return res.status(500).send({
code: 500,
message: '查询菜单失败!',
});
}
})
})
//测试结果
[{ menu_id: 1 },
{ menu_id: 2 },
{ menu_id: 3 },
{ menu_id: 4 },
{ menu_id: 100 },
{ menu_id: 104 },
]
👉根据菜单ID查询权限
javascript
// 第三步:根据菜单ID查询权限标识
const permsQuery = `
SELECT perms
FROM sys_menu
WHERE menu_id IN (?)
`;
connectionPool.query(permsQuery, [menuIds], (err, permsResults) => {
console.log(permsResults, 'permsResults');
const permissions = permsResults.map(result => result.perms).filter(perm => perm !== '');
console.log(permissions, 'permissions');
})
测试一下我们的数据
javascript
这个时候我们得到的权限数据已经符合我们的预期了
[
'system:user:list',
'system:role:list',
'system:menu:list',
'system:dept:list',
'system:post:list',
'system:dict:list',
'system:config:list',
'system:notice:list',
'monitor:online:list',
'monitor:job:list',
]
然后完善一下添加上自己对应的返回信息即可,相信这里大家都会自己操作了,然后就是自己优化一下,比较简单,我就不写了
javascript
{
"code": 200,
"data": {
"userId": 72,
"username": "222222",
"password": "XXXXX",
"name": "222222",
"age": null,
"sex": null,
"createTime": "1970-01-01 00:00:00",
"address": null,
"state": 1,
"phone": null,
"avatar": null,
"updateTime": "1970-01-01 00:00:00",
"userHeight": null,
"userWeight": null,
"disease": null,
"isDeleted": null
},
"msg": "欢迎你的访问!",
"msgtype": "success",
"roles": [
"common"
],
"permissions": [
"system:user:list",
"system:role:list",
"system:menu:list",
"system:dept:list",
"system:post:list",
"system:dict:list",
],
"isDefaultModifyPwd": false,
"isPasswordExpired": false
}