vue后台管理系统中的权限控制

0.背景和目的

后台管理系统一般都少不了登录权限控制,这里我们讨论以下如何实现权限控制。

1.主要概念

一个完整的权限控制模块涉及较多,一般包括用户管理、角色管理、资源管理(即菜单管理,当前系统总共有哪些菜单)。

用户

这个没什么好说的,就是你登录时候的账号,用户有自己的信息,比如姓名,账号密码,手机号等,用户登录之后会拿到用于区别用户身份的token,一般token会放在请求的接口的header里面,供后端来确定当前用户。

一个用户可以属于一个或者多个角色,用户会获得角色下的权限。

角色

角色可以理解为具有某些权限的一类用户,比如管理员角色,用于维护整个系统,具有最高权限,普通操作人员可能只具有某些业务的普通操作权限。

权限菜单

权限菜单可以理解为资源,比如当前有哪些菜单,哪些功能,功能下面有哪些按钮等。

权限有不同的粒度,比如有的系统相对简单只需要控制到具体页面即可,不再具体控制该页面下的具体按钮。

还有更进一步,除了菜单按钮之外,还可以控制到具体业务数据。

本篇只讨论控制到菜单和按钮级别。

对应关系图

2.核心流程

从流程上来说用户权限包括两部分:设定权限、拉取权限

设定权限

设定系统菜单

即当前系统的功能菜单、按钮管理,可以做成一个功能,如果系统简单也可以手动编辑数据库实现。

设定角色下的权限

由用户来勾选角色下包括的菜单、按钮。

设定用户所属角色

用户可以属于一个或几个角色,用户的权限是所属的角色最大集合


拉取权限

这里我们可以在全局状态管理(Vuex)设置一个状态标识(hasLoadAccess)和一个获取权限动作(loadAccess)

早期的时候动态生成路由并不现实,但当vue-router增加了addRouters方法之后,这一切变得简单了。

当用户拉取权限之后,根据得到的数据生成路由并addRouters和生成导航菜单。

3.服务端设计

数据库表设计

menu表

用于存储系统中需要权限验证的路由

id    int  主键自增

name String 菜单功能名称

index String  标识,用户匹配本地组件

parentId int 菜单所属,如果是一级菜单值为0

isMenu int   是否是菜单

这里需要特别注意,因为有些路由属于菜单,有些路由不属于菜单,比如用户管理页面是需要显示在导航菜单中,但用户新增、用户编辑不应该出现在导航菜单中,这里我们都当作路由处理,有一个好处就是用户刷新可以停留在当前页面,逻辑相对简单,当然也可以不使用路由来处理这种编辑的页面,比如通过对话框、抽屉等,根据实际交互设计选择

icon String 结合iconfront 相应图标的类名

container String 容器组件标识,这里我们讨论二层路由,container为第一层组件

path String 路由,绝对路径

sort int 排序,用于控制菜单的顺序,可以手动修改

按钮表

存储系统中的所有按钮

id int 主键

uuid string 唯一标识

name String 按钮名称

parentId int 所属菜单的id

角色表

id int 主键id

name String 角色名称

sort int  顺序

角色权限表

id int 主键id

roleId int 角色Id

menuId int 菜单Id

用户表

id int 主键id

name String 用户姓名

username String 用户名

password String 加密码存储的密码

phone string 电话号码 不是必须有的字段,根据业务需求设定

用户角色关系表

id int  主键

userId int  用户id

roleId int 角色id

权限控制少不了后端的配合,除了对角色,用户等的增删改查外,还需要提供一个接口getAccess返回用户拥有的权限,返回结构大致如下:

{

code: 0,

    data: [

{

id: 1

index: 'user-management',

name: '用户管理',

icon: 'icon icon-user',

refer: '/user-management',

            children: [

{

index:  '',

name: '',

refer: '',

isMenu: true

                }

...

            ],

...

        }

    ]

}

4.本地路由组织

