Vue如何进行权限管理原创
原创参考文档
面试官:Vue我应该做些什么来管理权限?我应该做什么来控制按钮级的权限?
权限管理分类
前端权限最终是发起请求的权利,通常有两种方式来触发它。
- 页面加载触发器(页面刷新、页面跳转)
- 用户事件触发器(点击按钮等)
在前端实现许可的目的:
- 在路由方面,用户登录时只能看到自己有权访问的导航菜单,只能访问自己有权访问的路由地址。
- 在视图方面,用户只能看到他们有权访问的资源和他们有权访问的控件。
- 在接口方面,如果路由、查看等配置错误,忘记添加权限,则可以在发起请求时拦截越权请求。
因此,前端权限管理可以分为四类:
- 接口权限
- 路由权限
- 菜单权限
- 查看权限(按钮权限)
接口权限
Vue在工程中广泛使用 axios
前后端互动,配合后端回击。 jwt
对界面进行权限控制
用户登录后即可获取。 token
,在 axios
将添加请求拦截器。 token
,若是 token
如果无效或已过期,将进行相应处理。
axios.interceptors.request.use(config => {
config.headers[token] = cookie.get(token)
return config
})
路由权限
方案一
在初始化时,挂载所有路由,并且挂载路由中的元信息。 (meta
标记相应的权限信息,并设置全局路由守卫,在每次跳路前进行检查。
const routerMap = [
{
path: /permission,
component: Layout,
redirect: /permission/index,
alwaysShow: true, // will always show the root menu
meta: {
title: permission,
icon: lock,
roles: [admin, editor] // you can set roles in root nav
},
children: [{
path: page,
component: () => import(@/views/permission/page),
name: pagePermission,
meta: {
title: pagePermission,
roles: [admin] // or you can only set roles in sub nav
}
}, {
path: directive,
component: () => import(@/views/permission/directive),
name: directivePermission,
meta: {
title: directivePermission
// if do not set roles, means: this page does not require permission
}
}]
}]
这种方法有四个缺点:
- 加载所有路线。如果存在多个路由,而用户没有访问所有路由的权限,则会影响性能。
- 在全局路由守卫中,每一次跳路都要根据权限进行判断。
- 菜单信息写在前端。要更改显示文本或权限信息,需要重新编译它。
- 菜单与路线相结合。在定义路线时,添加了菜单显示标题和图标等信息。此外,路线不一定显示为菜单,并且添加了更多字段来标识它。
方案二
初始化时,挂载不需要权限控制的路由,如登录页、404例如错误页。如果用户通过URL强制访问将直接进入。404,相当于从源头上控制。
登录后,获取用户的权限信息,然后过滤有权限访问的路由,并在全局路由守卫中调用。 addRoutes
添加路由
import router from ./router
import store from ./store
import { Message } from element-ui
import NProgress from nprogress // progress bar
import nprogress/nprogress.css// progress bar style
import { getToken } from @/utils/auth // getToken from cookie
NProgress.configure({ showSpinner: false })// NProgress Configuration
// permission judge function
function hasPermission(roles, permissionRoles) {
if (roles.indexOf(admin) >= 0) return true // admin permission passed directly
if (!permissionRoles) return true
return roles.some(role => permissionRoles.indexOf(role) >= 0)
}
const whiteList = [/login, /authredirect]// no redirect whitelist
router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar
if (getToken()) { // determine if there has token
/* has token*/
if (to.path === /login) {
next({ path: / })
NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it
} else {
if (store.getters.roles.length === 0) { // 确定当前用户是否已将其取出user_info信息
store.dispatch(GetUserInfo).then(res => { // 拉取user_info
const roles = res.data.roles // note: roles must be a array! such as: [editor,develop]
store.dispatch(GenerateRoutes, { roles }).then(() => { // 根据roles生成可访问的路由表的权限
router.addRoutes(store.getters.addRouters) // 动态添加可访问的路由表
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
})
}).catch((err) => {
store.dispatch(FedLogOut).then(() => {
Message.error(err || Verification failed, please login again)
next({ path: / })
})
})
} else {
// 不需要直接动态更改权限。next() 删除下面的权限判断 ↓
if (hasPermission(store.getters.roles, to.meta.roles)) {
next()//
} else {
next({ path: /401, replace: true, query: { noGoBack: true }})
}
// 可删 ↑
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) { // 在未登录白名单中,直接输入。
next()
} else {
next(/login) // 否则,所有内容都将重定向到登录页面。
NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
}
}
})
router.afterEach(() => {
NProgress.done() // finish progress bar
})
按需挂载、路由需要知道用户的路由权限,即用户登录时需要知道当前用户拥有哪些路由权限。
这种方法也有以下缺点:
- 在全局路径守卫中,应该对每一次路径跳跃进行判断。
- 菜单信息写在前端。要更改显示文本或权限信息,需要重新编译它。
- 菜单与路线相结合。在定义路线时,添加了菜单显示标题和图标等信息。此外,路线不一定显示为菜单,并且添加了更多字段来标识它。
菜单权限
菜单权限可以理解为将页面与路径分离。
方案一
前端定义路线,后端返回菜单。
前端定义路由信息
{
name: "login",
path: "/login",
component: () => import("@/pages/Login.vue")
}
name
不能为空,需要根据该字段关联后端退货菜单。后端返回菜单信息必须具有 name
对应的字段,并进行唯一性检查。
全局路由守卫
function hasPermission(router, accessMenu) {
if (whiteList.indexOf(router.path) !== -1) {
return true;
}
let menu = Util.getMenuByName(router.name, accessMenu);
if (menu.name) {
return true;
}
return false;
}
Router.beforeEach(async (to, from, next) => {
if (getToken()) {
let userInfo = store.state.user.userInfo;
if (!userInfo.name) {
try {
await store.dispatch("GetUserInfo")
await store.dispatch(updateAccessMenu)
if (to.path === /login) {
next({ name: home_index })
} else {
//Util.toDefaultPage([...routers], to.name, router, next);
next({ ...to, replace: true })//菜单权限更新完成,重新输入当前路线
}
}
catch (e) {
if (whiteList.indexOf(to.path) !== -1) { // 在未登录白名单中,直接输入。
next()
} else {
next(/login)
}
}
} else {
if (to.path === /login) {
next({ name: home_index })
} else {
if (hasPermission(to, store.getters.accessMenu)) {
Util.toDefaultPage(store.getters.accessMenu,to, routes, next);
} else {
next({ path: /403,replace:true })
}
}
}
} else {
if (whiteList.indexOf(to.path) !== -1) { // 在未登录白名单中,直接输入。
next()
} else {
next(/login)
}
}
let menu = Util.getMenuByName(to.name, store.getters.accessMenu);
Util.title(menu.title);
});
Router.afterEach((to) => {
window.scrollTo(0, 0);
});
每次路线跳跃都要判断权限,这里的判断也很简单,因为菜单 name
与路由的 name
是一一对应,后台返回的菜单已经按照权限进行了过滤。
如果按照路线的话 name
如果找不到对应的菜单,则表示用户没有访问权限。
如果有许多路由,则在应用程序初始化期间只能挂载不需要权限控制的路由。获取后台返回的菜单后,根据该菜单与路径的对应关系,过滤出可到达的路径并通过 addRoutes
动态挂载
方案二
路线和菜单都是后端返回的。
前端统一定义组件
const Home = () => import("../pages/Home.vue");
const UserInfo = () => import("../pages/UserInfo.vue");
export default {
home: Home,
userInfo: UserInfo
};
后端返回以下形式的路由组件
[
{
name: "home",
path: "/",
component: "home"
},
{
name: "home",
path: "/userinfo",
component: "userInfo"
}
]
在后端返回路线中通过。addRoutes在动态装载之间,需要处理数据,并将component该字段将替换为实际组件。
这一方案要求前端和后端之间高度协调。
视图权限
方案一
通过 v-if
控制按钮的显示或隐藏。
方案二
通过自定义说明确定按钮权限
首先配置路由
{
path: /permission,
component: Layout,
name: 权限测试,
meta: {
btnPermissions: [admin, supper, normal]
},
//页面所需的权限
children: [{
path: supper,
component: _import(system/supper),
name: 权限测试页,
meta: {
btnPermissions: [admin, supper]
} //页面所需的权限
},
{
path: normal,
component: _import(system/normal),
name: 权限测试页,
meta: {
btnPermissions: [admin]
} //页面所需的权限
}]
}
自定义权限身份验证说明
import Vue from vue
/**权限指令**/
const has = Vue.directive(has, {
bind: function (el, binding, vnode) {
// 获取页面按钮权限
let btnPermissionsArr = [];
if(binding.value){
// 如果指令通过,则获取指令参数,并与当前登录按钮权限进行比较。
btnPermissionsArr = Array.of(binding.value);
}else{
// 否则,根据路径获取路径中的参数。btnPermissionsArr与当前登录按钮权限进行比较。
btnPermissionsArr = vnode.context.$route.meta.btnPermissions;
}
if (!Vue.prototype.$_has(btnPermissionsArr)) {
el.parentNode.removeChild(el);
}
}
});
// 一种权限检查方法
Vue.prototype.$_has = function (value) {
let isExist = false;
// 获取用户按钮权限
let btnPermissionsStr = sessionStorage.getItem("btnPermissions");
if (btnPermissionsStr == undefined || btnPermissionsStr == null) {
return false;
}
if (value.indexOf(btnPermissionsStr) > -1) {
isExist = true;
}
return isExist;
};
export {has}
在使用的按钮中只需要引用。v-has指令
编辑
版权声明
所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除