权限是什么
权限是对特定资源的访问许可,所谓权限控制,也就是确保用户只能访问到被分配的资源
而前端权限归根结底是请求的发起权,请求的发起可能有下面两种形式触发
- 页面加载触发
- 页面上的按钮点击触发
- 总的来说,所有的请求发起都触发自前端路由或视图
所以我们可以从这两方面入手,对触发权限的源头进行控制,最终要实现的目标是:
- 路由方面,用户登录后只能看到自己有权访问的导航菜单,也只能访问自己有权访问的路由地址,否则将跳转 4xx 提示页
- 视图方面,用户只能看到自己有权浏览的内容和有权操作的控件
- 最后再加上请求控制作为最后一道防线,路由可能配置失误,按钮可能忘了加权限,这种时候请求控制可以用来兜底,越权请求将在前端被拦截
前端权限控制分为四个方面
- 接口权限
- 按钮权限
- 菜单权限
- 路由权限
接口权限
接口权限目前一般采用jwt的形式来验证,没有通过的话一般返回401,跳转到登录页面重新进行登录
登录完拿到token,将token存起来,通过axios请求拦截器进行拦截,每次请求的时候头部携带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')
}
})
面试官问:动态路由是怎么实现的?或者问 权限控制怎么做 权限控制分为三大块
- 路由权限
- 要达到的效果:用户只能看到自己有权限访问的页面,如果访问不是自己能看到的页面就是404 不同的账号能看到的页面不一样,有的能看到全部,有的只能看到部分
- 实现思路
-
- 前端路由维护了两份路由表(静态路由表,也就是所有的人都可以看到的,直接在路由实例router上面挂载,还有一份动态路由表要根据用户当前权限动态筛选addRoutes添加上去的)( router.addRoutes)
- 菜单权限
- 要达到的效果:后台左侧侧边栏导航是v-for动态渲染出来 有的用户能看到所有的菜单 有的只能看到部分
- 实现思路 菜单是根据当前用户自己的路由表来v-for动态渲染出来
- 按钮权限
- 要达到的效果:某个页面上有新增、删除、修改按钮,不同的账号登录之后有的可以操作所有的功能,有的只能新增 (一种思路是disabled,还有一种思路隐藏)
- 实现思路 登录成功会得到一份权限点列表,里面记录了各个页面上面的按钮能不能操作的标识,我们可以定义一个mixin混入,在里面定义一个方法,用于判断当前这个页面上面某个按钮的权限点是不是在权限点列表中,如果是这个按钮是启用或者显示,如果不是,这个按钮就禁用或隐藏0
路由权限控制
方案一
初始化即挂载全部路由,并且在路由上标记相应的权限信息,每次路由跳转前做校验
缺点:
加载所有的路由,如果路由很多,而用户并不是所有的权限都访问,对性能会有影响。
- 全部路由守卫里,每次跳转都会做权限判断。
- 菜单信息写死在前端,要改个显示文字或者权限信息,需要重新编译
- 菜单跟路由耦合在一起,定义路由的时候还有添加菜单显示标题,图标之类的信息,而且路由不一定作为菜单显示,还要多加字段进行标识
方案二
初始化的时候先挂载不需要权限控制的路由,比如登录页,404等错误页。如果用户通过URL进行强制访问,则会进入404,相当于从源头做了控制,登陆后,在获取用户权限信息,然后筛选有权限访问的路由,在全局路由守卫里进行条用addRoutes添加路由
按需挂载,路由就需要知道用户的路由权限,也就是在用户登录进来的时候就要知道当前用户的那些路由权限
缺点 :
- 全部路由守卫里,每次跳转都会做权限判断。
- 菜单信息写死在前端,要改个显示文字或者权限信息,需要重新编译
- 菜单跟路由耦合在一起,定义路由的时候还有添加菜单显示标题,图标之类的信息,而且路由不一定作为菜单显示,还要多加字段进行标识
菜单权限
菜单权限可以理解成页面与路由进行解耦
方案一
菜单与路由分离,菜单由后端返回
前端定义路由信息
{
name:'login',
path:'/login'
component:()=>import('@/pages/Login.vue')
}
name字段都不为空,需要根据此字段与后端返回菜单做关联,后端返回的菜单信息中必须要有name对应字段,并且作为一性校验
全局路由守卫里做判断
每次路由跳转的时候都需要判断权限,这里的判断也很简单,因为 菜单的name与路由的name是一一对应的,而后端返回的菜单就已经是经过权限过滤的
如果根据路由name找不到对应的菜单,就表示用户没有权限访问
如果路由很多,可以在应用初始化的时候,只挂载不需要权限控制的路由。取得后端返回的菜单后,根据菜单与路由的对应关系,筛选可访问的路由,通过addRoutes动态挂载
缺点:
- 菜单需要与路由做到一一对应,前端添加了新功能,需要通过菜单功能管理功能添加到新的菜单,如果菜单配置的不对会导致不能正常使用
- 全局路由守卫里,每次路由跳转都需要做判断
方案二
菜单和路由都有后端返回
前端统一定义路由组件
constHome=()=>import("../pages/Home.vue");
constUserInfo=()=>import("../pages/UserInfo.vue");
*export* *default*{
home:Home,
userInfo:UserInfo
};
后端组件返回以下格式(可以说后端统一返回格式)
[
{
name:"home",
path:"/",
component:"home"
},
{
name:"home",
path:"/userinfo",
component:"userInfo"
}
]
将后端返回路由经过addRoutes动态挂载之间,需要将数据处理一下,将component字段换为正在的组件
如果由嵌套路由,后端功能设计的时候,要注意添加相应的字段,前端拿到数据也要做相应的处理
缺点:
- 全局路由守卫里,每次录由跳转都要做判断
- 前后端的配合要求更高
按钮权限
方案一
按钮权限也可以用v-if来判断
但是如果页面过多,每个页面都要获取用户权限role和录由表里的meta.btnPermissions,然后再做判断
方案二
通过自定义指令进行按钮权限的判断
首先配置录由
{
path:'/permission',
component:Layout,
name:'权限测试',
meta:{
btnPermissions: ['admin','supper','normal']
},
*//页面需要的权限*
children: [{
path:'supper',
component:_import('system/supper'),
name:'权限测试页',
meta:{
btnPermissions: ['admin','supper']
} *//页面需要的权限*
},
{
path:'normal',
component:_import('system/normal'),
name:'权限测试页',
meta:{
btnPermissions: ['admin']
} *//页面需要的权限*
}]
}
**自定义权限鉴定指令**
*import* Vue *from*'vue'
*/**权限指令**/*
consthas= Vue.directive('has',{
bind:function(*el*,*binding*,*vnode*){
*// 获取页面按钮权限*
letbtnPermissionsArr= [];
*if* (*binding*.value) {
*// 如果指令传值,获取指令参数,根据指令参数和当前登录人按钮权限做比较。*
btnPermissionsArr=Array.of(*binding*.value);
}*else*{
*// 否则获取路由中的参数,根据路由的btnPermissionsArr和当前登录人按钮权限做比较。*
btnPermissionsArr=*vnode*.context.$route.meta.btnPermissions;
}
*if* (!Vue.prototype.$_has(btnPermissionsArr)) {
*el*.parentNode.removeChild(*el*);
}
}
});
*// 权限检查方法*
Vue.prototype.$_has=function(*value*){
letisExist=false;
*// 获取用户按钮权限*
letbtnPermissionsStr=sessionStorage.getItem("btnPermissions");
*if* (btnPermissionsStr==undefined||btnPermissionsStr==null) {
*return*false;
}
*if* (*value*.indexOf(btnPermissionsStr) >-1) {
isExist=true;
}
*return*isExist;
};
*export*{has}
在使用的按钮中只需要引用v-has指令
<el-button @click='editClick' type="primary" v-has>编辑</el-button>
小结
关于权限如何选择哪种合适的方案,可以根据自己项目的方案项目,如考虑路由与菜单是否分离
权限需要前后端结合,前端尽可能的去控制,更多的需要后台判断
路由和菜单权限控制:开始 --> 登录 --> 登录成功获取当前用户权限列表 --> 根据权限列表生成完整的路由表(包含静态路由表+筛选之后的动态路由表)--> 进入带有菜单的页面,根据完整路由表动态渲染左侧菜单