若依(RuoYi-Vue)前后端分离项目学生管理模块——菜单权限验证功能深度剖析
前言
在企业级后台管理系统中,菜单权限控制是必不可少的一环。一个按钮谁能点、谁能看、谁能操作,都需要精确到接口级别地管控。若依(RuoYi)作为国内最流行的开源后台管理框架之一,提供了从数据库到前端组件的全链路权限解决方案。
本文以若依 RuoYi-Vue-v3.8.2 前后端分离版本的学生管理模块为例,围绕「菜单权限测试」这一功能按钮,带你从零到一理解整个权限闭环:前端按钮如何通过 v-hasPermi 指令控制显隐 → Axios 发起请求 → 后端 Controller 接收 → @PreAuthorize 注解权限校验 → 权限拦截/放行 → 数据返回 → 前端弹窗提示。每一张截图对应一个关键环节,逐段分析代码作用,区分权限核心代码与辅助无关代码。
一、整体流程概述
在开始写代码之前,先把整个权限闭环的流程理清楚。以下是完整链路中 9 个关键节点:
① 前端页面 → 用户点击「菜单权限测试」按钮
② v-hasPermi 指令 → 按钮显隐判断(前端第一道权限门)
③ testPermi() API 方法 → Axios 构造 GET 请求
④ Axios 拦截器 → 自动携带 Token、封装响应
⑤ 后端 Controller → @PreAuthorize("@ss.hasPermi('system:student:test')")
⑥ Spring Security → 权限拦截 / 放行判定
⑦ 权限通过 → 执行接口逻辑,返回 AjaxResult
⑧ 权限拦截 → 抛出拒绝异常,前端收到 403/异常信息
⑨ 前端弹窗 → $modal.msgSuccess / msgError 反馈结果
下面我们按这个顺序,从 前端 → 后端 → 配置 → 效果展示 逐一展开。
二、前端代码分析
2.1 Vue 页面 —— 按钮 + v-hasPermi 指令
源码文件:ruoyi-ui/src/views/system/student/index.vue

代码位置:第 32-35 行。
<!-- 菜单权限测试按钮 已正确放入 -->
<el-col :span="2">
<el-button type="primary" plain icon="el-icon-check" size="mini" @click="handleTestPermi" v-hasPermi="['system:student:test']">菜单权限测试</el-button>
</el-col>
逐段分析:
| 代码片段 | 作用 | 是否权限核心 |
|---|---|---|
<el-col :span="2"> |
栅格布局,控制按钮占位宽度 | ❌ 辅助布局代码 |
type="primary" plain |
按钮样式(蓝色描边主按钮) | ❌ UI 样式代码 |
icon="el-icon-check" |
按钮图标 | ❌ UI 代码 |
size="mini" |
按钮尺寸 | ❌ UI 代码 |
@click="handleTestPermi" |
点击事件,触发接口调用 | ✅ 权限闭环触发点 |
v-hasPermi="['system:student:test']" |
权限指令,核心!控制按钮是否渲染 | ✅✅ 权限核心 |
核心要点:v-hasPermi 是若依前端权限体系的第一道关卡。它是一个 Vue 自定义指令(将在 2.4 节详细剖析),读取 Vuex Store 中当前用户的权限标识数组,只有当数组中包含 'system:student:test' 时,才保留该按钮的 DOM 节点;否则直接移除。
2.2 Vue 页面 —— 方法处理函数

// 菜单权限测试方法
handleTestPermi() {
testPermi().then(res => {
this.$modal.msgSuccess(res.msg);
}).catch(err => {
this.$modal.msgError("权限校验失败或接口异常");
});
}
逐段分析:
| 代码片段 | 作用 | 备注 |
|---|---|---|
testPermi() |
调用 API 文件中导出的权限测试接口 | 由 import { ..., testPermi } 引入(第 84 行) |
.then(res => { ... }) |
成功后弹窗提示,返回 res.msg 内容 |
即后端返回的"菜单权限测试接口调用成功!权限校验通过" |
.catch(err => { ... }) |
失败时弹窗提示错误信息 | 包括无权限(403)和网络异常两种情况 |
2.3 前端 API 文件 —— Axios 请求封装
源码文件:ruoyi-ui/src/api/system/student.js

