SpringBoot 动态路由菜单 权限系统开发 菜单权限 数据库设计 不同角色对应不同权限
介绍
系统中的路由配置可以根据用户的身份、角色或其他权限信息动态生成,而不是固定在系统中。不同的用户根据其权限会看到不同的路由,访问不同的页面。对应各部门不同的权限。
效果
[{"id": 1,"menuName": "用户管理","path": "/user","icon": null,"child": [{"id": 2,"menuName": "个人中心","path": "/info","icon": null,"child": [{"id": 7,"menuName": "修改密码","path": "/alterPassword","icon": null}]},{"id": 3,"menuName": "添加用户","path": "/addUser","icon": null}]},{"id": 4,"menuName": "系统管理","path": "/sysManange","icon": null,"child": [{"id": 5,"menuName": "站点配置","path": "/siteConfig","icon": null}]}
]
用户表设计
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (`id` int(11) NOT NULL COMMENT '编号',`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '用户名',PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;INSERT INTO `user` VALUES (1, 'dpc520');SET FOREIGN_KEY_CHECKS = 1;

角色表设计
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (`id` int(11) NOT NULL COMMENT '编号',`role_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '角色名称',`role_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '权限字符串',PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;INSERT INTO `role` VALUES (1, '超级管理员', 'admin');SET FOREIGN_KEY_CHECKS = 1;

菜单表设计
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;DROP TABLE IF EXISTS `menu`;
CREATE TABLE `menu` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '菜单ID',`menu_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '菜单名称',`parent_id` int(11) NULL DEFAULT NULL COMMENT '父亲ID',`path` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '路由路径',`icon` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '图标',PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 8 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;INSERT INTO `menu` VALUES (1, '用户管理', 0, '/user', NULL);
INSERT INTO `menu` VALUES (2, '个人中心', 1, '/info', NULL);
INSERT INTO `menu` VALUES (3, '添加用户', 1, '/addUser', NULL);
INSERT INTO `menu` VALUES (4, '系统管理', 0, '/sysManange', NULL);
INSERT INTO `menu` VALUES (5, '站点配置', 4, '/siteConfig', NULL);
INSERT INTO `menu` VALUES (7, '修改密码', 2, '/alterPassword', NULL);SET FOREIGN_KEY_CHECKS = 1;

用户与角色关联
对应不同用户的身份
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (`user_id` int(11) NOT NULL COMMENT '用户id',`role_id` int(11) NOT NULL COMMENT '角色id',PRIMARY KEY (`user_id`, `role_id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Fixed;INSERT INTO `user_role` VALUES (1, 1);SET FOREIGN_KEY_CHECKS = 1;

角色与菜单关联
对应不同角色查看不同菜单的权限

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;DROP TABLE IF EXISTS `role_menu`;
CREATE TABLE `role_menu` (`role_id` int(11) NOT NULL COMMENT '角色ID',`menu_id` int(11) NOT NULL COMMENT '菜单ID',PRIMARY KEY (`role_id`, `menu_id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Fixed;INSERT INTO `role_menu` VALUES (1, 1);
INSERT INTO `role_menu` VALUES (1, 2);
INSERT INTO `role_menu` VALUES (1, 3);
INSERT INTO `role_menu` VALUES (1, 4);
INSERT INTO `role_menu` VALUES (1, 5);
INSERT INTO `role_menu` VALUES (1, 7);SET FOREIGN_KEY_CHECKS = 1;
SQL语句
根据用户ID,查出对应的角色,根据角色查出对应的菜单
SELECT m.id ,m.menu_name ,m.parent_id,m.path,m.icon FROM user u
LEFT JOIN user_role ur ON ur.user_id=u.id
LEFT JOIN role r ON ur.role_id=r.id
LEFT JOIN role_menu rm ON rm.role_id=r.id
LEFT JOIN menu m ON rm.menu_id=m.id
WHERE u.id=1 ORDER BY m.parent_id ASC
查出数据

后端根据id和parent_id进行菜单的匹配
控制器
@RestController
@RequestMapping("/menu")
@RequiredArgsConstructor
public class MenuController {private final IMenuService menuService;@GetMapping("/getMenu")public List<Menu> getMenu(){return menuService.getMenuList();}}
实体类
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("menu")
public class Menu implements Serializable {private static final long serialVersionUID = 1L;/*** 菜单ID*/@TableId(value = "id", type = IdType.AUTO)private Integer id;/*** 菜单名称*/@TableField("menu_name")private String menuName;/*** 父亲ID*/@TableField("parent_id")@JsonIgnore // 不返回该字段private Integer parentId;/*** 路由路径*/@TableField("path")private String path;/*** 菜单图标*/@TableField("icon")private String icon;/*** 子菜单*/@JsonInclude(JsonInclude.Include.NON_NULL)@TableField(exist=false)List<Menu> child;}
业务层
@Service
@RequiredArgsConstructor
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IMenuService {private final MenuMapper mapper;/*** 获取菜单列表* @return 返回构建好的菜单列表(包含顶级菜单及其子菜单)*/@Overridepublic List<Menu> getMenuList() {// 从数据库获取所有菜单列表List<Menu> list = mapper.getMenuList();// 创建一个返回给前端的菜单列表List<Menu> returnList = new ArrayList<Menu>();// 遍历数据库中获取的菜单列表for (Iterator<Menu> iterator = list.iterator(); iterator.hasNext();){// 获取当前菜单Menu t = iterator.next();// 判断是否为顶级菜单(ParentId 为 0)if (t.getParentId() == 0){// 如果是顶级菜单,查找并设置其子菜单recursionFn(list, t);// 将该菜单加入到返回列表returnList.add(t);}}// 返回构建好的菜单列表return returnList;}/*** 递归查找并设置子菜单* @param list 菜单列表* @param t 当前菜单(可能是父菜单)*/private void recursionFn(List<Menu> list, Menu t){// 获取当前菜单的所有子菜单List<Menu> childList = getChildList(list, t);// 设置当前菜单的子菜单t.setChild(childList);// 遍历子菜单,递归处理每个子菜单的子菜单for (Menu tChild : childList){if (hasChild(list, tChild)){// 如果子菜单还有子菜单,递归调用recursionFn(list, tChild);}}}/*** 获取某个菜单的所有子菜单* @param list 菜单列表* @param t 当前菜单(父菜单)* @return 当前菜单的子菜单列表*/private List<Menu> getChildList(List<Menu> list, Menu t){// 存储当前菜单的子菜单List<Menu> tlist = new ArrayList<Menu>();// 使用迭代器遍历菜单列表Iterator<Menu> it = list.iterator();while (it.hasNext()){// 获取当前菜单Menu n = it.next();// 如果菜单的 ParentId 等于当前菜单的 ID,表示该菜单是当前菜单的子菜单if (n.getParentId() == t.getId()){// 将子菜单添加到子菜单列表中tlist.add(n);}}// 返回子菜单列表return tlist;}/*** 判断一个菜单是否有子菜单* @param list 菜单列表* @param t 当前菜单(父菜单)* @return 如果当前菜单有子菜单则返回 true,否则返回 false*/private boolean hasChild(List<Menu> list, Menu t){// 如果获取的子菜单列表的大小大于 0,说明当前菜单有子菜单return getChildList(list, t).size() > 0;}
}
数据层
<select id="getMenuList" resultMap="menuList">SELECT m.id ,m.menu_name ,m.parent_id,m.path,m.icon FROM user uLEFT JOIN user_role ur ON ur.user_id=u.idLEFT JOIN role r ON ur.role_id=r.idLEFT JOIN role_menu rm ON rm.role_id=r.idLEFT JOIN menu m ON rm.menu_id=m.idWHERE u.id=1 ORDER BY m.parent_id ASC
</select><resultMap id="menuList" type="com.role.web.pojo.Menu" autoMapping="true"><id property="id" column="id"></id><result property="menuName" column="menu_name"></result>
</resultMap>
相关文章:
SpringBoot 动态路由菜单 权限系统开发 菜单权限 数据库设计 不同角色对应不同权限
介绍 系统中的路由配置可以根据用户的身份、角色或其他权限信息动态生成,而不是固定在系统中。不同的用户根据其权限会看到不同的路由,访问不同的页面。对应各部门不同的权限。 效果 [{"id": 1,"menuName": "用户管理"…...
[dp8_子数组] 乘积为正数的最长子数组长度 | 等差数列划分 | 最长湍流子数组
目录 1.乘积为正数的最长子数组长度 2.等差数列划分 3.最长湍流子数组 写代码做到,只用维护好自己的一小步 1.乘积为正数的最长子数组长度 链接:1567. 乘积为正数的最长子数组长度 给你一个整数数组 nums ,请你求出乘积为正数的最长子数…...
资深词源学家提示词
Role: 资深词源学家 Profile: Language: 中文Description: 作为在词源学领域的卓越专家,具备深厚且多元的学术背景。精通拉丁语、古希腊语、梵语等一众古老语言,能够精准解析这些语言的古代文献,为探寻词汇起源挖掘第一手资料。在汉语研究方…...
深入探讨MySQL存储引擎:选择最适合你的数据库解决方案
前言 大家好,今天我们将详细探讨MySQL中几种主要的存储引擎,了解它们的工作机制、适用场景以及各自的优缺点。通过这篇文章,希望能帮助你根据具体需求选择最合适的存储引擎,优化数据库性能。 1. InnoDB - 默认且强大的事务性存储…...
【图像处理基石】什么是通透感?
一、画面的通透感定义 画面的通透感指图像在色彩鲜明度、空间层次感、物体轮廓清晰度三方面的综合表现,具体表现为: 色彩鲜明:颜色纯净且饱和度适中,无灰暗或浑浊感;层次分明:明暗过渡自然,光…...
无锡无人机超视距驾驶证怎么考?
无锡无人机超视距驾驶证怎么考?在近年来,无人机技术的迅猛发展使得无人机的应用场景变得愈发广泛,其不仅在环境监测、农业喷洒、快递配送等领域展现出真金白银的价值,同时也推动了无人机驾驶证的需求。尤其是在无锡,随…...
213、【图论】有向图的完全联通(Python)
题目描述 原题链接:105. 有向图的完全联通 代码实现 import collectionsn, k list(map(int, input().split())) adjacency collections.defaultdict(list) for _ in range(k):head, tail list(map(int, input().split()))adjacency[head].append(tail)visited_…...
(二十二)安卓开发中的数据存储之SQLite简单使用
在Android开发中,SQLite是一种非常常用的数据库存储方式。它轻量、简单,非常适合移动设备上的数据管理。本文将通过通俗易懂的语言,结合代码示例和具体场景,详细讲解SQLite在Android中的使用。 1. 什么是SQLite? SQLite是一个开…...
图像形态学操作对比(Opencv)
形态学基于图像的形状进行操作,用于处理二值化图像,主要包括腐蚀和膨胀两种基本操作。这些操作通常用于去除噪声、分隔或连接相邻的元素以及寻找图像中显著的最大点和最小点。 1. 形态学操作 import cv2 import numpy as np import matplotlib.pyplot …...
复刻系列-星穹铁道 3.2 版本先行展示页
复刻星穹铁道 3.2 版本先行展示页 0. 视频 手搓~星穹铁道~展示页~~~ 1. 基本信息 作者: 啊是特嗷桃系列: 复刻系列官方的网站: 《崩坏:星穹铁道》3.2版本「走过安眠地的花丛」专题展示页现已上线复刻的网…...
请你说一说测试用例的边界
一、什么是测试用例的边界? 边界是指输入、输出、状态或操作的极限条件,是系统行为可能发生变化的临界点。例如: 输入字段的最小值、最大值、空值、超长值; 循环的第0次、第1次、最后一次; 时间相关的闰年、月末、跨时区操作等。 边界测试的核心思想是:缺陷更容易出现在…...
Linux:进程理解1(查看进程,创造进程,进程状态)
进程理解 (一)查看进程通过系统调用获取进程标示* (二)创造进程(fork)1. 创造的子进程的PCB代码数据怎么来?2.一个函数为什么有两个返回值?3. 为什么这里会有 两个 id值?…...
异形遮罩之QML中的 `OpacityMask` 实战
文章目录 🌧️ 传统实现的问题👉 效果图 🌈 使用 OpacityMask 的理想方案👉代码如下🎯 最终效果: ✨ 延伸应用🧠 总结 在 UI 设计中,经常希望实现一些“异形区域”拥有统一透明度或颜…...
如何为您的设计应用选择高速连接器
电气应用的设计过程需要考虑诸多因素,尤其是在设计高速网络时。许多连接器用户可能没有意识到,除了在两个互连之间组装导电线路之外,还需要考虑各种工艺。在建立高速连接并确保适当的信号完整性时,必须考虑蚀刻、公差、屏蔽等因素…...
mongodb 4.0+多文档事务的实现原理
1. 副本集事务实现(4.0) 非严格依赖二阶段提交 MongoDB 4.0 在副本集环境中通过 全局逻辑时钟(Logical Clock) 和 快照隔离(Snapshot Isolation) 实现多文档事务,事务提交时通过…...
【论文阅读】UniAD: Planning-oriented Autonomous Driving
一、Introduction 传统的无人驾驶采用了区分子模块的设计,即将无人驾驶拆分为感知规划控制三个模块,这虽然能够让无人驾驶以一个很清晰的结构实现,但是感知的结果在传达到规划部分的时候,会导致部分信息丢失,这势必会…...
upload-labs二次打
1(前端js绕过) 弹窗,先看看有没有js有,禁用js 禁用后就可以上传php文件了,然后我们就去访问文件,成功 2(MIME绕过) 先上传一个php文件试试,不行,.htaccess不行, 试试MIME类型&am…...
Flutter命令行打包打不出ipa报错
Flutter打包ipa报错解决方案 在Flutter开发中,打包iOS应用时可能会遇到以下错误: error: exportArchive: The data couldn’t be read because it isn’ in the correct format. 或者 Encountered error while creating the IPA: error: exportArchive…...
网页制作中的MVC和MVT
MVC(模型-视图-控制器)和MVT(模型-模板-视图)是两种常见的软件架构模式,通常用于Web应用程序的设计。它们之间的主要区别在于各自的组件职责和工作方式。 MVC(模型-视图-控制器): 模…...
C. Good Subarrays
time limit per test 2 seconds memory limit per test 256 megabytes You are given an array a1,a2,…,ana1,a2,…,an consisting of integers from 00 to 99. A subarray al,al1,al2,…,ar−1,aral,al1,al2,…,ar−1,ar is good if the sum of elements of this subarra…...
聊天室项目day4(redis实现验证码期限,实现redis连接池)
1.redis连接池操作和之前所学过的io_context连接池原理一样这里不多赘述,也是创建多个连接,使用时按顺序取出来。 2.知识补充redisConnect()函数建立与 Redis 服务器的非阻塞网络连接,成功返回 redisContext*(连接上下文指针&…...
提交至git
通过 Pull Request 提交代码 如果你无法直接推送到 master 分支(例如,因为分支保护或权限限制),通常的做法是将代码推送到一个新分支,并通过 Pull Request(或 Merge Request)提交代码࿱…...
0x06.Redis 中常见的数据类型有哪些?
回答重点 Redis 常见的数据结构主要有五种,这五种类型分别为:String(字符串)、List(列表)、Hash、Set(集合)、Zset(有序集合,也叫sorted set)。 String 字符串是Redis中最基本的数据类型,可以存储任何类型的数据,包括文本、数字和二进制数据。它的最大长度为512MB。 使…...
如何查看自己 Android App 的私有数据?从 `adb backup` 到数据提取全过程
🛠️ 如何查看自己 Android App 的私有数据?从 adb backup 到数据提取全过程 📌 前言:作为一名 Android 开发者,我常常想知道自己写的 App 在用户设备上的数据存储结构是怎样的,比如有没有数据写入成功、有…...
提权实战!
就是提升权限,当我们拿到一个shell权限较低,当满足MySQL提权的要求时,就可以进行这个提权。 MySQL数据库提权(Privilege Escalation)是指攻击者通过技术手段,从低权限的数据库用户提升到更高权限ÿ…...
Vue使用el-table给每一行数据上面增加一行自定义合并行
// template <template><el-table:data"flattenedData":span-method"objectSpanMethod"borderclass"custom-header-table"style"width: 100%"ref"myTable":height"60vh"><!-- 订单详情列 -->&l…...
ChromeOS 135 版本更新
ChromeOS 135 版本更新 一、ChromeOS 135 更新内容 1. ChromeOS 电池寿命优化策略 为了延长 Chromebook 的使用寿命,ChromeOS 135 引入了一项全新的电池充电限制策略 —— DevicePowerBatteryChargingOptimization,可提供更多充电优化选项,…...
国内协作机器手焊接领域领军人物分析
国内焊接协作机器手领域的专家涵盖学术界与产业界,他们在核心技术研发、行业标准制定及重大工程应用中发挥关键作用。以下从技术方向、行业贡献、典型案例三个维度展开分析: 一、学术界领军人物:理论创新与技术突破 1. 吴林(哈尔滨工业大学) 学术地位:中国焊接学会名誉…...
javaSE.Lambda表达式
如果一个接口中有且只有一个待实现的抽象方法,那么我们可以将匿名内部类简写为Lambda表达式。 简写规则 标准格式: (【参数类型 参数名称,】...) -> {代码语句, 包括返回值} 只有一行花括号{}可以省略。…...
【随身wifi】青龙面板保姆级教程
0.操作前必看 本教程基于Debian系统,从Docker环境。面板安装,到最后拉取脚本的使用。 可以拉库跑狗东京豆,elm红包等等,也可以跑写自己写的脚本,自行探索 重要的号别搞,容易黑号,黑号自己负责…...
