基于 Spring Boot 权限管理 RBAC 模型

一、 核心架构:RBAC 三巨头

本系统采用经典的 RBAC 模型,其核心思想是:用户不直接关联权限,而是通过"角色"作为中间桥梁。

1. 实体关系图 (ER Diagram)

整个权限体系由 三张实体表两张关联表 支撑:

  1. 用户关联角色 关联
  2. 角色分配菜单 关联
    system_user
    bigint
    id
    我是谁
    string
    username
    USER_ROLE
    system_role
    bigint
    id
    我是什么身份
    string
    code
    如: admin, hr
    ROLE_MENU
    system_menu
    bigint
    id
    我能干什么
    string
    permission
    如: system:user:add
    string
    component
    Vue组件路径

2. 表结构解析

  • system_user (用户表):代表"自然人"。只负责登录,不直接记录权限。
  • system_role (角色表):代表"身份/职位"(如:超级管理员、HR、财务)。它是连接用户和资源的纽带。
  • system_menu (菜单/权限表):代表"资源"。它不仅包含左侧可见的菜单,还包含页面内不可见的按钮(API 权限)。

二、 核心资产:深入理解 system_menu

system_menu 是权限控制中最复杂的表,它通过 type 字段将三种资源统一管理:

1. 三种形态 (Type)

类型 含义 作用
目录 1 左侧菜单的父节点 归纳子菜单,通常无实际页面(如"系统管理")。
菜单 2 具体的业务页面 点击后跳转路由,加载 Vue 组件(如"用户管理")。
按钮 3 页面内的操作/API 不可见,仅用于控制按钮显示和后端接口鉴权(如"用户新增")。

2. 关键字段详解

  • path (路由地址)

  • 浏览器地址栏显示的 URL 后缀。

  • 拼接规则父级 path + 子级 path = 最终访问地址。

  • 例如/system (目录) + user (菜单) = /system/user

  • component (组件路径)

  • 对应前端 Vue 项目中的文件物理路径。

  • 告诉前端路由去哪里加载代码。

  • 例如system/user/index 对应 src/views/system/user/index.vue

  • permission (权限标识)【核心】

  • 权限的唯一身份证,通常采用 模块:资源:操作 的格式。

  • 例如system:user:create(允许在系统模块下创建用户)。

  • 它是连接后端 @PreAuthorize 和前端 v-hasPermi 的唯一暗号。


三、 运行流程:从登录到鉴权

当一个用户登录并进行操作时,权限数据经历了以下流转:

阶段 1:登录 (后端计算与发放)

  1. 用户登录成功。
  2. 后端根据 User -> Role -> Menu 的关联关系,查询出该用户拥有的所有 permission 字符串。
  3. 后端将这个 权限字符串列表 (如 ['system:user:query', 'system:user:create']) 返回给前端。

阶段 2:渲染 (前端存储与展示)

  1. 前端将权限列表存入 Pinia/Vuex 全局 Store 中。
  2. 按钮级控制 :在加载页面时,Vue 自定义指令 v-hasPermi 会工作。
  • 代码<el-button v-hasPermi="['system:user:create']">新增</el-button>
  • 逻辑 :去 Store 里找,如果没找到 system:user:create,直接从 DOM 中移除该按钮。

阶段 3:请求 (后端 AOP 拦截与裁决)

  1. 用户点击按钮,发起 API 请求(如 /system/user/create)。
  2. 请求到达 Controller 之前,被 Spring Security 的 AOP 切面 拦截。
  3. 注解生效@PreAuthorize("@ss.hasPermission('system:user:create')")
  • 解析 :调用名为 ss 的 Bean (PermissionService),执行 hasPermission 方法。
  • 判断:再次检查当前用户的权限列表(通常缓存于 Redis 或 SecurityContext)是否包含该字符串。
  1. 结果
  • 包含 -> 放行(执行业务代码)。
  • 不包含 -> 抛出 403 Forbidden 异常。

四、 代码实战示例

1. 后端 Controller 写法

利用 SpEL 表达式实现细粒度控制:

java 复制代码
@RestController
@RequestMapping("/system/user")
public class UserController {

    @Resource
    private UserService userService;

