权限控制
在项目中,尤其是在后台管理系统中,不同人员登陆,看到的页面菜单是不一样的,比如,一个公司的办公系统,超级管理员登陆可以看到所有的页面,而普通员工账号登录可能无法看到人员管理等页面,比如公司的员工个人资料页面只有人力资源部门有权利看,其他部门的员工是不允许查看公司员工信息数据的。当然了除了页面的权限,还会有一些按钮级别的权限,比如一个下载按钮,有的帐号可以用,有的人不能用,比如人员账号管理中,一个页面中有一个确认添加、删除该账号人员按钮,这个按钮只有管理员有权利点击,其他人员登陆是无法点击的。
页面级权限控制
当前系统方案为:前端路由信息完全写死,并且动态生成页面菜单。自己本身的router.js文件定义好页面所有的路由。这种方式弊端很明显,并不能实现真正的权限控制,因为如果用户记住了某个理由,用户不点击菜单,直接在地址栏里输入地址,那么页面还是可以显示出来。
所有方案前提:登录成功后后台返回当前登录用户权限code(或者name、role,名称沟通后自定义),前端存储在vuex中。
方案一:
前端只在路由跳转时做权限判断。
方案一改动当前代码量最小,无需后端改动,前端路由和侧边栏显示不变,在对需要权限区分的路由在跳转时候添加前置路由守卫router.beforeEach,进行当前权限判断,不满足权限的跳转到相应页面(自定义401、404页面等),但该方案不能实现真正的权限控制。
router.beforeEach((to, from, next) => {
if (store.getters.roles.length === 0) { // 判断是否保存有权限信息
const roles = store.getters.roles;
if (roles !== 'admin') { // 不为管理员
if (to.path === '/faultInfo') {
next({ path: '/401' })
} else {
next()
}
} else {
next()
}
} else {
next('/login');
}
});
方案二:
后端返回路由权限名,前端存储完整路由权限表,并动态生成路由。
建议看大神的文档,已经写的很详细了:手摸手,带你用vue撸后台 系列二(登录权限篇) - 掘金
以下是自己实现思路:
建议登录login和返回用户路由信息get_user_info两件事分开请求比较好。
步骤:
· 登录:当用户填写完账号和密码后向服务端验证是否正确,验证通过之后,服务端会返回一个token,拿到token之后(将这个token存贮到cookie中,保证刷新页面后能记住用户登录状态),前端会根据token再去拉取一个 user_info 的接口来获取用户的详细信息(如用户权限,用户名等等信息)。
· 权限验证:通过token获取用户对应的 role,动态根据用户的 role 算出其对应有权限的路由,通过 router.addRoutes 动态挂载这些路由。
数据和操作都可存储至vuex全局管理。(刷新页面后 vuex的内容也会丢失,所以需要重复get_user_info的操作)
权限控思路:前端会有一份路由表,它表示了每一个路由可访问的权限。当用户登录之后,通过 token 获取用户的 role ,动态根据用户的 role 算出其对应有权限的路由,再通过router.addRoutes动态挂载路由。
- 创建vue实例的时候将vue-router挂载,但这个时候vue-router挂载一些登录或者不用权限的公用的页面。
- 当用户登录后,获取用role,将role和路由表每个页面的需要的权限作比较,生成最终用户可访问的路由表。
- 调用router.addRoutes(store.getters.addRouters)添加用户可访问的路由。
- 使用vuex管理路由表,根据vuex中可访问的路由渲染侧边栏组件。
方案二开始,路由分为两种:constantRoutes 和 asyncRoutes。
constantRoutes: 代表那些不需要动态判断权限的路由,如登录页、404、等通用页面。
asyncRoutes: 代表那些需求动态判断权限并通过 addRoutes 动态添加的页面。
//固定路由
const constantRoutes = [
{
path: '/',
redirect: '/login',
},
{
path: '/login',
name: '登录页面',
component: ()=>import("@/views/login.vue")
},
{
path: '/404',
name: '404页面',
component: ()=>import("@/views/404.vue")
},
{
path: '/401',
name: '401页面',
component: ()=>import("@/views/401.vue")
},
]
需要权限路由:字段roles,表示当前路由所需要的权限。
// 需要权限路由
export const asyncRoutes = [
{
path: '/permission',
name: 'permissionhome',
meta: {
icon: 'el-icon-setting',
roles: ['admin','superadmin']
},
component: ()=>import("@/views/permission.vue")
},
{
path: '/detail',
name: 'detail',
meta: {
icon: 'el-icon-setting',
roles: ['superadmin']
},
component: ()=>import("@/views/detail.vue")
},
使用addRoutes方法动态添加路由并进行权限判断:
// main.js
const whiteList = ['/login'] // 免登陆页面
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() //当有用户权限的时候,说明所有可访问路由已生成 如访问没权限的全面会自动进入404页面
}
}
} else {
if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
next();
} else {
next('/login'); // 否则全部重定向到登录页
}
}
});
方案三:
登陆成功之后直接由后端返回异步路由表,然后前端直接通过addRoutes方法添加。前端添加用户页面权限配置页面。
实现方式应该跟方案二差不多,具体实现还需跟后端人员沟通讨论。
实现步骤:前端权限控制(二):VUE-router.addRoutes根据后台接口传递数据生成动态路由,前端拿到配置并生成侧边栏-实现页面级权限控制
按钮级权限控制
方案一:
在获取到用户的role之后,在前端用v-if手动判断来区分不同权限对应的按钮的。或者将它封装成一个自定义指令使用。
方案二:
在页面级权限控制方案三基础上来操作,后端返回异步路由表中添加页面按钮权限字段,前端根据返回表来显示操作按钮。
具体实现:前端权限控制(三):根据后台接口数据传递页面按钮权限-实现按钮级权限控制
方案三:
不同权限的用户显示不同的侧边栏和限制其所能进入的页面,后端验证每一个涉及请求的操作,验证其是否有该操作的权限,每一个后台的请求 get 或者 post 前端都在请求 header里面携带用户的 token,后端根据该 token 来验证用户是否有权限执行该操作。若没有权限则抛出一个对应的状态码,前端检测到该状态码,做出相对应的操作。