做后台管理程序关于安全性和权限问题是重中之重的,不同的角色对于不同的权限,不同权限对于不同的路由,同时侧边栏也是根据路由配置异步生成。
一、登录
用户进行登录,我们要做的就是对登入角色进行判断(根据具体的业务需要进行角色的划分)。
二、权限验证
当我们拿到当前用户登录的信息之后,我们需要根据信息来处理当前用户有哪一些权限(这一步是关键也是最容易死循环的操作)
1、前期的准备工作
我们需要编写一份映射表,关于这个项目所对应的动态路由映射表和静态路由映射表。比如:
export const asyncMap=[
{
path: "/home",
component: () => import( /* webpackChunkName: "home" */ '@/views/home/Home.vue'),
meta: {
breadcrumbName: '首页',
},
},
];
//当前所需的路由表
export const constantRouterMap =[
{
path: "/",
redirect: '/login'
},
]
那么实例化的时候只需挂载静态的路由表即可:
//实例化vue的时候只挂载constantRouter
export default new Router({
routes: constantRouterMap
});
在vuex定义存放路由已经配置好的路由容器
permitRouter:[], //动态路由容器
router:constantRouterMap, //静态路由信息
使用vuex管理路由表,再根据vuex中可访问的路由渲染侧边栏组件
2、路由前置守卫
通过路由前置守卫对vuex中的路由容器进行赋值操作请求,写好的动态路由表根据用户的角色计算出于其对于的权限的路由,再通过router.addroutes动态挂载路由。
具体实现思路:
前端路由表的动态设置思路:(需要根据不同的登入业务需求做判断,但基本大同小异。)
- 先判断 loginIframeUrl(标识符用来判断是否显示登入页面)是否为false(false代表获取到登入的页面)
- 不为false 则它在登入的页面,让它去登入。
- 为false (可能已经登入过了,也可能登入信息过期了)
- 判断去的路径是否是 / || /login ,
- 是 判断是否已经有用户信息
- 没有去登录
- 有 去/home(首页)
- 不是 判断是否有用户信息
- 有 判断是否已经有路由容器表
- 没有 去获取 (做路由的权限配置)
- 有 直接状态管理取
- 没有 去登录(/login)
- 有 判断是否已经有路由容器表
- 是 判断是否已经有用户信息
- 判断去的路径是否是 / || /login ,
export default{
permitRouter:function(to,from,next){
if(!store.state.loginIframeUrl){ //当loginIframeUrl为false 1.可能已经登入了2.也可能没登入
if(to.path!=='login' && to.path!=='/'){ //看看当前路径是在登录|| / 上
if(Object.keys(store.state.userMessage).length!==0 ){ //已经拥有用户的信息了
if(store.state.permitRouter===null || store.state.permitRouter.length===0){ //如果没有权限的表就去获取再添加
store.dispatch('GetRouteDatas').then(payload=>{ //获取当前角色的路由权限表
store.dispatch('GenerateRoutes',payload).then(res=>{
let arr=constantRouterMap.concat([...res]);
this.options.routes=arr;
this.addRoutes(arr) // 动态添加可访问路由表
next({...to,replace: true }) ;
})
}).catch(err=>{
this.$message('当前无权限');
});
}else{ //有就直接添加从vuex中获取添加
//进入每个路由前判断是否已经添加过路由数组
if(this.options.routes.length<=1){ //没添加则添加。
if(store.state.permitRouter.length!==0){
store.dispatch('GenerateRoutes',store.state.permitRouter).then(res=>{
let arr=constantRouterMap.concat([...res]);
this.options.routes=arr;
this.addRoutes(arr) // 动态添加可访问路由表
next({...to,replace: true }) ;
})
}
}else{
next(); //有添加了直接过
}
}
}else{
//没用户信息
next('/');
}
}else{
//如果地址栏在/或者登录,用户信息为空,则去登入。
if(Object.keys(store.state.userMessage).length===0){
// next('/');
next();
}else{
next('/home')
}
}
}else{
//当loginIframeUrl有值,一定在登入也,则让它通过去登录
next();
}
}
}
代码核心:
let arr=constantRouterMap.concat([...res]);
this.options.routes=arr;
this.addRoutes(arr) //动态添加可访问路由表
next({ ...to, replace: true }) // hack方法确保addRoutes已完成,set the replace:true so the navigation will not leave a history record
注意事项:这里有一个需要非常注意的地方就是 404 页面一定要最后加载,如果放在constantRouterMap一同声明了404,后面的所以页面都会被拦截到404。
3、处理路由权限数据
将获取路由权限的接口放在vuex的actions中进行请求,以及前端编写的动态路由映射表和后端请求回来的数据做比对的逻辑都将放在actions当中。
// 验证是否有权限并且返回
function hasPermission(route,addRouters) {
if(addRouters[route['path']]){
return route;
}
};
export default {
//匹配侧边栏的数据
GenerateRoutes:({commit},payload)=>{
return new Promise(resole=>{
let addRouters=payload.reduce((prev,cur,index,arr)=>{
prev[cur.url]=cur.url;
return prev;
},{});
//根据权限过滤自己设定的路由表
let newaddRouters=asyncMap.filter(item=>{
return hasPermission(item,addRouters);
})
commit('SET_permitRouter', newaddRouters); //设置已经匹配的路由映射
resole(newaddRouters);
})
},
// 获取侧边栏数据
GetRouteDatas:({commit},payload)=>{
return new Promise((resolve,reject)=>{
getUserModules().then(res=>{
if(res.data.code===200){
commit('SET_permitRouter',res.data.data);
resolve(res.data.data) //将后台获取的数据传出去
}else{
reject(false);
}
})
})
}
}
好了,权限配置的问题就基本上完成啦~~
这里你可通过自己定义的路由容器permitRouter来获取当前用户有哪些权限了。在这里我也处理了当浏览器刷新时,存放在vuex的数据失效的问题。我是通过vuex的plugin进行sessionStorage的会话保存。但是还有一个问题就是JSON.stringify()会将函数失效了,那么就获取不到前端的组件了,我通过再次匹配GenerateRoutes函数进行比对。