权限控制方案
既然是后台权限管理系统,当然少不了权限控制啦,至于权限控制,前端方面当然就是对页面资源的访问和操作控制啦。
前端资源权限主要又分为两个部分,即导航菜单的查看权限和页面增删改操作按钮的操作权限。
我们的设计把页面导航菜单和页面操作按钮统一存储在菜单数据库表中,菜单表中包含以下权限关注点。
菜单类型
菜单类型代码页面资源的类型。类型包括,0:目录 1:菜单 2:按钮'。
权限标识
权限标识即是代表此页面资源,用来进行权限控制的唯一标识,主要是进行增删改查的权限控制。
权限标识包括,sys:user:add:新增 sys:user:edit:编辑 sys:user:delete:删除 sys:user:view:查看。
注:目前查看都可以通过菜单可见性进行控制,所以查看权限标识目前没有用上,如果需要显示无权限页面可以使用。
菜单表结构
具体的菜单表结构如下。
-- ------------------------------------------------ -- 菜单 -- ------------------------------------------------ -- Table structure for `sys_menu` -- ------------------------------------------------ CREATE TABLE `sys_menu` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号', `name` varchar(50) COMMENT '菜单名称', `parent_id` bigint COMMENT '父菜单ID,一级菜单为0', `url` varchar(200) COMMENT '菜单URL', `perms` varchar(500) COMMENT '授权(多个用逗号分隔,如:sys:user:add,sys:user:edit)', `type` int COMMENT '类型 0:目录 1:菜单 2:按钮', `icon` varchar(50) COMMENT '菜单图标', `order_num` int COMMENT '排序', `create_by` varchar(50) COMMENT '创建人', `create_time` datetime COMMENT '创建时间', `last_update_by` varchar(50) COMMENT '更新人', `last_update_time` datetime COMMENT '更新时间', `del_flag` tinyint DEFAULT 0 COMMENT '是否删除 -1:已删除 0:正常', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='菜单管理';
导航菜单实现思路
1.用户登录系统
用户登录系统之后,跳转到首页。
2.根据用户加载导航菜单
在路由导航守卫路由时加载用户导航菜单并存储到store。
加载过程如下,返回结果排除按钮类型。
user -> user_role -> role -> role_menu -> menu。
3.导航栏读取菜单树
导航栏到sotre读取导航树并进行展示。
页面按钮实现思路
1.用户登录系统
用户登录系统之后,跳转到首页。
2.根据用户加载权限标识集合
在路由导航守卫路由时加载用户权限标识集合。
加载过程如下,返回结果是用户权限标识的集合。
user -> user_role -> role -> role_menu -> menu。
3.页面按钮控制
页面操作按钮提供权限标识,查询是否在用户权限标识集合中。
在:有权限,可见或可用,不在:无权限,不可见或禁用。
目前本系统采用的是状态禁用。
权限控制实现
导航菜单权限
加载导航菜单
如下图所示,在导航守卫路由时加载导航菜单并保存状态。
router/index.js
页面组件引用
导航栏页面从共享状态中读取导航菜单树并展示。
views/NavBar/NavBar.vue
views/NavBar/NavBar.vue
页面按钮权限
添加权限获取接口
http/modules/user.js
// 查找用户的菜单权限标识集合 export const findPermissions = (params) => { return axios({ url: '/user/findPermissions', method: 'get', params }) }
添加权限获取接口
store/modules/user.js
export default { state: { perms: [], // 用户权限标识集合 }, getters: { }, mutations: { setPerms(state, perms){ // 用户权限标识集合 state.perms = perms; } }, actions: { } }
加载权限标识
如下图所示,在导航守卫路由时加载权限标识并保存状态。
router/index.js
权限按钮判断
封装了权限操作按钮组件,在组件中根据外部传入的权限标识进行权限判断。
views/Core/KtButton.vue
<template> <el-button :size="size" :type="type" :loading="loading" :disabled="!hasPerms(perms)" @click="handleClick"> {{label}} </el-button> </template> <script> import { hasPermission } from '@/permission/index.js' export default { name: 'KtButton', props: { label: { // 按钮显示文本 type: String, default: 'Button' }, size: { // 按钮尺寸 type: String, default: 'mini' }, type: { // 按钮类型 type: String, default: null }, loading: { // 按钮加载标识 type: Boolean, default: false }, disabled: { // 按钮是否禁用 type: Boolean, default: false }, perms: { // 按钮权限标识,外部使用者传入 type: String, default: null } }, data() { return { } }, methods: { handleClick: function () { // 按钮操作处理函数 this.$emit('click', {}) }, hasPerms: function (perms) { // 根据权限标识和外部指示状态进行权限判断 return hasPermission(perms) & !this.disabled } }, mounted() { } } </script> <style scoped> </style>
权限判断逻辑
src/permission/index.js
import store from '@/store' /** * 判断用户是否拥有操作权限 * 根据传入的权限标识,查看是否存在用户权限标识集合 * @param perms */ export function hasPermission (perms) { let hasPermission = false let permissions = store.state.user.perms for(let i=0, len=permissions.length; i<len; i++) { if(permissions[i] === perms) { hasPermission = true; break } } return hasPermission }
权限按钮引用
views/Sys/User.vue
测试效果
1.可用状态,操作按钮可用。
2.修改页面的权限标识,导致认证失败。
如下图所示,修改新增和删除按钮的权限标识(加个2),导致权限认证失败。
3.无权限,操作按钮禁用。
新增和删除按钮因为修改了权限标识,匹配失败,变成了禁用状态。
源码下载
后端:https://gitee.com/liuge1988/kitty
前端:https://gitee.com/liuge1988/kitty-ui.git