存储本地所有路由组件

export default {

    'console': import('path/console.vue'), // 根容器

   'index':import('path/index.vue'), //  以键值对的形式,键为数据库中的index  根据index进行匹配,值为组件,这里使用import的方式

   'user-management': import('path/index.vue')

}

5.权限获取

结合vue-router,我们可以在路由守卫的钩子函数beforeEach中执行动作,伪代码如下:

import router from 'vue-router'

import Store from 'vuex'

const whiteList = [  'login' ]  //不需要权限的路由

const token = Cookies.get('token')

...

router.beforeEach((to, from, next) => {

    if(whiteList.indexOf(to.name) > -1)  {  // 跳转的是不需要登录状态的路由  直接放行

        next()

    } else if(!token) { // 需要权限  但是token不存在  直接跳转到登录页面

        router.push({path: '/login'})

    } else if(!Store.hasLoadAccess) {  //需要权限 且token 但没有还没有获取权限  则开始获取权限

        Strore.loadAccess()

    } else {  // 其余直接跳转到相应路由  这里一定要调用next 函数,不然不能跳转

        next()

    }

})

// 拉取权限动作,此处在vuex模块access中

export default {

    state: {

        hasLoadAccess: false,

        accessTree: []

    },

mutiation:{

        setLoadStatus(state, status){

            state.hasLoadAccess = status

        }

}

action: {

        async loadAccess(){

            const res = await getAccess()

            if(res.success){

                    // 处理路由

            }

        }

    }

}

6.动态生成路由和导航菜单

生成路由

根据后台服务返回,生成路由数组,路由对应的组件从resourceMap中根据index进行匹配。

并不是所有路由都需要根据根据后端数据来生成,这里主要是指不需要用户权限验证的页面,如登录页面、找回密码等页面,这些路由可以写在前端初始路由里面。

生成菜单

生成菜单可以放在导航菜单的计算属性中,或者直接vuex 中的getter中

7.用户管理、角色管理、资源管理

此处为正常的增删改茶没有需要特别注意的内容

8.优化

按照以上逻辑即可实现权限控制,但是还有以下几个问题需要处理

1.当跳转到不是菜单对应的路由时,菜单缺少高亮状态

可以在menu表中增加refer字段,并存储到meta中,设定当前路由应该高亮的路由,比如用户管理页面的refer为‘/user-management’,即本身的path,用户新增、编辑页面的refer也为‘/user-management’

el-menu 组件的default-active设置为router.meta.refer

2.默认路由问题,一般登录后的默认主页是home页面,但如果在菜单设置的时候用户不勾选home页面如何处理。

有两种处理方式:

1、设置角色权限时home默认为选中状态且不允许取消选中。

2、允许不选择home菜单,当用户没有home页权限时,自动生成一个home页面,即不包含任何数据的欢迎页面。

3.后端接口过滤问题

这些过滤只是在前端对用户的动作进行限制,但是防君子不防小人,因为用户完全可以绕过前端的这些限制,直接利用postman请求接口,所以权限限制最终还要后端处理,进行用户验证。不过这是后端同学的工作,我们不做讨论。

4.资源管理问题

资源管理也就是我们的菜单、按钮的管理,如果产品在不断的迭代,需要频繁修改菜单,或者想把产品做的更晚上,需要一个资源管理功能,通过接口来处理菜单逻辑。

如果资源管理频率很低,也可以手动修改数据库,但需要注意的是手动修改menu表,不会处理角色的逻辑关系,当新增或者删除菜单时,需要重新保存角色权限,才会更新角色权限。

9.结语

以上是对vue项目中权限控制的一种简单实现,适合小型系统,如果对于多系统统一认证的系统,本篇文章的后端部分并不适用,前端部分可以借鉴参考。

原创不易,如果本篇文章对您有所帮助,还请点赞支持。如果您有疑问或者建议,请留言,我会在方便的时候做解答。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容