权限管理主要分为后端权限和前端权限。后端权限是关键,主要控制对数据库的操作;前端权限为辅,主要表现在对界面显示、路由导航、按钮显示操作与否、无效请求前端拦截和响应拦截提升用户体验等。
菜单的控制:在登录成功后,会得到后端提供的权限数据。前端根据权限数据,展示对应的菜单;
界面的控制:
点击根据用户数据显示的对应菜单,显示不同界面
如果用户没有登录,手动在地址栏敲入某个需要权限的界面的地址, 则需要跳转到登录界面
如果用户已经登录,如果手动敲入非权限内的地址,则需要跳转404 界面
按钮的控制:在某个菜单的界面中, 还得根据权限数据, 展示出可进行操作的按钮,比如删除, 修改, 增加
请求和响应的控制(接口):如果用户通过非常规操作, 比如通过浏览器调试工具将某些禁用的按钮变成启用状态, 此时发的请求, 也应该被前端所拦截
说明:使用mock模拟后端返回数据
path代表路由地址,authName代表菜单名,children代表子菜单,rights代表该菜单下该用户拥有的权限
mockjs配置:
https://blog.csdn.net/qq_34569497/article/details/132496048
import Mock from 'mockjs'
const menuList = [
{'id': 1, path:'/uploadSpec','authName': "上传spec", icon: 'User', children:[], rights:['view','add','edit','delete']},
{'id': 2, path:'/showSpec', 'authName': "Spec预览", icon: 'DataAnalysis',children:[], rights:['view','add','edit','delete']},
{'id': 3, path:'/generateTxt', 'authName': "生成测试数据", icon: 'DataAnalysis',children:[], rights:['view','add','edit','delete']},
{'id': 4, path:'/generateCronjob', 'authName': "生成转码程序", icon: 'Promotion',children:[], rights:['view','add','edit','delete']},
{'id': 5, path:'/pdfCompare', 'authName': "PDF文档对比", icon: 'DocumentCopy',children:[], rights:['view','add','edit','delete']},
{'id': 6, path:'/resourceUpdate', 'authName': "资源更新管理", icon: 'Management',children:[], rights:['view','add','edit','delete']},
{'id': 7, path:'/generateTestCase', 'authName': "自动生成ST/SIT案例", icon: 'Files',children:[], rights:['view','add','edit','delete']},
{'id': 8, path:'/userManagement', 'authName': "用户管理", icon: 'User', rights:['view']},
{'id': 9, path:'/roleManagement', 'authName': "角色管理", icon: 'Stamp', rights:['view']},
]
Mock.mock("/user/login", Mock.mock({
"code": 200,
"success": true,
"data": {
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwidXNlck5hbWUiOiJhZG1pbiIsIm5pY2tOYW1lIjoi6LaF566hIiwiaWNvbiI6IiIsInJvbGVJZCI6MSwic3ViIjoiYWRtaW4iLCJleHAiOjE2OTI3NzMzNTMsImp0aSI6ImZkNmVkOWZiMjdiYzQxODg5OWRmYmYzNzhlMTMzZmQ0In0.APGpN-i2edKwPQA52LP10aDEM2DZi7G71k8f_njGcpE"
}
})
)
Mock.mock("/user/me", Mock.mock({
"code": 200,
"success": true,
"data": {
"id": 1,
"userName": "admin",
"nickName": "超管",
"icon": "",
"roleId": 1,
"menuList": menuList
}
})
)
Mock.mock("/role/delete", Mock.mock({
"code": 200,
"success": true,
})
)
后端(mock)提供菜单权限数据,根据数据渲染一级和二级菜单,并将数据存到vuex中;
权限数据rightList: setRightList(登录成功后将数据设置到vuex中)
Home.vue中引入mapState,并在computed中映射到rightList,并将rightList设置到menuList中
刷新数据后菜单栏消失,将vuex中数据存到localStorage或者sessionStorage中
退出登录后删除sessionStorage和vuex中的数据(刷新vuex数据,跳转页面后,window.location.reload())
参考以下:
vue权限管理——菜单权限设置__Jyann_的博客-CSDN博客
判断用户是否登录:token
什么时机判断登录:路由全局守卫,to.path进行判断 === "/login"直接next(); 否则需要获取token,如果token不存在,跳转到登录页,有则next()
手动在地址栏敲入地址:没有权限也会显示该界面。
需要权限控制的页面,使用动态路由进行管理
登录成功后,根据登录后的rightList用户拥有的权限,动态添加路由规则
对路由规则和权限字符串进行映射
router.js中导出,initDynamicRoutes()方法,登录后导入该方法,并遍历rightList,并将路由添加到Home页面的children中
动态路由在刷新后,又不存在了,但是在登录后点刷新操作不会再次登录,所以需要将在App.vue中created()中导入并执行initDynamicRoutes
详细见以下:
vue3动态路由设置__Jyann_的博客-CSDN博客
按钮也是根据数据显示,数据中有rights属性定义该用户拥有的所有权限。
实现:使用自定义指令。
permission.js中created()中方法实现
initDynamicRoutes给用户对应的router中动态添加meta元数据后,permission.js中通过router.currentRoute就可以获取
对比指令中指定的role和rightList中的role相同则像有权限,不同则删除
可禁用元素或者删除
参考我以下文章:
vue权限管理——按钮控制__Jyann_的博客-CSDN博客
登录后,每次发起请求前携带请求头token
根据用户权限数据,判断为非权限请求,则拒绝响应:
判断当前请求的行为
restful请求风格
get请求-》view
post请求-》add
put请求-》edit
delete请求-》delete请求
const actionMapping = {
'get':'view',
'post':'add',
'put':'edit',
'delete':'delete',
}
action中数据和currentRoute中数据不匹配则拒绝请求,return new Promise(new Error("没有操作权限"))
响应控制:请求返回401,token无效或者后期,则需要跳转到登录界面
详细参考我以下文章:
vue权限管理——请求和响应权限控制__Jyann_的博客-CSDN博客
前端权限控制必须根据后端返回数据实现
菜单控制:vuex和sessionStorage数据必须一致
界面控制:
路由导航守卫,防止跳过登录界面
动态路由可以让不存在权限的界面的路由根本不存在
按钮控制:
路由规则中增加路由元数据meta
通过路由对象可以得到当前路由规则,以及存储在此规则中的meta数据
自定义指令可以方便的实现按钮控制
请求和响应控制:
请求和响应拦截器的使用
请求方式行为的映射关系,需要请求api规范