最近我的一个大学城家教
项目准备上线了,预计用户量会达到不错的预期。随之,后台管理中审核用户学生信息的工作量会增大。所以我打算,开多几个后台账号分别给不同的管理员使用,这里就需要配置后台的权限管理
。
我在网上搜寻到的很多文章教程仅有两个角色
的权限管理,在我的项目中不太足够,所以我将写一个动态添加后台管理权限
的demo。
技术栈
- 前端:Vue2
- 后端:Koa + MongoDB
思路
- 前端先把所有的页面和菜单功能都写好
- 前后端沟通
- 每个
路由对应的字段
,前端在router/index.js,中根据字段的映射,把对应路由添加到当前路由下 - 每个
菜单对应的字段
,这部分的映射放在后端,可以减少前端打包的体积,把菜单对应的数据发给后端
3.创建角色管理模块,注册用户,并给用户添加相应权限
具体实现
先看看整体的结构
一共两个菜单,三个路由,对应的映射如下;
后端代码
注册接口
routerMap中的数据是前端菜单的渲染数据,由前端提前测试好后,传给后端
let routerMap = {
'userManger': {
id: 1,
authName: '权限管理',
icon: 'el-icon-connection',
children: [
{
id: 11,
authName: '用户页面',
icon: 'el-icon-s-grid',
path: 'table',
rights: [] //存放对应菜单的功能权限
},
{
id: 12,
authName: '素材页面',
icon: 'el-icon-s-marketing',
path: 'image',
rights: [] //存放对应菜单的功能权限
}
]
},
'test': {
id: 2,
authName: '测试菜单',
icon: 'el-icon-set-up',
children: [
{
id: 21,
authName: '测试页面',
icon: 'el-icon-s-custom',
path: 'users',
rights: [] //存放对应菜单的功能权限
}
]
}
}
const signup = (form) => {
let {username,password,type,type2,role} = form;
let rights = [];
// type => 菜单权限 , type => 功能权限
type.forEach(item => {
let route = JSON.parse(JSON.stringify(routerMap[item]));
let children = route.children.map(item=>{
item.rights = type2;
return item;
})
route.children = children;
rights.push(route);
})
// 在数据库中生成数据
const users = new Users({
username,
password,
role,
rights //存放菜单的数据,前端可以直接渲染
})
return users.save()
}
需要鉴权的接口管理
解密出token
中保留的用户角色role
,例如:角色管理模块的接口,只有超级管理员
可以操作这部分,都通过中间件判断role === 'admin'
,不是的话就返回错误信息。不了解JWT
的同学,可以看看我往期的文章 轻松解决JWT鉴权并实现简单权限管理
const jwt = require('jsonwebtoken')
const { secretKey } = require('../config/config')
tokenRouter.delete('/deleteUsers', Auth.middleware,async ctx => {
const { username } = ctx.request.body;
// console.log(form)
let res = await remove(username)
console.log(username)
ctx.body = {
res
}
})
class Auth {
get middleware() {
return async (ctx, next) => {
const token = ctx.request.header.authorization.split(' ')[1];
// 解密出token中的保留的信息
let result = jwt.verify(token, secretKey)
if (result.role !== 'admin') {
ctx.body = { errCode: 1005, msg: '权限不足', }
return
}
await next()
}
}
}
module.exports = Auth
到这一步后端的工作已全部完成啦🎉🎉
前端代码
用户注册
注册时就把对应的的权限发给后端,这部分就不上代码了吧哈哈哈
我这里设置了两个角色,便于后面做接口的权限管理
。实际开发中只要按自己的需求去定制就可以啦。
用户登录
将菜单数据存到VueX中,并动态添加路由规则
async onLogin () {
this.loading = true
let result = await login(this.form);
if(result.data.errCode == 10001){
this.$message.error(result.data.msg);
this.loading = false
return;
}
const {role,username,photo,rights} = result.data.user
this.$store.commit('setRole', role);
localStorage.setItem('token', result.data.token);
// 把菜单数据存到VueX中
this.$store.commit('setRightList', rights)
this.loading = false
this.$message.success('登陆成功')
// 根据用户所具备的权限 动态添加路由规则
initDynamicRoutes()
this.$router.push('/home')
}
动态添加路由规则
// 动态路由
const tableRule = {
path: '/home/table',
name: 'table',
component: () => import('@/views/table/index.vue')
}
const imageRule = {
path: '/home/image',
name: 'image',
component: () => import('@/views/image')
}
const userRule = {
path: '/home/users',
name: 'users',
component: () => import('@/views/users')
}
export function initDynamicRoutes () {
// 根据二级权限 对路由规则进行动态的添加
const currentRoutes = router.options.routes
// 从VueX中拿到菜单权限,把对应的菜单路由添加到路由表中
const rightList = store.state.rightList
console.log('rightList',rightList)
rightList.forEach(item => { // 如果是没有子路由的话 就直接添加进去 如果有子路由的话就进入二级权限遍历
// console.log(item, 'item-1')
if (item.path) {
const temp = ruleMapping[item.path]
// 路由规则中添加元数据meta
temp.meta = item.rights
currentRoutes[2].children.push(temp)
}
item.children.forEach(item => {
// item 二级权限
// console.log(item, 'item-2')
const temp = ruleMapping[item.path]
// 将路由的功能权限中添加元数据meta
temp.meta = item.rights
currentRoutes[2].children.push(temp)
})
})
// console.log(currentRoutes)
router.addRoutes(currentRoutes)
}
渲染菜单列表
在菜单组件created()
拿到Vuex中的rightlists
,直接渲染上去就ok了,这部分就不上代码了,毕竟大家用的ui组件不同,对应的数据结构
也不同。
按钮权限的处理
我的项目中暂时不需要考虑到按钮权限
,按钮权限是指:根据存在路由meta中的功能权限
,控制按钮的显示
和点击
简单介绍一下实现方法,网上的都差不多。就是利用Vue的自定义指令
实现,具体如下:
<el-button
class="btn"
type="primary"
round
@click="handleNew"
v-permission="{ action: 'add', effect: 'disabled' }"
>新增管理员</el-button
>
Vue.directive('permission', {
inserted (el, binding) {
// el是dom节点
// action,effect都是自定义的变量
const action = binding.value.action
const effect = binding.value.effect
// 判断 当前的路由所对应的组件中 如何判断用户是否具备action的权限
if (router.currentRoute.meta.indexOf(action) === -1) { // 等于-1说明没找到 不具备权限
if (effect === 'disabled') {
el.disabled = true;
el.classList.add('is-disabled');
} else {
el.parentNode.removeChild(el)
}
}
}
})
接口权限
用户可能绕开按钮权限(F12删掉disabled),发送请求。我这里使用的是方案二
方案一
根据get,post,delete,put请求方法,与路由meta中的功能权限字段形成映射
,在axios请求拦截中进行权限判断
// 映射
const actionMapping = {
get: 'view',
post: 'add',
put: 'edit',
delete: 'delete'
}
//拦截请求
request.interceptors.request.use((config)=>{
let token = localStorage.getItem('token');
config.headers.Authorization = token;
if (config.url !== '/token') {
// 拿到请求对应的字段
const action = actionMapping[config.method]
// 判断非权限范围内的请求
const currentRight = router.currentRoute.meta
if (currentRight && currentRight.indexOf(action) === -1) {
// 没有权限,返回提示,抛错
Message.error('没有权限')
return Promise.reject(new Error('没有权限'))
}
}
return config
})
方案二
后端在需要鉴权的接口,解密出token
中保留的用户角色role
,例如角色管理模块的接口,都通过中间件判断role === 'admin'
,不是的话就返回错误信息。具体实现可见后端部分代码
到这一步前端的工作已全部完成啦🎉🎉
END
权限管理其实也没那么难,主要的工作是前后端沟通
,要先确认好对应的字段才能进行接下来的工作。
希望这篇文章可以帮助到有需要的小伙伴,有问题可以在评论区留言或私信我🤞🤞
暂无评论内容