前端权限
前端权限管理涵盖权限资源、菜单、按钮、列表及表单等多维度,确保用户仅能访问和操作其被授权的内容。
权限资源
本框架维护菜单、按钮资源有两种方式,一种是在菜单管理模块下增、删、改、查,另一种则是通过维护前端路由并同步到菜单,这里主要演示前端路由同步的方式
资源维护
下面以系统管理模块路由为例:
import { AppRouteRecordRaw } from '/@/router/types';
import { LAYOUT } from '/@/router/constant';
const menu: AppRouteRecordRaw = {
name: 'sys:manager',
path: '/sys/manager',
component: LAYOUT,
meta: {
title: '系统设置',
icon: 'ant-design:setting-outlined',
perms: ['admin', 'sys:manager'],
orderNo: 100,
component: 'LAYOUT',
},
children: [
{
name: 'sys:user',
path: '/sys/user/index',
component: () => import('/@/views/sys/user/index.vue'),
meta: {
title: '用户管理',
icon: 'ant-design:user-outlined',
orderNo: 210,
perms: ['admin', 'sys:user'],
component: '/sys/user/index',
btns: {
'sys:user:page': '分页查询用户',
'sys:user:detail': '查看用户详情',
'sys:user:save': '添加用户',
'sys:user:update': '修改用户',
'sys:user:remove': '删除用户',
'sys:playUser': '扮演用户',
'sys:user:grantRole': '授权角色',
},
},
},
],
};
export default menu;
一级菜单(目录)
一般为目录,没有具体的路由页
import { AppRouteRecordRaw } from '/@/router/types';
import { LAYOUT } from '/@/router/constant';
const menu: AppRouteRecordRaw = {
name: 'sys:manager',
path: '/sys/manager',
component: LAYOUT,
meta: {
title: '系统设置',
icon: 'ant-design:setting-outlined',
perms: ['admin', 'sys:manager'],
orderNo: 100,
component: 'LAYOUT',
},
children: [],
}
- name 组件名称,一般和权限码对应,格式如:
a:b:c
,用:号分割 - path 路由地址,由name进行转换,如:
a:b:c
==>/a/b/c
- component,组件对象,一级菜单一般为布局组件LAYOUT
- meta 组件的扩展属性
- title 标题,中文名称
- icon 图标,ant-design的图标则以ant-design开头加冒号拼接,可选值可看文件,一般ant-design的图标都支持:
src/components/Icon/data/icons.data.ts
- perms 权限码,用户拥有该权限码则会显示,注:admin是必须配置,方便超管拥有所有权限;
- orderNo 排序,路由按升序排序
- component 组件相对地址,为字符串,用于同步给后端的component的,如后续有动态菜单需求,可以次来改造,这里不先不展开。
二级菜单
可为目录,也可为路由(菜单)页 对比一下
- name:
'sys:user'
- path:
'/sys/user/index'
- component :
() => import('/@/views/sys/user/index.vue')
- perms:
['admin', 'sys:user']
- component:
'/sys/user/index'
其实就是有一定的规律,也算是一种路由规则。
三级接口/按钮
在上面的例子中,我们还会看到有btn的配置
这个其实就是对应这个菜单(路由页)的接口、按钮权限。本着只有前端开发人员才知道该页面用到哪些接口、按钮权限为原则,该信息理应由前端维护,所以就统一放在路由中进行配置。
菜单权限
菜单权限的定义
菜单权限是指定义在管理系统中的左侧菜单(或其他导航结构)中哪些菜单项可以被特定角色或用户访问和操作。通过菜单权限的设置,可以确保不同角色或用户只能看到和操作他们被授权的部分。
新增功能模块与菜单的生成
当在管理系统中新增一个功能模块时,通常需要为这个模块生成对应的菜单项。这个过程可能涉及到前端和后端的协作。前端负责展示菜单项,后端负责处理与这个模块相关的数据和逻辑。本框架主要是通过同步路由清单的方式对菜单进行维护的。
角色与菜单权限的分配
在生成了对应的菜单项之后,下一步是将这些菜单项与角色进行关联,即给角色分配相应的菜单权限。这通常是通过权限管理系统来实现的。管理员可以在权限管理系统中,为不同的角色选择他们可以访问的菜单项,从而控制他们在系统中的操作范围。
按钮权限
在前端实现按钮级权限通常涉及以下几个步骤:
- 定义权限数据:首先,你需要定义系统中所有可能的权限,并将它们与特定的角色或用户关联起来。这通常是通过后端服务来完成的,前端通过API接口获取这些数据。
- 获取用户权限:当用户登录到系统时,前端需要获取用户的身份信息和相关的权限数据。这通常是通过API调用实现的,本系统通过调用
/sys/user/permCode
接口来获取用户当前的角色和对应的权限列表。 - 渲染按钮:在前端渲染按钮时,根据用户权限数据来决定是否显示某个按钮。这可以通过使用指令(Directives)或组件(Components)来实现。
按钮权限标识维护
对应的是路由配置项的meta.btns
使用指令
在Vue.js中,你可以创建自定义指令来实现按钮级权限。例如,创建一个名为v-auth的指令,该指令会根据用户权限决定是否渲染某个元素(按钮)。 本框架自定义指令代码,用于实现按钮级权限,具体看:src/directives/index.ts
然后,在你的模板中,你可以使用v-auth指令来控制按钮的显示:
<a-button v-auth="['admin','sys:user:save']">添加用户</a-button>
<a-button v-auth="['admin','sys:user:update']">修改用户</a-button>
在这个例子中,'admin'、'sys:user:save'、'sys:user:update'是权限标识符,它们应该与后端定义的权限相匹配。如果用户具有'admin'或'sys:user:save'权限,则第一个按钮将被渲染;如果用户具有'admin'或'sys:user:update'权限,则第二个按钮将被渲染。
使用组件
src/components/Authority/src/Authority.vue
<template>
<PageWrapper>
<Authority :value="['admin', 'sys:user:save']">
<a-button type="primary"> 添加用户 </a-button>
</Authority>
<Authority :value="['sys:user:update']">
<a-button type="primary"> 修改用户 </a-button>
</Authority>
</PageWrapper>
</template>
<script setup>
import Authority from '/@/components/Authority/src/Authority.vue';
</script>
请注意,这种方法仅在前端进行权限检查,后端也应该进行相应的权限验证,因为前端验证可以被绕过。后端验证是确保系统安全性的关键。
列表权限
前面的课程我们也提到过,列表的列配置中是有auth和ifShow配置权限的
auth权限码样例
<template>
<PageWrapper>
<BasicTable @register="register" />
</PageWrapper>
</template>
<script setup>
import { BasicTable, useTable } from '/@/components/Table';
import { sysUserPage } from '/@/api/sys/user';
const columns = [
{
title: '用户名',
dataIndex: 'userName',
},
{
title: '姓名',
dataIndex: 'realName',
},
{
title: '手机号',
dataIndex: 'mobilePhone',
},
{
title: '性别',
dataIndex: 'sex',
component: 'ApiDict',
componentProps: {
code: 'sex',
},
auth: 'admin', // 显示
// auth: 'adminxxxx', // 隐藏
},
];
const [register] = useTable({
columns: columns,
api: sysUserPage,
});
</script>
ifShow权限码样例
<template>
<PageWrapper>
<BasicTable @register="register">
<template #mm-sex="{ text }">
<a-tag v-if="text == 1" color="blue">男</a-tag>
<a-tag v-else-if="text == 2" color="red">女</a-tag>
<a-tag v-else>未知</a-tag>
</template>
</BasicTable>
</PageWrapper>
</template>
<script setup>
import { BasicTable, useTable } from '/@/components/Table';
import { sysUserPage } from '/@/api/sys/user';
import { usePermission } from '/@/hooks/web/usePermission';
const { hasPermission } = usePermission();
const columns = [
{
title: '用户名',
dataIndex: 'userName',
},
{
title: '姓名',
dataIndex: 'realName',
},
{
title: '手机号',
dataIndex: 'mobilePhone',
},
{
title: '管理员类型',
dataIndex: 'adminType',
component: 'ApiDict',
componentProps: {
code: 'sys_user_admin_type',
},
auth: ['admin', 'adminxxx'], // 拥有admin或adminxxx权限码的用户才能看到
},
{
title: '性别',
dataIndex: 'sex',
slots: { customRender: 'mm-sex' },
ifShow() {
return hasPermission('adminxx');
},
},
];
const [register] = useTable({
showIndexColumn: false, // 是否显示序号
columns: columns, // 表格列配置
showTableSetting: true, // 是否显示表格设置
api: sysUserPage, // 表格请求数据接口
});
</script>
表单权限
本框架表单字段并没有权限码配置,只能通过ifShow来实现
<template>
<PageWrapper>
<BasicForm @register="register" />
</PageWrapper>
</template>
<script setup>
import { BasicForm, useForm } from '/@/components/Form';
import { usePermission } from '/@/hooks/web/usePermission';
const { hasPermission } = usePermission();
const schemas = [
{
field: 'name',
label: '名称',
component: 'Input',
componentProps: {
placeholder: '请输入名称',
},
ifShow() {
return hasPermission(['adminxx']);
},
},
{
field: 'remark',
label: '备注',
component: 'InputTextArea',
componentProps: {
placeholder: '请输入备注',
},
},
];
const [register] = useForm({
schemas: schemas,
submitButtonOptions: {
text: '提交',
},
});
</script>