权限管理在前端属于绕不开的一个话题,尤其是后台管理系统类的业务,需要明确权限。最近业务上也遇到一些权限相关问题,经过查阅一些资料,记录下前端权限管理的常见场景以及方案。
由于目前主要常见的权限问题集中在SPA单页应用中,因此这里只讨论SPA应用中的关于权限的校验。
说前端权限之前,先说下后端权限。不管前端权限设计怎么完美,后端的权限校验都不能省,权限问题,安全问题在前端是没办法做到真正有用的,因此,后端校验必不可少,切记,切记。
后端权限校验:
后端权限大致思路是,用户登录之后获取一个token,每次请求时携带该token,后端根据token是否有效决定是否有资源访问权限。
伪代码如下:
axios.interceptors.request.use(config => {
config.headers['token'] = cookie.get('token')
return config
})
axios.interceptors.response.use(res=>{},{response}=>{
if (response.data.code === 40099 || response.data.code === 40098) { //token过期或者错误
router.push('/login')
}
})
前端权限校验:
接着到重点前端权限
前端权限按照使用场景分为:按钮权限 路由权限 菜单权限
按钮权限
按钮权限相对来说比较简单,一般根据用户的角色信息(后端控制,一般通过接口返回)直接控制按钮是否渲染,或者控制按钮是否可点击即可。
伪代码:
提交信息
提交信息
小技巧:可以将hasRole直接存在vuex中调用起来比较方便。
路由权限
路由权限根据场景需要也分为几种看以下场景。
场景一:路由可访问。
场景二:路由不可访问。
针对场景一,路由可访问就是注册好所有路由,用户登录后,访问路由的时候在路由拦截器 router.beforeEach()
里面做判断是否可访问,如果可以就继续访问,如果不可以,那就直接拦截不让其访问。
伪代码:
const {routeAuthList} = login() // 一般后端给出可访问路由接口
cosnt whiteList = ['/login','/','/404'] // 不需要权限的即可访问的路由
// 合并总路由
whiteList = whiteList.concat(routeAuthList)
//路由守卫判断
router.beforeEach((to, from, next) => {
//权限校验
let pass = whiteList.includes(to);
if(!pass){
alert('无权访问')
return;
}
next();
});
针对场景二,路由不可访问是指,当用户没有某个路由访问权限的时候,直接去访问该路由的时候会直接报404访问路由不存在,而不是像场景一一样提示没有权限。
这种场景一般是这样操作的:
首先注册不需要权限即可访问的路由,然后通过vue-router的api router.addRoutes()
直接动态生成有权限的路由。
伪代码:
const {routeAuthList} = login() // 一般后端给出可访问路由接口
cosnt whiteList = ['/login','/','/404'] // 不需要权限的即可访问的路由
//路由守卫判断
router.beforeEach((to, from, next) => {
if(login === false) { // 判断是否已经登录(未登录时)
store.dispatch("GETUSERINFO").then((res) => { // 获取后端返回的用户权限路由数组
const roles = res.data.roles;
store.dispatch("GENERATEROUTES", {roles}).then(() => { // 加工成router.addRoutes可接收的参数形式
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
})
})
}
})
该情况的核心就是router.addRoutes()
菜单权限
菜单权限实际上也是路由权限,只不过比路由复杂一点,需要同时协调menu的激活态,所以一般也是有两种场景。
场景一:没有权限的菜单依然可见,点击菜单访问时在路由拦截器中进行拦截,阻断访问。
场景二:没有权限的菜单直接不可见,也就无法访问。
场景一和路由权限的场景一相同,区别就在于菜单激活问题。
场景二
router.beforeEach(async (to, from, next) => {
if(login === false) { // 判断是否已经登录(未登录时)
await store.dispatch("GETUSERINFO"); // 获取用户信息,里面包含可访问menu列表
await store.dispatch("UPDATEMENU"); // 两个功能,1. 调用addRoutes()增加动态路由 2. 更新菜单,将菜单路由存储在vuex中。
if (to.path === '/login') {
next({ name: 'home_index' })
} else {
next({ ...to, replace: true })//菜单权限更新完成,重新进一次当前路由
}
}
})
这种场景核心就是需要在获取到用户的可访问menu列表后,使用addRoutes()方法动态添加路由,之后还需要更新menu。
参考资料:
https://mp.weixin.qq.com/s/kku7-HJ1UjOUD29fXf446Q
https://segmentfault.com/a/1190000020887109