vue 权限管理
核心想法:
1.登陆后获得用户角色,通过角色获得用户的权限,注入权限对应的路由。
2.刷新页面,从localStorage用角色(更好的方式是通过token)再次获得所属权限,再次注入路由。在管理界面左端循环权限对应的路由菜单。
3.localStorage存用户的信息(token),权限路由不会存。
所有的路由分为2种:
- 公共路由:所有用户可以查看。
- 权限路由:当前用户权限所属的路由。
实现控制的方式分两种:
- 通过 vue-router addRoutes 方法注入路由实现控制
- 通过 vue-router beforeEach 钩子限制路由跳转
一般来讲,需要采用第一种方式,addRoutes注入的方式,第二种每次判断,开销比较大,而且一次性拿不到角色对应的所有路由列表。这个列表需要再登陆后立即渲染在左边。
最终的效果:
权限的控制分为好几种:
- 一级菜单的权限控制,
- 二级菜单的权限控制,
- 按钮级别的权限控制。
前后端约定控制方式有2种:
- 后端返回路由控制列表
- 前后端约定路由控制列表
以下代码展示一级菜单的权限控制,并且路由不是后端返回,而是通过前后端约定的方式,确定角色能访问的路由。
routers/index.js
import Vue from 'vue' import Router from 'vue-router' import loginRoute from './modules/login.js'; import homeRoute from './modules/home.js' import productionRoute from './modules/production.js'; import store from '../stores/index.js'; Vue.use(Router) //不需要权限的路由 export const constantRoutes = [loginRoute]; //需要权限的路由 //这里只是导出。实际上添加到router里在在store的permission里 export const asyncRoutes = [ homeRoute, productionRoute, ]; console.log(asyncRoutes); const router = new Router({ mode: 'history', base: process.env.BASE_URL, routes: [ ...constantRoutes, ] }) router.beforeEach((to,from,next)=>{ let user = store.state.user.user; let permissionRoutes= store.state.permission.permissionRoutes; console.log(store.state); console.log(user); if(!user && to.path!=='/'){ next({ path:'/' }); }else{ //刷新页面的情况 if(!permissionRoutes && user){ store.dispatch('generateRoutes',{ role:user.role }) console.log(router); console.log(to); //这里写next没用 next({...to}) }else{ next(); } // next({...to, replace: true }) } }); export default router;
其中,productionRoute:
import Layout from '../../layouts/Index.vue' const productionAdd = () => import('../../pages/production/Add.vue') const productionList = () => import('../../pages/production/List.vue') const productionTemplateComplete = () => import('../../pages/production/TemplateComplete.vue') let routes = { path: '/production', component: Layout, meta: { title: '产品管理', roles: [0, 1] }, children: [ { path: 'add', component: productionAdd, beforeEnter (to, from, next) { console.log('router beforeEnter'); next(); }, meta: { title: '产品新增' } }, { path: 'list', component: productionList, meta: { title: '产品列表' } }, { path: 'templateComplete', component: productionTemplateComplete, meta: { title: '产品新增完善' } } ] } export default routes
stores/index.js
import Vue from 'vue' import Vuex from 'vuex' import user from './modules/user.js' import permission from './modules/permission.js' Vue.use(Vuex) export default new Vuex.Store({ modules: { user, permission, } })
stores/modules/user.js
const SET_USER = 'SET_USER' //登录成功 const CLEAR_USER = 'CLEAR_USER' //退出登录 import $ajax from '../../axios/index.js'; import store from '../index.js'; export default { state: { user:JSON.parse(localStorage.getItem('user')) || null }, mutations: { [SET_USER](state, user) { state.user = user; localStorage.setItem('user', JSON.stringify(user)) }, [CLEAR_USER](state) { state.user = null; localStorage.removeItem('user'); } }, actions: { login({commit}, param) { console.log('action'); console.log(param); return new Promise((resolve, reject) => { //一期不做后台登陆,前端判断 // $ajax({ // url:api.login, // data:{ // ...param // } // }).then(res=>{ // let user = res.data.data; // commit(SET_USER, user); // resolve(); // }).catch(res=>{ // reject(); // }) //前端验证用户名,密码 window.setTimeout(()=>{ if(param.username === 'huainanju' && param.password === 'huainanju123'){ let user = { username:'管理员', role:0 } commit(SET_USER, user); store.dispatch('generateRoutes',{ role:user.role }) resolve(); }else{ reject(); } },1000) }); }, logout({commit}) { commit(CLEAR_USER) } } }
stores/modules/permisson.js
import {asyncRoutes} from '../../routers/index'; import router from '../../routers/index'; /** * 递归过滤异步路由表,返回符合用户角色权限的路由表 * @param role */ function filterAsyncRoutes(role) { const res = asyncRoutes.filter(route => { return route.meta.roles.includes(role) }); return res } const permisssion = { state:{ permissionRoutes:null }, getter:{ }, mutations:{ SET_ROUTERS: (state, routes) => { //添加路由 console.log('添加路由'); router.addRoutes(routes); state.permissionRoutes = routes // 权限路由 }, }, actions:{ generateRoutes({ commit }, data) { let {role} = data; let routes = filterAsyncRoutes(role); console.log(routes); commit('SET_ROUTERS', routes) } } } export default permisssion;
layouts/Left.vue
<template> <el-aside style="min-height:100%;min-width:250px;" width="250px"> <el-menu :default-active="defaultActive" background-color="#324157" text-color="#bfcbd9" active-text-color="#409EFF" style="position:fixed;top:0;left:0;min-height: 100%;width:250px;z-index:100" router> <template v-for="(item,index) in permissionRoutes"> <!--一级菜单--> <el-menu-item :index="item.path" v-if="item.children.length === 1 "> <i class="el-icon-menu"></i> <!-- <div>我是</div>--> <span>{{item.meta.title}}</span> </el-menu-item> <!--多级菜单--> <el-submenu :index="index+''" v-else> <template slot="title"> <i class="el-icon-menu"></i> <span>{{item.meta.title}}</span> </template> <template v-for="child in item.children"> <el-menu-item :index="item.path+'/'+child.path">{{child.meta.title}}</el-menu-item> </template> </el-submenu> </template> </el-menu> </el-aside> </template> <script> import {mapState} from 'vuex' export default { data() { return { } }, computed: { ...mapState({ 'permissionRoutes':state=>state.permission.permissionRoutes }), defaultActive: function () { if (this.$route.path.indexOf('/classInfo') >= 0) { return 'classList' } return this.$route.path.replace('/org/', ''); } }, created() { window.setTimeout(()=>{ console.log(this.permissionRoutes); },1000) } } </script>