路由拦截器

每次路由变化时,判断是否登录、是否有权限

注入拦截器

@/router/index.js:

1
2
3
4
5
6
7
import { routerBeforeEachFunc, routerAfterEachFunc } from '@/config/interceptor/router';

// 注入拦截器
routerInstance.beforeEach(routerBeforeEachFunc);
routerInstance.afterEach(routerAfterEachFunc);

export default routerInstance;

@/index.js:

1
2
3
4
5
6
7
import router from '@/router';

window.app = new Vue({
el: '#app',
router,
render: h => h(App)
});

拦截器设置

设置loadingBar,判断是否登录、是否有权限

@/config/interceptor:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import router from '@/router';
import store from '@/store';
import { getToken } from '@/utils/auth'; // getToken from cookie
import { treeToList } from '../../utils';
import iView from 'iview';

//设置加载条的高度
iView.LoadingBar.config({
height: 3
});

function hasPermission(urlList, to) {
if (to.meta.noControl) return true;
return urlList.includes(to.path || to.meta.fullPath);
}

const whiteList = ['/login']; // no redirect whitelist

export function routerBeforeEachFunc(to, from, next) {
iView.LoadingBar.start();
if (to.meta.title) {
document.title = to.meta.title;
}
window.currentRoute = to; //保存当前的路由,功能按钮权限访问
if (getToken()) {
// determine if there has token是否登录 使用本地缓存Cookie
/* has token 已经登录了访问login 直接跳转到 dashboard*/
if (to.path === '/login') {
next({ path: '/' });
iView.LoadingBar.finish(); //hack 技巧 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 => {
// 获取全局的菜单
const routeTreeData = res;
let routeList = treeToList(routeTreeData);
// 对比代码配置的路由对象 与服务器点返回的路由 生成路由表
store.dispatch('GenerateRoutes', routeList).then(() => {
// 根据roles权限生成可访问的路由表
router.addRoutes(store.getters.addRouters); // 动态添加可访问路由表
// 以由当前用角色 改变[左侧渲染菜单]
store.dispatch('ChangeRoles');
// 下次再进入路由时候 直接走else进入到动态添加的路由中
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(() => {
iView.Message.error('获取角色信息失败,请重新登录');
next({ path: '/' });
});
});
} else {
// 没有动态的改变权限则直接进入
// next();
// 改变角色会改变vuex保存的路由,所以这里要判断
if (hasPermission(store.getters.addUrlList, to)) {
next();
} else {
// 进入未授权的页面
next({ path: '/401', replace: true, query: { noGoBack: true } });
}
}
}
} else {
/* has no token*/
// 白名单里面初始化
if (whiteList.indexOf(to.path) !== -1) {
// 在免登录白名单,直接进入
next();
} else {
next(`/login?redirect=${to.path}`); // 否则全部重定向到登录页
iView.LoadingBar.finish(); // if current page is login will not trigger afterEach hook, so manually handle it
}
}
}

export function routerAfterEachFunc(to, from) {
iView.LoadingBar.finish();
}

引用模块

@/utils/auth.js:

1
2
3
4
5
import Cookies from 'js-cookie';
const TokenKey = 'Admin-Token';
export function getToken() {
return Cookies.get(TokenKey);
}

@/utils/index.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export function treeToList(routeList) {
let res = [];
function foo(nodeData) {
let { resourceName, resourceUrl, children, actionVOS } = nodeData;
// 过滤掉 resourceUrl是null的选项
if (resourceUrl) res.push({ resourceName, resourceUrl, actionVOS });
// children 挂载的是子节点
if (!isEmpty(children)) {
children.forEach(item => {
foo(item);
});
}
}
routeList.forEach(item => {
foo(item);
});

return res;
}

@/store/user.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
const user = {
// actions中可以使用 async函数
actions: {
// 获取用户信息 角色及对应的路由表 在路由的钩子中调用
async GetUserInfo({ commit, state, dispatch }) {
await api['user/getRolePermission']().then(data => {
if (isEmpty(data)) {
throw new Error('getRolePermission: getRolePermission must be a non-null array !');
} else {
// 注意这里的角色data 是数组 用户对应多个角色
commit('SET_ROLES', data);
let roleId = sessionStorage.getItem('currentRoleId');
if (roleId) {
let roleObj = data.find(item => {
return item.id == roleId;
});
if (roleObj) {
commit('SET_CURRENT_ROLE', roleObj);
} else {
throw new Error('用户的同名的角色id值前后不一致');
}
} else {
// 默认使用第一个权限 作为默认权限
commit('SET_CURRENT_ROLE', data[0]);
}
}
});
// 获取全局菜单
let allRouteList = await api['user/globalMenuResource']().then(data => {
if (isEmpty(data)) {
throw new Error('globalMenuResource: globalMenuResource must be a non-null array !');
} else {
return data;
}
});
commit('SET_GLOBAL_MENU', allRouteList); //设置全局菜单
return allRouteList;
},
// 前端 登出
FedLogOut({ commit }) {
return new Promise(resolve => {
commit('SET_TOKEN', '');
removeToken();
sessionStorage.removeItem('currentRoleId');
resolve();
router.push({ name: 'login' });
});
},
//动态的改变 用户权限 修改左侧的菜单
ChangeRoles({ state, commit, dispatch }, role) {
let routeList;
if (role) {
commit('SET_CURRENT_ROLE', role);
routeList = role.resourceVOS; //该角色能够访问的菜单
} else {
routeList = state.currentRole.resourceVOS;
}
let urlList = treeToList(routeList);
dispatch('GenerateRoutes', urlList);
},
GenerateRoutes({ state, commit }, routeList) {
return new Promise(resolve => {
let urlList = [];
routeList.forEach(item => {
urlList.push(item.resourceUrl);
});
let accessedRouters = filterAsyncRouter(asyncRouterMap, urlList);
// 动态的控制左侧菜单的 显示隐藏
commit('SET_ROUTERS', accessedRouters);
commit('SET_ADD_URLLIST', urlList);
resolve();
});
}
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers;
state.routers = constantRouterMap.concat(routers);
},
SET_ADD_URLLIST: (state, urlList) => {
state.addUrlList = urlList;
},
SET_CODE: (state, code) => {
state.code = code;
},
SET_TOKEN: (state, token) => {
state.token = token;
},
SET_INTRODUCTION: (state, introduction) => {
state.introduction = introduction;
},
SET_STATUS: (state, status) => {
state.status = status;
},
SET_USER_NAME: (state, name) => {
state.userName = name;
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar;
},
SET_ROLES: (state, roles) => {
state.roles = roles;
},
SET_CURRENT_ROLE: (state, role) => {
state.currentRole = role; // 设置当前系统使用角色
sessionStorage.setItem('currentRoleId', role.id); // 角色的id 保存在当前会话
}
},
}