-
Vue
路由权限控制
Vue
路由权限控制
当我们在做后台管理系统的时候,都会涉及到系统左侧的菜单
树如何动态
显
示
的
问
题
。
目
前
基
本
上
都<
/p>
是
RBAC
的
解
决
方
案
,
p>
即
Role-Based
Access
Control
,权限与角色相关联,用户通过成为适当角色的
成员而
得到这些角色的权限。这就极大地简化了权限的管理。
vue
有很多优秀的后台管理系统模板,这些开源项目都提供了
RBAC
权限
控制的思路,但是在实际
项目中,写死角色的方式可能并不适合。
看了网上蛮多的解决方案,个人感觉都有弊端,好多都是前端
先把完整的
路由表注册到项目中,然后通过后台返回树过滤显示的方案,这样的做法
p>
其实只是隐藏了左侧菜单,但是路由还是已经注册进去了,用户猜到访问
路径还是可以轻易进入页面,没有真正的做到动态路由加载
以下是我设计的解决方案,
-
。
-
<
/p>
先看一下已经完成的系统结构,便于理解,用户绑定角色
(
一对多
)
,角色
绑定
菜单
(
一对多
)
用户菜单
选择角色
角色菜单
选择菜单,由于本项目是多系统,所以会有
ADMIN
和
HMI
两个子系
统,后面再来解释
p>
资源管理
(
我这里没有叫做菜单管理,因为
会涉及到各个子系统我称作模
块,模块下面有菜单,菜单下面有按钮
)
好了,看完几张图大家估计也明白了这就是典型的
RBAC
。内部具体怎么
运作的呢
要实现动态添加路由,即只有有权限的路由才会注
册到
Vue
实例中,核
心是
vue-router
的
addRoutes
p>
和导航钩子
beforeEach
两个方法
大体思
路为,在
beforeEach
方法中
(
即每次路由跳转之前做判断
)
,如果
已经加载了路由表,则把路由表注册到实例中,如果没有,则从后端拉取
路由表注册到实例中。那为什么不在登录的时候加载一次就可以了呢?这
是因为
如果只是登录的时候加载一次,网页刷新的时候注册的路由表会丢
失,所以我们统一在<
/p>
beforeEach
这个钩子中去实现
// ,
此文件在
< br>
中直接导入即可,这边的思路是仿照的
element-
admin
import router from
'./router'
import store from
'./store'
import NProgress
from
'nprogress'
// progress
bar
import
'nprogress/'
// progress bar
style
import Cookies from
'js-cookie'
import
screenfull from
'screenfull'
Each(async (to, from, next)
=> {
const token =
(
'token'
)
()
if
(token) {
//
如果已经处于登录状态,跳到登录页重定向到首页
if
( ===
'/login'
) {
next({ path:
'/'
})
()
}
else
{
if
(!ized) {
try {
tes(await
ch(
'setAccessRoutes'
))
ch(
'setAllDict'
)
next({ ...to, replace:
true
})
} catch (e) {
(
'token'
)
(
'userInfo'
)
next({ path:
'/login'
})
()
}
}
else
{
next()
//
全屏参数判断该页面是否全屏
if
(!led)
return
if
( && reen) {
t().catch(() => null)
}
else
{
if
(screen) {
()
}
}
}
}
}
else
{
next( !==
'/login'
? { path:
'/login'
} :
true
)
}
})
ach(() => {
()
})
由于路由是动态注册的,所以项目的初始路由就会很简洁,只要提供基础
的路由
,其他路由都是从服务器返回之后动态注册进来的
//
import Vue
from
'vue'
import Router
from
'vue-router'
import
Login from
'modules/Login'
import NoPermission from
'modules/NoPermission'
(Router)
//
Fixed NavigationDuplicated Problem
const originalPush =
=
function
push(location,
onComplete, onAbort) {
if
(onComplete || onAbort)
return
(this, location,
onCompl
ete, onAbort)
return
(this,
location).catch(err => err)
}
const createRouter = () =>
new Router({
mode:
'history'
,
scrollBehavior: () => ({ y: 0 }),
routes: [
{
path:
'/'
,
redirect:
'/platform'
},
{
path:
'/noPermission'
,
component: NoPermission
},
{
path:
'/login'
,
component: Login
}
]
})
const router = createRouter()
export
function
resetRouter
() {
const newRouter = createRouter()
r
= r // reset router
}
export
default router
webpack
之前不支持动态编译
,所以很多项目都在路由表维护了一份映射表如下:
const routerMap = {
user: () =>
import(
'/views/user'
),
role: () =>
import(
'/views/role'
),
...
}
我觉得这样很不
nice
,最新版的
vue-cli
集成的
webpack
已经可以支持
动态导入啦,因此可以把
所有的路由信息全部放到数据库里面配置,前端
不在需要维护一份
router
的映射关系表啦,如果你是老版的
CLI
,可以
使用
dynamic-
import
我们再来看看下面这个神奇的文件,也可以大致
浏览一下然后看下面的解
释
//
// id:
随便是什么规则,只要唯一就行,这里前端写死
ID
而不是每次导入数据库时候再生成是因
为如果每次入库的时候重新生成会丢失之前的关联
关系
//
title:
菜单的标题
//
name:
唯一标识
//
type
:
'MD'
代
表模块
(
子系统
),
< br>'MN'
代表菜单,
'BT'
代
表按钮,如果需要控制到按钮权限则需要
配置到
BT
级别
//
icon:
菜单的图标
//
uri:
菜单的路由地址
// co
mponentPath:
该菜单在对应前端项目的路径,在后续的
会看到用法,就是上述说
的不需要在写一份
routerMap
// hidden:
作为菜单
的时候是否在左侧显示,有些菜单比如某个列表的详情页,需要注册到实例
中,但是并不
需要在左侧菜单栏显示
// noCache:
由于项目页面增加了缓存控制,因此该字段用于判断当前页面是否需要缓存
// fullScreen:
有些菜单,进入的时候就是全屏
展示的,例如某些大屏展示页面,通过该字段配
置
//
children:
和上述字段一样
[
{
:
,
:
,
:
,
:
,
: [
{
{
:
,
:
系统管理
,
:
,
:
,
:
,
:
,
:
,
: [
{
:
,
:
用户管理
,
:
,
:
,
:
,
:
,
: [
{
:
,
:
新增
,
:
,
:
},
{
:
,
:
编辑
,
:
,
:
},
{
:
,
:
删除
,
:
,
:
},
{
:
,
:
修改密码
,
:
,
:
},
{
:
,
:
选择角色
,
:
,
:
}
]
},
{
:
,
:
角色管理
,
:
,
:
,
:
,
:
,
: [
{
:
,
:
新增
,
:
,
:
},
{
:
,
:
编辑
,
:
,
:
},
{
:
,
:
删除
,
:
,
:
},
{
:
,
:
设置菜单
,
:
,
:
}
]
},
{
:
,
:
资源管理
,
:
,
:
,
:
,
:
,
: [
{
:
,
:
新增
,
:
,
:
},
{
:
,
:
编辑
,
:
,
:
},
{
:
,
:
删除
,
:
,
:
}
]
}
]
},
}
-
-
-
-
-
-
-
-
-
上一篇:《父母与孩子之间的爱》导学案完美版
下一篇:前端工程师必须掌握的知识点