SpringBoot 动态路由菜单 权限系统开发 菜单权限 数据库设计 不同角色对应不同权限

news2025/4/15 22:18:59

介绍

系统中的路由配置可以根据用户的身份、角色或其他权限信息动态生成,而不是固定在系统中。不同的用户根据其权限会看到不同的路由,访问不同的页面。对应各部门不同的权限。

效果

[
    {
        "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 返回构建好的菜单列表(包含顶级菜单及其子菜单)
     */
    @Override
    public 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 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
</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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2334625.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[dp8_子数组] 乘积为正数的最长子数组长度 | 等差数列划分 | 最长湍流子数组

目录 1.乘积为正数的最长子数组长度 2.等差数列划分 3.最长湍流子数组 写代码做到&#xff0c;只用维护好自己的一小步 1.乘积为正数的最长子数组长度 链接&#xff1a;1567. 乘积为正数的最长子数组长度 给你一个整数数组 nums &#xff0c;请你求出乘积为正数的最长子数…

【图像处理基石】什么是通透感?

一、画面的通透感定义 画面的通透感指图像在色彩鲜明度、空间层次感、物体轮廓清晰度三方面的综合表现&#xff0c;具体表现为&#xff1a; 色彩鲜明&#xff1a;颜色纯净且饱和度适中&#xff0c;无灰暗或浑浊感&#xff1b;层次分明&#xff1a;明暗过渡自然&#xff0c;光…

无锡无人机超视距驾驶证怎么考?

无锡无人机超视距驾驶证怎么考&#xff1f;在近年来&#xff0c;无人机技术的迅猛发展使得无人机的应用场景变得愈发广泛&#xff0c;其不仅在环境监测、农业喷洒、快递配送等领域展现出真金白银的价值&#xff0c;同时也推动了无人机驾驶证的需求。尤其是在无锡&#xff0c;随…

213、【图论】有向图的完全联通(Python)

题目描述 原题链接&#xff1a;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_…

图像形态学操作对比(Opencv)

形态学基于图像的形状进行操作&#xff0c;用于处理二值化图像&#xff0c;主要包括腐蚀和膨胀两种基本操作。这些操作通常用于去除噪声、分隔或连接相邻的元素以及寻找图像中显著的最大点和最小点。 1. 形态学操作 import cv2 import numpy as np import matplotlib.pyplot …

复刻系列-星穹铁道 3.2 版本先行展示页

复刻星穹铁道 3.2 版本先行展示页 0. 视频 手搓&#xff5e;星穹铁道&#xff5e;展示页&#xff5e;&#xff5e;&#xff5e; 1. 基本信息 作者: 啊是特嗷桃系列: 复刻系列官方的网站: 《崩坏&#xff1a;星穹铁道》3.2版本「走过安眠地的花丛」专题展示页现已上线复刻的网…

Linux:进程理解1(查看进程,创造进程,进程状态)

进程理解 &#xff08;一&#xff09;查看进程通过系统调用获取进程标示* &#xff08;二&#xff09;创造进程&#xff08;fork&#xff09;1. 创造的子进程的PCB代码数据怎么来&#xff1f;2.一个函数为什么有两个返回值&#xff1f;3. 为什么这里会有 两个 id值&#xff1f;…

异形遮罩之QML中的 `OpacityMask` 实战

文章目录 &#x1f327;️ 传统实现的问题&#x1f449; 效果图 &#x1f308; 使用 OpacityMask 的理想方案&#x1f449;代码如下&#x1f3af; 最终效果&#xff1a; ✨ 延伸应用&#x1f9e0; 总结 在 UI 设计中&#xff0c;经常希望实现一些“异形区域”拥有统一透明度或颜…

如何为您的设计应用选择高速连接器

电气应用的设计过程需要考虑诸多因素&#xff0c;尤其是在设计高速网络时。许多连接器用户可能没有意识到&#xff0c;除了在两个互连之间组装导电线路之外&#xff0c;还需要考虑各种工艺。在建立高速连接并确保适当的信号完整性时&#xff0c;必须考虑蚀刻、公差、屏蔽等因素…

【论文阅读】UniAD: Planning-oriented Autonomous Driving

一、Introduction 传统的无人驾驶采用了区分子模块的设计&#xff0c;即将无人驾驶拆分为感知规划控制三个模块&#xff0c;这虽然能够让无人驾驶以一个很清晰的结构实现&#xff0c;但是感知的结果在传达到规划部分的时候&#xff0c;会导致部分信息丢失&#xff0c;这势必会…

upload-labs二次打

1(前端js绕过) 弹窗&#xff0c;先看看有没有js有&#xff0c;禁用js 禁用后就可以上传php文件了&#xff0c;然后我们就去访问文件&#xff0c;成功 2&#xff08;MIME绕过&#xff09; 先上传一个php文件试试&#xff0c;不行&#xff0c;.htaccess不行, 试试MIME类型&am…

提权实战!

就是提升权限&#xff0c;当我们拿到一个shell权限较低&#xff0c;当满足MySQL提权的要求时&#xff0c;就可以进行这个提权。 MySQL数据库提权&#xff08;Privilege Escalation&#xff09;是指攻击者通过技术手段&#xff0c;从低权限的数据库用户提升到更高权限&#xff…

ChromeOS 135 版本更新

ChromeOS 135 版本更新 一、ChromeOS 135 更新内容 1. ChromeOS 电池寿命优化策略 为了延长 Chromebook 的使用寿命&#xff0c;ChromeOS 135 引入了一项全新的电池充电限制策略 —— DevicePowerBatteryChargingOptimization&#xff0c;可提供更多充电优化选项&#xff0c…

javaSE.Lambda表达式

如果一个接口中有且只有一个待实现的抽象方法&#xff0c;那么我们可以将匿名内部类简写为Lambda表达式。 简写规则 标准格式&#xff1a; &#xff08;【参数类型 参数名称&#xff0c;】...&#xff09; -> {代码语句&#xff0c; 包括返回值} 只有一行花括号{}可以省略。…

【随身wifi】青龙面板保姆级教程

0.操作前必看 本教程基于Debian系统&#xff0c;从Docker环境。面板安装&#xff0c;到最后拉取脚本的使用。 可以拉库跑狗东京豆&#xff0c;elm红包等等&#xff0c;也可以跑写自己写的脚本&#xff0c;自行探索 重要的号别搞&#xff0c;容易黑号&#xff0c;黑号自己负责…

Android 之美国关税问题导致 GitHub 403 无法正常访问,责任在谁?

这几天各国关税问题导致世界动荡不安&#xff0c;如今GitHub又无法正常访问&#xff0c;是不是Google到时候也无法正常使用了。

4月13日星期日早报简报微语报早读

4月13日星期日&#xff0c;农历三月十六&#xff0c;早报#微语早读。 1、北京处置倒伏树木843棵&#xff0c;已全部处置完毕&#xff1b; 2、山西大同“订婚强奸案”本月16日二审宣判&#xff0c;一审男方被判3年刑&#xff1b; 3、今年我国快递业务量已突破500亿件&#xf…

动态路由, RIP路由协议,RIPv1,RIPv2

动态路由 1、回顾 路由&#xff1a;从源主机到目标主机的过程 源主机发送数据给目标主机&#xff0c;源主机会查看自身的路由信息 如果目标主机是自己同网段&#xff0c;源主机查看的是直连路由 如果目标主机和自己不同网段&#xff0c;源主机查看的是静态路由、动态路由、默…

【已更新完毕】2025泰迪杯数据挖掘竞赛B题数学建模思路代码文章教学:基于穿戴装备的身体活动监测

基于穿戴装备的身体活动监测 摘要 本研究基于加速度计采集的活动数据&#xff0c;旨在分析和统计100名志愿者在不同身体活动类别下的时长分布。通过对加速度数据的处理&#xff0c;活动被划分为睡眠、静态活动、低强度、中等强度和高强度五类&#xff0c;进而计算每个志愿者在…

212、【图论】字符串接龙(Python)

题目描述 题目链接&#xff1a;110. 字符串接龙 代码实现 import collectionsn int(input()) beginStr, endStr input().split() strList [input() for _ in range(n)]deque collections.deque() # 使用队列遍历结点 deque.append([beginStr, 1]) # 存储当前字符串和遍…