共计 3225 个字符,预计需要花费 9 分钟才能阅读完成。
前言
首先还是谢谢各位童鞋的大大的赞赞,你们的支持是我前进的动力!上周写了一篇从 0 到 1 搭建 element 后台框架,很多童鞋留言提到权限问题,这一周就给大家补上。GitHub
一、jwt 授权认证
现在大多数项目都是采用 jwt 授权认证,也就是我们所熟悉的 token 登录身份校验机制,jwt 的好处多多,由于 jwt 是由服务端生成,中间人修改密串后,服务端会校验不过,安全有效。一般呆在请求头上的 Authorization
里面。前端童鞋一般获取 token 后通过 vuex 存储起来,随后数据持久化存到 session 中。
路由跳转验证 token
首先在路由跳转的时候需要验证 vuex
是否存储了 token, 如果没有 token 的话直接跳到登陆页面获取 token。
if (to.path !== '/login' && !store.state.token) {next('/login')
NProgress.done() // 结束 Progress} else {next();
}
请求拦截带上 token
详细请看项目中的router.js
本地存在 token 之后,我们在每次请求接口的时候都需要带上 token 来验证 token 的合法性。
// 在请求前拦截
if (store.state.token) {config.headers["Authorization"] = "Bearer" + store.state.token;
}
如果 token 不合法,全局错误处理,直接跳到登陆页面
case 401:
messages("warning", "用户登陆过期,请重新登陆");
store.commit('COMMIT_TOKEN','')
setTimeout(() => {
router.replace({
path: "/login",
query: {redirect: router.currentRoute.fullPath}
});
}, 1000);
break;
详细代码看项目中的request.js
二、菜单权限
本项目中,我主要是通过后端传过来的角色类型来判断导航菜单的显示与隐藏。
也就是说首先前端请求接口,后端返回 token, 以及对应的角色,比如项目中用 admin
登陆的话,roles=['admin']
, 用 user
登陆的话roles=['user']
。
接下来我这边设计了一份菜单表和一份路由表,路由表主要是为了注册路由,不需要考虑层级关系。而菜单表需要考虑层级关系,里面可以配置主菜单,子菜单,图标等等一系列的东西,当然菜单表最好是通过接口数据从后端传过来。值得注意的是无论是菜单表,还是路由表,里面都有一个 meta
配置项。里面可以配置我们的角色权限。路由表对应的菜单表角色权限需要一致。没有配置角色权限的菜单默认都开放。
menu.js
{
icon: "el-icon-question",
index: "premission",
title: "权限测试",
subs: [{
index: "permission",
title: "菜单测试",
meta: {roles: ['admin']
}
},
{
index: "permissionBtn",
title: "按钮权限",
},
]
}
router.js
{
path: '/permission',
component: getComponent('permission', 'permission'),
meta: {
title: '菜单权限',
roles: ['admin']
}
},
根据角色过滤菜单
现在我们开始编写菜单逻辑,进入Aside.vue
, 首先根据角色过滤菜单表menu.js
/**
* @param {Arrary} menus 菜单
* @param {Arrary} roles 角色
* @return {Arrary} res 过滤后的菜单
*/
filterMenus(menus, roles) {const res = [];
menus.forEach(route => {const tmp = { ...route};
//hasPermission 判断权限是否匹配
if (this.hasPermission(roles, tmp)) {if (tmp.subs) {tmp.subs = this.filterMenus(tmp.subs, roles);
}
res.push(tmp);
}
});
return res;
},
/**
* 通过 meta.role 判断是否与当前用户权限匹配
* @param roles
* @param menu
*/
hasPermission(roles, menu) {if (menu.meta && menu.meta.roles) {return roles.some(role => menu.meta.roles.includes(role));
} else {return true;}
},
过滤结果
computed: {items() {let items = this.filterMenus(menu, this.$store.state.roles);
return items;
}
},
这样就获得了权限菜单
到目前为止,权限控制基本完成,不过在项目运行的过程中,还发现一个 bug。本项目中存在一个 tagList
, 也就是打开的导航标签,当用户从admin
切换到 user
的时候打开的导航标签依旧存在,也就是说用户可以通过导航标签进入 premission 页面。此时我这边直接通过路由拦截来处理此时的情况。
if(to.meta.roles){to.meta.roles.includes(...store.getters.roles)?next():next('/404')
}else{next();
}
没有权限的页面一律进入 404 页面。
三、按钮权限控制
按钮级别的权限说实话一般都通过数据接口来控制是否展示,点击等等情况。如果光有前端来控制绝对不是可行之道。
项目中按钮权限注册全局自定义指令来完成的。首先 src
下面新建一个 directive
文件夹,用于注册全局指令。在文件夹下新建一个premissionBtn.js
。如果对自定义指令不熟的话可以查阅官方文档。
全局指令
import Vue from 'vue'
import store from '@/store/store'
// 注册一个 v -allowed 指令
Vue.directive('allowed', {inserted: function (el, bingding) {
let roles = store.getters.roles
// 判断权限
if (Array.isArray(roles) && roles.length > 0) {
let allow = bingding.value.some(item => {return roles.includes(item)
})
if (!allow) {if (el.parentNode) {el.parentNode.removeChild(el)
}
}
}
}
})
引用
import './directive/premissionBtn'
那自定义指令如何使用呢?
<div class="premissionBtn">
<el-button type="primary" v-allowed="['admin']"> 我是只有 admin 的时候才能显示 </el-button>
<br>
<el-button type="info" v-allowed="['user']"> 我是只有 user 的时候才能显示 </el-button>
<br>
<el-button type="warning" v-allowed="['admin','user']"> 我是 admin 或者 user 才能显示 </el-button>
<br>
<el-button type="danger"> 任何角色都可以显示 </el-button>
</div>
后记
本项目还有许多需要完善和优化的地方,最后项目存在着不足或者更好的方法,请及时提出来,方便修正。谢谢大家。
相关文章链接
从 0 到 1 搭建 element 后台框架