在h5和后台开发中,我们经常需要权限登录。所以需要总结日常经常需要的几种方式。
1、路由方面
一般由vue-router和vuex完成路由体系,h5一般通过router.beforeEach()进行拦截,后台一般通过addRoutes添加路由实现菜单控制。
1.1router.beforeEach() 判断mate标识页面是否需要登录和store用户登录的标识。
//路由配置meta信息
const routes = [
{
path: '/',
name: 'Home',
component: (resolve) => require(['@/views/Home.vue'], resolve),
},
{
path: '/login',
component: (resolve) => require(['@/views/Login.vue'], resolve),
name: 'Login',
meta: {
redirectAlreadyLogin: true //登录后,不需要再进来
}
},
{
path: '/profile',
component: (resolve) => require(['@/views/Profile.vue'], resolve),
name: 'Profile',
meta: {
requiredLogin: true //需要登录
}
}
]
// 路由钩子进行拦截判断是否需要登录
router.beforeEach((to, from, next) => {
if (to.meta.requiredLogin && !store.state.use.isLogin) {
next({ name: 'Login'})
} else if (to.meta.redirectAlreadyLogin && store.state.user.isLogin) {
next('/');
} else {
next();
}
})
// store配置用户信息是否登录
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
user: {
name: '张三',
isLogin: false
}
}
})
export default store
1.2动态路由
简单的角色配置:只涉及管理员和普通用户的权限。通常在前端进行角色判断
复杂的路由权限:后端返回路由列表,前端渲染使用
通常不同的用户对应不同的菜单,根据用户的角色后台返回不同的路由列表,进行添加。
每一个页面动态配置权限,之后将这份路由表存储到后端。当用户登录后得到 roles
,前端根据roles
去向后端请求可访问的路由表,从而动态生成可访问页面,之后就是 router.addRoutes 动态挂载到 router 上。
第一步:将公用的页面constantRouterMap放在vue-router上。
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const constantRouterMap = [
{
path: '/',
name: 'Home',
component: (resolve) => require(['@/views/Home.vue'], resolve),
},
{
path: '/login',
component: (resolve) => require(['@/views/Login.vue'], resolve),
name: 'Login',
},
{
path: '/profile',
component: (resolve) => require(['@/views/Profile.vue'], resolve),
name: 'Profile',
},
]
export default new Router({
mode: 'history',
constantRouterMap
})
//动态需要根据权限加载的路由表
export const asyncRouterMap = [
{
path: '/permission',
component: Layout,
name: '权限测试',
meta: { role: ['admin','super_editor'] }, //页面需要的权限
children: [
{
path: 'index',
component: Permission,
name: '权限测试页',
meta: { role: ['admin','super_editor'] } //页面需要的权限
}]
},
{ path: '*', redirect: '/404', hidden: true }
];
第二步:当用户登录后,获取用户role,将role和路由表权限做比较,生成用户的访问的用户表。
router.beforeEach((to, from, next) => {
if (store.getters.token) { // 判断是否有token
if (to.path === '/login') {
next({ path: '/' });
} else {
if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息
store.dispatch('GetInfo').then(res => { // 拉取info
const roles = res.data.role;
store.dispatch('GenerateRoutes', { roles }).then(() => { // 生成可访问的路由表
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
})
}).catch(err => {
console.log(err);
});
} else {
next()
}
}
} else {
next('/login');
}
});
第三步:实现匹配路由
// store/permission.js
import { asyncRouterMap, constantRouterMap } from 'src/router';
function hasPermission(roles, route) {
if (route.meta && route.meta.role) {
return roles.some(role => route.meta.role.indexOf(role) >= 0)
} else {
return true
}
}
const permission = {
state: {
routers: constantRouterMap,
addRouters: []
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers;
state.routers = constantRouterMap.concat(routers);
}
},
actions: {
GenerateRoutes({ commit }, data) {
return new Promise(resolve => {
const { roles } = data;
const accessedRouters = asyncRouterMap.filter(v => {
if (roles.indexOf('admin') >= 0) return true;
if (hasPermission(roles, v)) {
if (v.children && v.children.length > 0) {
v.children = v.children.filter(child => {
if (hasPermission(roles, child)) {
return child
}
return false;
});
return v
} else {
return v
}
}
return false;
});
commit('SET_ROUTERS', accessedRouters);
resolve();
})
}
}
};
export default permission;
2、指令方面
import store form '@/store'
export default {
inserted(el, binding, vnode) {
const { value } = binding //获取v-auth=['admin']中的admin这个值
const roles = store.getters && store.getters.roles
if (value && value instanceof Array && value.length > 0 ) {
const permissionRoles = value
const hasPremission = roles.some(role =>{
return permissionRoles.includes(role)
})
// 如果没有权限,移除当前节点
if (!hasPremission) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error('need roles!')
}
}
}
3、接口方面
一般我们用axios的拦截器(service.interceptors.request.use)header添加token,后台进行验证。根据后台返回的code做不同的判断。
// 响应拦截 对响应消息作初步的处理
service.interceptors.response.use(resp => {
if (resp.data) {
if (resp.data.errcode !== 0 && resp.data.errcode !== 401) {
Notify({ type: 'danger', message: resp.data.errmsg })
}
return resp.data
} else {
Notify({ type: 'danger', message: resp.errmsg })
return resp
}
}, error => {
if (error.response) {
Notify({ type: 'danger', message: '抱歉,系统发生错误!' })
}
})
后台权限验证参考花裤衩大佬,地址:https://juejin.cn/post/6844903478880370701