vue-vben-admin 如何用权限码和角色实现按钮级细粒度权限控制
【免费下载链接】vue-vben-adminA modern vue admin panel built with Vue3, Shadcn UI, Vite, TypeScript, and Monorepo. It's fast!项目地址: https://gitcode.com/GitHub_Trending/vu/vue-vben-admin
在 vue-vben-admin 中,菜单和路由的显隐只是权限控制的一半,很多业务场景需要控制到按钮级别:同一个页面对不同账号显示不同的操作按钮。框架通过@vben/access包提供了一套按钮级控制能力,判断依据有两类:接口返回的权限码(access code)和登录用户的角色(role),两者都可以用组件、API、指令三种方式写在模板里。本文以 playground 演示应用为运行环境,说明权限码如何从接口流入前端状态、三种控制写法如何区分,以及如何切换账号验证按钮显隐是否符合预期。
准备:跑起带权限演示的 playground 环境
按钮级权限的完整链路(接口返回权限码 → 登录时存入状态 → 模板中判断显隐)在 playground 应用中有可运行的演示,后端由内置的 mock 服务提供,不需要自己搭接口。
环境要求与启动步骤(来自 快速开始):
- Node.js 22.18.0 及以上版本;
- 项目只支持使用
pnpm安装依赖,默认通过corepack安装指定版本的 pnpm。
cd vue-vben-admin # 使用项目指定的pnpm版本进行依赖安装 npm i -g corepack # 安装依赖 pnpm install然后只启动 playground 应用,避免在选择界面里逐项挑选:
pnpm run dev:play启动后在浏览器访问http://localhost:5555。mock 数据里内置了三个账号(见 mock-data.ts),密码均为123456:
| 账号 | 用户名 | 角色 | 权限码 |
|---|---|---|---|
| Super | vben | ['super'] | AC_100100、AC_100110、AC_100120、AC_100010 |
| Admin | admin | ['admin'] | AC_100010、AC_100020、AC_100030 |
| User | jack | ['user'] | AC_1000001、AC_1000002 |
后续所有按钮显隐的验证都以这三个账号的差异为基准。
权限码从哪里来:登录流程中的注入点
按钮能否显示,前提是当前登录账号的权限码已经进入前端状态。登录流程位于应用的src/store/auth,playground 中的实现在 store/auth.ts:
const [fetchUserInfoResult, accessCodes] = await Promise.all([ fetchUserInfo(), getAccessCodesApi(), ]); userInfo = fetchUserInfoResult; accessStore.setAccessCodes(accessCodes);也就是说,登录成功后会并行请求用户信息和权限码,权限码通过accessStore.setAccessCodes写入 access store,之后模板中的判断函数读取的都是这份状态。
对应接口在 api/core/auth.ts 中定义,为一个返回字符串数组的 GET 请求:
export async function getAccessCodesApi() { return requestClient.get<string[]>('/auth/codes'); }权限码返回的数据结构为字符串数组,例如:['AC_100100', 'AC_100110', 'AC_100120', 'AC_100010']。
官方文档要求:找到getAccessCodes对应的接口,可根据业务逻辑进行调整。在本仓库中,mock 实现位于 apps/backend-mock/api/auth/codes.ts,它根据登录用户从 MOCK_CODES 中按用户名匹配出权限码数组返回;替换为真实后端时,只需要让新接口保持"字符串数组"这一返回结构。
三种写法控制按钮:组件、API、指令
拿到权限码后,用@vben/access提供的AccessControl组件和 API 来控制按钮显示与隐藏。以下示例与 docs/src/guide/in-depth/access.md 及 button-control.vue 中的演示一致,权限码均取自上表。
组件方式
AccessControl通过type属性区分判断依据:传type="code"时按权限码判断,codes支持单个或数组(多个权限码只要有一个满足即显示):
<script lang="ts" setup> import { AccessControl, useAccess } from '@vben/access'; const { accessMode, hasAccessByCodes } = useAccess(); </script> <template> <!-- 需要指明 type="code" --> <AccessControl :codes="['AC_100100']" type="code"> <Button> Super 账号可见 ["AC_100100"] </Button> </AccessControl> <AccessControl :codes="['AC_100030']" type="code"> <Button> Admin 账号可见 ["AC_100030"] </Button> </AccessControl> <AccessControl :codes="['AC_1000001']" type="code"> <Button> User 账号可见 ["AC_1000001"] </Button> </AccessControl> <AccessControl :codes="['AC_100100', 'AC_100030']" type="code"> <Button> Super & Admin 账号可见 ["AC_100100","AC_100030"] </Button> </AccessControl> </template>API 方式
useAccess()暴露hasAccessByCodes,配合v-if使用,适合需要在逻辑里做进一步分支的场景:
<script lang="ts" setup> import { AccessControl, useAccess } from '@vben/access'; const { hasAccessByCodes } = useAccess(); </script> <template> <Button v-if="hasAccessByCodes(['AC_100100'])"> Super 账号可见 ["AC_100100"] </Button> <Button v-if="hasAccessByCodes(['AC_100030'])"> Admin 账号可见 ["AC_100030"] </Button> <Button v-if="hasAccessByCodes(['AC_1000001'])"> User 账号可见 ["AC_1000001"] </Button> <Button v-if="hasAccessByCodes(['AC_100100', 'AC_100030'])"> Super & Admin 账号可见 ["AC_100100","AC_100030"] </Button> </template>指令方式
指令支持绑定单个或多个权限码:单个时可以直接传入字符串,多个则传入数组。判断不通过时元素会被移除(见 directive.ts 中el?.remove()的实现):
<template> <Button class="mr-4" v-access:code="'AC_100100'"> Super 账号可见 'AC_100100' </Button> <Button class="mr-4" v-access:code="['AC_100030']"> Admin 账号可见 ["AC_100030"] </Button> <Button class="mr-4" v-access:code="['AC_1000001']"> User 账号可见 ["AC_1000001"] </Button> <Button class="mr-4" v-access:code="['AC_100100', 'AC_1000001']"> Super & Admin 账号可见 ["AC_100100","AC_1000001"] </Button> </template>按角色控制按钮及其适用条件
角色判断方式不需要接口返回的权限码,直接通过userInfo.roles判断按钮是否显示。三种写法与权限码一一对应:
<script lang="ts" setup> import { AccessControl } from '@vben/access'; </script> <template> <AccessControl :codes="['super']"> <Button> Super 角色可见 </Button> </AccessControl> <AccessControl :codes="['admin']"> <Button> Admin 角色可见 </Button> </AccessControl> <AccessControl :codes="['user']"> <Button> User 角色可见 </Button> </AccessControl> <AccessControl :codes="['super', 'admin']"> <Button> Super & Admin 角色可见 </Button> </AccessControl> </template><script lang="ts" setup> import { useAccess } from '@vben/access'; const { hasAccessByRoles } = useAccess(); </script> <template> <Button v-if="hasAccessByRoles(['super'])"> Super 账号可见 </Button> <Button v-if="hasAccessByRoles(['admin'])"> Admin 账号可见 </Button> <Button v-if="hasAccessByRoles(['user'])"> User 账号可见 </Button> <Button v-if="hasAccessByRoles(['super', 'admin'])"> Super & Admin 账号可见 </Button> </template><template> <Button class="mr-4" v-access:role="'super'"> Super 角色可见 </Button> <Button class="mr-4" v-access:role="['admin']"> Admin 角色可见 </Button> <Button class="mr-4" v-access:role="['user']"> User 角色可见 </Button> <Button class="mr-4" v-access:role="['super', 'admin']"> Super & Admin 角色可见 </Button> </template>两点需要注意:
AccessControl组件的type属性默认为'role'(见 access-control.vue),所以按角色控制时可以不显式写type;按权限码控制时必须写type="code"。v-access:role指令只在权限模式为frontend时才按角色判断,其他模式下该指令会走权限码判断逻辑(见 directive.ts 中accessMode.value === 'frontend' && binding.arg === 'role'的分支)。这也解释了为什么演示页面 button-control.vue 只在accessMode === 'frontend'时才渲染角色相关的卡片。
权限模式在应用目录的preferences.ts中通过app.accessMode配置,可选frontend、backend、mixed,写法参考 权限文档。以 playground 为例,在 playground/src/preferences.ts 的overridesPreferences.app中加一行:
app: { accessMode: 'frontend', },该文件注释明确提醒:更改配置后请清空缓存,否则可能不生效。文档同时说明了前端模式的局限:权限相对不自由,如果后台改动角色,前台也需要跟着改动,适合角色较固定的系统;如果角色或权限码经常变化,应使用后端/混合模式,改由接口决定权限。
验证:切换账号观察按钮变化
验证方式直接沿用演示页的设计——切换到不同账号,观察同一页面上按钮集合的变化。以权限码为例,对照表可以预先列出预期结果(依据是上文三个账号的权限码数据):
| 按钮绑定的权限码 | vben (Super) | admin (Admin) | jack (User) |
|---|---|---|---|
['AC_100100'] | 显示 | 隐藏 | 隐藏 |
['AC_100030'] | 隐藏 | 显示 | 隐藏 |
['AC_1000001'] | 隐藏 | 隐藏 | 显示 |
['AC_100100', 'AC_100030'] | 显示 | 显示 | 隐藏 |
具体操作路径:
- 用
vben/123456登录,进入演示路由/demos/access/button-control(mock 菜单数据中该路由名为AccessButtonControlDemo,对应组件 button-control.vue)。 - 确认页面上当前显示 Super 可见的按钮(
AC_100100、AC_100100+AC_100030组合),不显示 Admin、User 按钮。 - 在页面"账号切换"卡片中点击"切换为 Admin 账号",页面内部执行
resetAllStores()后重新登录并刷新(router.go(0)),此时AC_100030相关按钮出现、Super 按钮消失。 - 再切换到 User 账号(
jack),只应看到AC_1000001相关按钮。
页面顶部会显示"当前角色",可以用来确认切换账号已经生效;如果按钮显示与上表不一致,优先检查登录流程里getAccessCodesApi()是否真的被调用并写入了accessStore,以及权限码字符串是否与接口返回值完全一致(权限码是精确字符串匹配,如AC_100100与AC_1000001是两个不同的码)。
限制与边界
AccessControl组件在codes为空数组时会直接渲染插槽内容(v-if="!codes"),即不传权限码等于不控制,配置漏传不会导致按钮被隐藏,排查"按钮不该显示却显示了"时要先确认codes真的传入了。- 按钮级控制只负责前端显示/隐藏。菜单级别还有一个相关但不同的机制:
meta.menuVisibleWithForbidden: true可以让菜单可见但访问时跳转 403,这是路由权限配置,与本文的按钮控制不冲突,也不要用它来隐藏按钮。 - 前端权限码和角色都只控制展示,真实的数据安全仍要由后端接口自行校验,这一点在框架层面不做保证,接入真实后端时需要自行落实。
完成以上配置后,按钮级权限的落地就完整了:接口负责发放权限码,store/auth负责在登录时写入状态,模板里用组件、API 或指令三选一完成显隐判断,最后用三个内置账号的切换演示核对每个按钮的归属是否符合预期。
【免费下载链接】vue-vben-adminA modern vue admin panel built with Vue3, Shadcn UI, Vite, TypeScript, and Monorepo. It's fast!项目地址: https://gitcode.com/GitHub_Trending/vu/vue-vben-admin
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考