// 菜单权限测试
export function testPermi() {
return request({
url: '/system/student/testPermi',
method: 'get'
})
}
逐段分析:
| 代码片段 | 作用 | 备注 |
|---|---|---|
export function testPermi() |
ES6 模块导出,供 Vue 组件调用 | 函数名与后端接口名保持一致 |
request |
封装的 Axios 实例,来源于 @/utils/request |
全局配置在此文件中统一管理 |
url: '/system/student/testPermi' |
请求路径,与后端 @RequestMapping 对应 |
格式:/模块/实体/接口名 |
method: 'get' |
HTTP 方法 | 此接口仅做权限校验,使用 GET 即可 |
注意:此文件中其他函数(
listStudent、getStudent、addStudent、updateStudent、delStudent)均为 CRUD 常规操作,与权限验证无关,但权限校验注解同样绑定了各自的权限字符串。
2.4 v-hasPermi 自定义指令源码剖析
源码文件:ruoyi-ui/src/directive/permission/hasPermi.js

import store from '@/store'
export default {
inserted(el, binding, vnode) {
const { value } = binding
const all_permission = "*:*:*"
const permissions = store.getters && store.getters.permissions
if (value && value instanceof Array && value.length > 0) {
const permissionFlag = value
const hasPermissions = permissions.some(permission => {
return all_permission === permission || permissionFlag.includes(permission)
})
if (!hasPermissions) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error(`请设置操作权限标签值`)
}
}
}
执行原理:
1.inserted 钩子:Vue 指令在元素插入 DOM 时触发,此时执行权限判断逻辑。
2.store.getters.permissions:从 Vuex Store 获取当前登录用户的所有权限标识(数组),这个数组由登录时后端返回并存储在前端全局状态中。
3.permissions.some(...):使用数组的 some 方法遍历,只要匹配到任意一个权限标识即返回 true。
4.all_permission === permission:如果用户拥有 *:*:*(超级管理员全权限),则始终放行。
5.permissionFlag.includes(permission):用户权限数组中包含按钮所需权限时放行。
6.el.parentNode.removeChild(el):权限不满足时,物理移除 DOM 节点,按钮彻底不存在于页面中,而非简单的隐藏。
三、后端代码分析
3.1 Controller 层 —— 权限注解 + 接口定义
源码文件:ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/MyStudentController.java

/**
* 菜单权限测试
*/
@PreAuthorize("@ss.hasPermi('system:student:test')")
@GetMapping("/testPermi")
public AjaxResult testPermi()
{
return AjaxResult.success("菜单权限测试接口调用成功!权限校验通过");
}
逐段分析:
| 代码片段 | 作用 | 权限核心度 |
|---|---|---|
@PreAuthorize("@ss.hasPermi('system:student:test')") |
Spring Security 权限校验注解,核心! | ✅✅ 权限拦截核心 |
@GetMapping("/testPermi") |
路由映射,定义接口访问路径 | 接口定义 |
public AjaxResult testPermi() |
接口方法,返回 JSON 统一格式 | 逻辑执行 |
return AjaxResult.success(...) |
构造成功响应体,前端 res.msg 即为此文本 |
数据返回 |
核心要点:@PreAuthorize 是 Spring Security 提供的方法级权限注解。@ss 是若依框架注册的 Spring Bean(PermissionService),.hasPermi() 方法内部通过 SecurityUtils 获取当前登录用户的所有权限标识,与注解中传入的 'system:student:test' 进行匹配校验。匹配成功则方法放行,否则抛出 AccessDeniedException,前端收到异常并进入 .catch 分支弹窗报错。
3.2 其他接口的权限注解对照表
同一个 Controller 中,所有业务接口均绑定了各自的权限字符:
| 接口方法 | HTTP 方法 | 路径 | 权限字符 |
|---|---|---|---|
list |
GET | /list |
system:student:query |
getInfo |
GET | /{id} |
system:student:query |
add |
POST | / |
system:student:add |
edit |
PUT | / |
system:student:edit |
remove |
DELETE | /{ids} |
system:student:remove |
export |
POST | /export |
system:student:export |
testPermi |
GET | /testPermi |
system:student:test |
可以看到,若依的权限字符串采用
模块:实体:操作的三级格式(如system:student:test),清晰直观,便于管理员在角色管理页面进行配置。
四、权限配置截图演示
4.1 菜单管理 —— 新增权限测试按钮菜单项