    // 只有拥有 'system:user:query' 权限才能访问
    @GetMapping("/page")
    @PreAuthorize("@ss.hasPermission('system:user:query')")
    public CommonResult<PageResult<UserRespVO>> getUserPage(@Valid UserPageReqVO reqVO) {
        return success(userService.getUserPage(reqVO));
    }

    // 只有拥有 'system:user:delete' 权限才能访问
    @DeleteMapping("/delete")
    @PreAuthorize("@ss.hasPermission('system:user:delete')")
    public CommonResult<Boolean> deleteUser(@RequestParam("id") Long id) {
        userService.deleteUser(id);
        return success(true);
    }
}

2. 前端 Vue 写法

利用自定义指令控制显隐:

html 复制代码
<template>
  <el-button 
    type="primary" 
    plain 
    icon="el-icon-plus"
    v-hasPermi="['system:user:create']"
    @click="handleAdd"
  >
    新增
  </el-button>
</template>

菜单表 (system_menu) 的三大"脊梁"字段

在 RBAC 权限体系中,system_menu 表不仅存储了权限数据,更承载了 前端路由生成页面渲染 的重任。其中,parent_idpathcomponent 这三个字段构成了菜单系统的核心逻辑闭环。

1. parent_id:构建无限层级 (The Skeleton)

这是实现树形结构 的关键字段,采用经典的 邻接表 (Adjacency List) 设计模式。

  • 含义:记录当前菜单的父节点 ID。

  • 逻辑

  • 0:代表根节点(一级菜单/目录)。

  • 非0:代表子节点,挂载在对应的父 ID 下。

  • 作用 :后端通过递归算法将扁平的数据库记录转化为树形结构(Tree),前端据此渲染出层级分明的侧边栏菜单

2. path:定义访问地址 (The URL)

这是 Vue Router 的路由标识,决定了浏览器地址栏显示什么 URL。

  • 含义:路由访问路径。

  • 拼接规则 :前端路由通常采用嵌套模式,最终访问地址 = 父级 Path + 子级 Path

  • 目录 (Type=1) :通常以 / 开头,作为模块前缀(如 /system)。

  • 菜单 (Type=2) :通常是纯字符串,作为具体页面的后缀(如 user)。

  • 示例 :当用户点击"用户管理"时,浏览器地址栏变为 /system/user

3. component:映射代码文件 (The Content)

这是连接路由代码的桥梁,决定了页面具体加载哪个 Vue 文件。

  • 含义 :前端 Vue 组件的物理文件路径(相对于 src/views/)。
  • 关键用法
  • 目录 :通常配置为 Layout。这代表加载系统的标准布局框架(包含侧边栏、顶栏、面包屑),子菜单内容将渲染在 Layout 的"坑位" (<router-view>) 中。
  • 菜单 :配置为具体的 Vue 文件路径。例如 system/user/index,对应前端工程中的 src/views/system/user/index.vue 文件。
相关推荐
暮色妖娆丶11 小时前
SpringBoot 启动流程源码分析 ~ 它其实不复杂
spring boot·后端·spring
火车叼位11 小时前
脚本伪装:让 Python 与 Node.js 像原生 Shell 命令一样运行
运维·javascript·python
VT.馒头11 小时前
【力扣】2727. 判断对象是否为空
javascript·数据结构·算法·leetcode·职场和发展
鹏北海11 小时前
micro-app 微前端项目部署指南
前端·nginx·微服务
发现一只大呆瓜12 小时前
虚拟列表:从定高到动态高度的 Vue 3 & React 满分实现
前端·vue.js·react.js
Coder_Boy_12 小时前
Deeplearning4j+ Spring Boot 电商用户复购预测案例中相关概念
java·人工智能·spring boot·后端·spring
css趣多多12 小时前
add组件增删改的表单处理
java·服务器·前端
证榜样呀12 小时前
2026 大专计算机专业必考证书推荐什么
大数据·前端
蓝帆傲亦12 小时前
前端性能极速优化完全指南:从加载秒开体验到丝滑交互
前端·交互
鱼毓屿御12 小时前
如何给用户添加权限
前端·javascript·vue.js