权限部分详解
本文章不讲具体实现方式,只说实现思路,如需具体实现形式可查阅别的文章
- 权限管理⼀般需求是⻚⾯权限和按钮权限的管理
- 具体实现的时候分后端和前端两种⽅案
目前权限区分可分为前端和后端来处理:
后端处理:把所有⻚⾯路由信息存在数据库中,⽤户登录的时候根据其⻆⾊查询得到其能访问的所有⻚⾯路由信息返回给前端,前端再通过 addRoutes 动态添加路由信息。
前端处理:
统一将固定路由放在constantRoutes,固定通过如下处理
const createRouter = () =>
new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes
})
const router = createRouter()
登录后,通过后台数据比对 asyncRoutes 获取前端应该展示的路由数据 accessRoutes
最后通过如下代码,增加到路由中。
router.addRoutes(accessRoutes)
其中有三种处理方案:
-
① 后台返回的角色信息,如[‘admin’,‘editor’]。
② 路由meta中如配置
{
path: 'rights',
name: 'rights',
component: () => import('@/views/rights/index.vue'),
meta: {
title: 'Page Reports',
roles: ['admin'] // or you can only set roles in sub nav
}
},
③ 根据后台返回的角色信息,进行路由信息比对,获得最终应动态addRoutes的路由accessRoutes。
-
① 后台返回当前登录人携带的所有auth信息
② 路由meta中如配置
export default {
meta: {
// auth: '智慧航显',
auth: '宣传通知',
icon: 'el-icon-setting',
type: 'menu',
iconType: 'class',
sort: 3
},
path: '/broadcastNotice',
// 外层不需要其他处理,只是区分父子关系,采用这种写法。
component: { render: e => e('router-view') },
children: [
{
path: '/broadcastNotice/broadcastNotice',
name: 'BroadcastNotice',
component: () =>
import(
/* webpackChunkName: "UserRoleManagement" */ '@/views/BroadcastNotice/NoticeList/NoticeList.vue'
),
meta: {
breadcrumb: '通知列表',
// menuIndex: '/flightDisplay/StaticModel',
auth: '通知列表'
}
}
]
};
③ 根据后台返回的auth信息,进行路由meta中name信息比对,获得最终应动态addRoutes的路由accessRoutes。
[
{
"id": "02a3b989-0b7a-4011-b4d2-1a842f6a83cf",
"name": "查询服务管理",
"description": null,
"type": "menu,page",
"parentId": null,
"enabled": true,
"apiInfos": [],
"children": []
},
{
"id": "06c02de0-f111-4a39-a83b-23644993758d",
"name": "综合演练",
"description": null,
"type": "menu,page",
"parentId": null,
"enabled": true,
"apiInfos": [],
"children": []
},
]
-
① 后台直接返回路由信息
② 直接通过addRoutes添加路由信息即可。
整体流程梳理:
-
login.vue页面登录调用sotre中的登录dispatch,主要用于
① 设置token
② 异步存储一些vuex的信息
③ 完事进行路由跳转
④ 触发permission.js,根据后台返回的权限信息,生成路由
login({ commit }, userInfo) { return new Promise((resolve, reject) => { loginApi(userInfo) .then((res) => { commit('setToken', res.token) setToken(res.token) setTokenTime() router.replace('/') resolve() }) .catch((err) => { reject(err) }) }) },
-
进入permission.js
① app/getInfo 获取权限信息 [‘admin’] [‘developer’,‘editor’]
② 按照权限信息生成路由 generateRoutes
③ addRoutes增加路由信息
④ 作跳转处理
// permission.js // get user info // note: roles must be a object array! such as: ['admin'] or ,['developer','editor'] // 获取权限信息 const { roles } = await store.dispatch('app/getInfo') // 生成动态路由 // generate accessible routes map based on roles const accessRoutes = await store.dispatch( 'permission/generateRoutes', roles ) // addRoutes增加路由信息 // dynamically add accessible routes router.addRoutes(accessRoutes) // 作跳转处理 // hack method to ensure that addRoutes is complete // set the replace: true, so the navigation will not leave a history record next({ ...to, replace: true }) // sotre/permission.js generateRoutes({ commit }, roles) { return new Promise((resolve) => { let accessedRoutes if (roles.includes('admin')) { accessedRoutes = asyncRoutes || [] } else { accessedRoutes = filterAsyncRoutes(asyncRoutes, roles) } commit('SET_ROUTES', accessedRoutes) resolve(accessedRoutes) }) }
commit('SET_ROUTES', accessedRoutes) 一旦触发,会调整menulist,渲染左侧的列表。
-
其他编码信息:
// get user info
getInfo({ commit, state }) {
return new Promise((resolve, reject) => {
const roles = ['editor']
commit('SET_ROLES', roles)
resolve({ roles })
})
},
resetToken({ commit }) {
return new Promise((resolve) => {
commit('setToken', '')
localStorage.clear()
removeToken()
router.replace('/login')
resolve()
})
},
logout({ commit }) {
commit('setToken', '')
localStorage.clear()
removeToken()
router.replace('/login')
}
/**
* 过滤数组 将树状结构转换为平铺数组
* @param data 树形结构的数据
*/
export const treeToArray = (data) => {
const arr = []
const expanded = (datas) => {
if (datas && datas.length > 0) {
datas.forEach((item) => {
arr.push(item)
if (item.children) {
expanded(item.children)
// eslint-disable-next-line no-param-reassign
delete item.children
}
})
}
}
expanded(data)
return arr
}
/**
* 过滤数组 去掉不需要的部分
* @param data 树形结构的数据
*/
export const deleteArray = (data, referList) => {
const len = data.length
for (let i = 0; i < len; i++) {
if (data[i]) {
if (!referList.includes(data[i].path)) {
delete data[i]
i -= 1
continue
}
if (data[i].children && data[i].children.length) {
deleteArray(data[i].children, referList)
}
}
}
return data.filter(Boolean)
}