该截图展示的是在菜单管理中,将「权限测试」按钮项(类型为"按钮")添加到「学生管理」菜单树下:
- 上级菜单:学生管理(
system/student) - 菜单类型:按钮
- 菜单名称:权限测试
- 权限字符:
system:student:test← 此字符串需与前端v-hasPermi和后端@PreAuthorize完全一致! - 菜单状态:正常
⚠️ 关键配置点:权限字符是连接前端、后端、配置的唯一纽带,三端必须保持完全一致才能正常工作。
4.2 角色管理 —— 为角色分配权限

该截图展示的是在角色管理中,为普通角色配置菜单权限的树形选择界面。关键点如下:
- 角色名称:普通角色(编号 2,超级管理员为编号 1)
- 功能选项:已勾选「父子联动」,保证树形选择的一致性。
- 学生管理节点已展开,其下包含:
- ✅ 学生查询
- ✅ 学生新增
- ✅ 学生修改
- ✅ 学生删除
- ✅ 学生导出
- ✅ 权限测试 ← 红框标注的核心新增权限项,权限字符为
system:student:test
配置流程:管理员进入「角色管理」→「普通角色」→「编辑」→ 勾选「权限测试」权限项 → 确定。配置保存后,普通角色的用户即可在前端看到「菜单权限测试」按钮,且调用接口时后端权限校验会通过。
补充说明:「超级管理员」角色默认拥有
*:*:*全权限,不需要单独配置任何菜单权限,所有按钮对其均可见且可用。
五、权限拦截原理讲解
5.1 整体拦截链路
浏览器发起 GET /system/student/testPermi 请求
↓
Spring Security Filter Chain(过滤器链)
↓
进入 MyStudentController.testPermi()
↓
执行 @PreAuthorize("@ss.hasPermi('system:student:test')")
↓
调用 PermissionService.hasPermi("system:student:test")
↓
获取当前登录用户的权限标识集合(从 SecurityUtils 获取)
↓
权限集合中是否包含 "system:student:test" ?
↓
┌──────────────┐ ┌────────────────┐
│ 是 ✅ │ │ 否 ❌ │
│ 放行执行 │ │ 抛出拒绝异常 │
│ 返回成功 │ │ 返回 403 │
└──────────────┘ └────────────────┘
↓ ↓
前端 .then() 前端 .catch()
↓ ↓
弹窗绿色提示 弹窗红色提示
5.2 前端权限与后端权限的关系
| 层次 | 实现机制 | 作用 |
|---|---|---|
| 前端显隐层 | v-hasPermi 指令,移除无权限按钮 DOM |
提升用户体验,减少无效请求 |
| 后端拦截层 | @PreAuthorize 注解,方法级校验 |
安全防线,防止绕过前端直接调用接口 |
| 数据库权限层 | sys_role_menu 关联表,角色-菜单绑定 |
数据持久化存储权限关系 |
⚠️ 重要提醒:前端权限按钮显隐只是用户体验优化,并不是安全防线。恶意用户可以通过 Postman、curl 等工具直接构造请求绕过前端。因此,后端 Controller 的
@PreAuthorize才是真正的安全关卡,二者缺一不可。
六、运行效果展示
6.1 页面效果 —— 菜单权限测试按钮

效果说明:用户以「普通角色」登录后,正确配置了 system:student:test 权限的账号可以看到并点击该按钮。若未配置该权限,按钮不会显示在页面上。
6.2 接口请求 —— 浏览器 Network 面板

关键信息:
- 请求路径:
GET /system/student/testPermi - 响应内容:json
{ "msg": "菜单权限测试接口调用成功!权限校验通过", "code": 200 }
6.3 弹窗提示 —— 权限通过

该弹窗由前端 handleTestPermi 方法的 .then() 分支触发:
this.$modal.msgSuccess(res.msg); // res.msg === "菜单权限测试接口调用成功!权限校验通过"
七、完整流程总结
7.1 权限闭环全链路回顾
| 步骤 | 环节 | 关键代码 / 操作 | 截图 |
|---|---|---|---|
| ① | 管理员在角色管理中勾选「权限测试」权限 | 角色管理 → 编辑 → 勾选 system:student:test |
图6 |
| ② | 用户登录,后端返回权限标识集合 | 用户权限数据写入 Session / Token | - |
| ③ | 前端 Vue 初始化,store.getters.permissions 填充 |
登录成功后权限数组存入 Vuex Store | - |
| ④ | 页面渲染,v-hasPermi 指令执行权限判断 |
v-hasPermi="['system:student:test']" |
图1 / 图3 |
| ⑤ | 按钮可见,用户点击「菜单权限测试」 | @click="handleTestPermi" |
图8 |
| ⑥ | Axios 发起 GET 请求 | testPermi() → request({ url: '/system/student/testPermi' }) |
图2 |
| ⑦ | 后端 Controller 接收请求 | @GetMapping("/testPermi") |
图1 |
| ⑧ | @PreAuthorize 执行权限校验 |
@PreAuthorize("@ss.hasPermi('system:student:test')") |
图1 |
| ⑨ | 权限通过,执行接口方法 | return AjaxResult.success(...) |
- |
| ⑩ | 前端 .then() 收到响应 |
$modal.msgSuccess(res.msg) |
图9 |
| ⑪ | 弹窗显示绿色成功提示 | "菜单权限测试接口调用成功!权限校验通过" | 图9 |
7.2 权限字符串三端一致对照
| 配置位置 | 权限字符串 | 截图 |
|---|---|---|
| 后端 Controller 注解 | system:student:test |
图1 |
| 前端 v-hasPermi 指令 | ['system:student:test'] |
图3 |
| 前端 API 文件 | /system/student/testPermi(路径不含权限字符) |
图2 |
| 数据库菜单表 | system:student:test |
图7 |
| 角色权限分配 | 勾选「权限测试」项 | 图6 |
三端一致是权限系统正常工作的铁律,任一处不匹配都会导致权限失效。
7.3 开发者日常开发 Checklist
✅ 新增按钮 → 在 Vue 模板中添加 v-hasPermi="['模块:实体:操作']"
✅ 新增接口 → 在 Controller 方法上添加 @PreAuthorize("@ss.hasPermi('模块:实体:操作')")
✅ 新增 API 方法 → 在 api/xx.js 中导出对应的 Axios 请求函数
✅ 配置菜单 → 在菜单管理中新增"按钮"类型菜单项,填入权限字符
✅ 分配权限 → 在角色管理中为相应角色勾选该菜单权限
✅ 测试验证 → 以普通用户登录,点击按钮,确认弹窗提示
结语
本文通过若依 RuoYi-Vue-v3.8.2 学生管理模块的「菜单权限测试」功能,完整串联了前端按钮 → v-hasPermi 指令 → API 方法 → Axios 请求 → 后端 Controller → @PreAuthorize 注解 → 权限校验 → 响应返回 → 前端弹窗提示的全链路闭环。
核心要点归纳为三点:
1.前端 v-hasPermi 指令是按钮显隐的第一道关卡,提升用户体验,但不能作为安全防线。
2.后端 @PreAuthorize 注解是真正的权限安全防线,即使绕过前端直接请求接口,后端也会拦截。
3.权限字符串三端一致(后端注解 / 前端指令 / 数据库菜单)是整个权限系统的灵魂,务必保持完全一致。
若依框架将这套权限体系封装得非常完善,开发者在代码生成器生成的模板基础上,仅需关注业务逻辑实现,权限配置交由管理员在运维阶段通过可视化界面完成。
更多推荐

所有评论(0)