vue从零搭建一个前中后台权限管理模板
背景
我司有很多需要进行权限管理的产品。其中有一个产品,需要给多个客户部署前中后台。在开发第一个版本时,代码全部分离。前端三套,后端三套。加上kafka,redis,算法,数据库等服务器,每有一个新的客户就需要部署一次,需要花费很长的时间且代码难以维护。
后决定重构代码,产品分为前,中,后三个平台。前后端分别一套代码,支持权限管理,可拓展。前端使用路由前缀判断平台,登录时会返回不同的token和用户信息。不同的token只能访问对应平台的接口,根据用户角色生成可访问的菜单,进入不同的系统
前言
权限模块对于一个项目来说是比较麻烦的部分,通常一个项目的权限管理,需要做的是下面三种级别的鉴权。
- 平台级别
- 页面级别(菜单)
- 控件级别(如按钮,表格展示字段等)
本篇文章站在前端的角度,实现前两种级别的权限管理(控件级别可以通过条件渲染实现)。用vue从零搭建一个前中后台权限管理模板。供大家参考。
演示地址:http://auth.percywang.top
项目地址:https://github.com/pppercyWang/vue-authentication
其实大部分项目都会分离前后台,因为整合在一套代码,确实对打包优化,代码分割需要做的更多。且项目架构上会复杂一些,安全性方面需要考虑的更全面。这里也提供了一个纯后台的权限管理模板。
项目地址:https://github.com/pppercyWang/vue-authentication2
项目结构
技术栈:vue vue-router vuex element
assets 静态资源
pluginselement-style.scss element样式element.js 按需引入
routerindex.js 静态路由及createRouter方法
serviceapi.js 前中后台接口管理
store vuex
utilshttp.js axios封装
viewsforeground 前台页面midground 中台页面background 后台页面layout 前中后台布局文件404.vue 404页面Login.vue 前台登录AgentLogin.vue 中台登录AdminLogin.vue 后台登录
permission.js 动态路由 前中后台鉴权 菜单数据生成
main.js 应用入口
一. 路由初始化——staticRoutes
三个平台登录是三个不一样的页面。/开头的是前台的路由,/agent是中台,/admin是后台。这里的重定向也可以跳转到具体的页面,但这里因为权限角色不同的原因,不能写死,就直接重定向到登录页。
注意:404页需要放在路由的最后面,所以放在动态路由部分
router/index.js
const staticRoutes = [{path: '/login',name: '用户登录',component: () => import('@/views/Login.vue'),},{path: '/agent/login',name: '中台登录',component: () => import('@/views/AgentLogin.vue'),},{path: '/admin/login',name: '后台登录',component: () => import('@/views/AdminLogin.vue'),},{path: '/',redirect: '/login',},{path: '/agent',redirect: '/agent/login',},{path: '/admin',redirect: '/admin/login',},
]
二. 动态路由——dynamicRoutes
本例只有中台和后台进行鉴权,一级栏目需要icon字段,用于菜单项图标。children为一级栏目的子栏目,meta中的roles数组代表可访问该route的角色。
permission.js
const dynamicRoutes = {// 前台路由'user': [{path: '/',component: () => import('@/views/layout/Layout.vue'),name: '首页',redirect: '/home',children: [{path: 'home',component: () => import('@/views/foreground/Home.vue'),}]}, ],// 中台路由'agent': [{path: '/agent/member',component: () => import('@/views/layout/AgentLayout.vue'),name: '会员管理',redirect: '/agent/member/index',icon: 'el-icon-star-on',children: [{path: 'index',component: () => import('@/views/midground/member/Index.vue'),name: '会员列表',meta: {roles: ['super_agent', 'second_agent'] // 超级代理和二级都可访问},},{path: 'scheme',component: () => import('@/views/midground/member/Scheme.vue'),name: '优惠方案',meta: {roles: ['super_agent'] // 只有超级代理可访问},},]},],// 后台路由'admin': [{path: '/admin/user',component: () => import('@/views/layout/AdminLayout.vue'),name: '用户管理',redirect: '/admin/user/index',icon: 'el-icon-user-solid',children: [{path: 'index',component: () => import('@/views/background/user/Index.vue'),name: '用户列表',meta: {roles: ['super_admin', 'admin']},},{path: 'detail',component: () => import('@/views/background/user/UserDetail.vue'),name: '用户详情',meta: {roles: ['super_admin']},},]},],'404': {path: "*",component: () => import('@/views/404.vue'),}
}
三. 登录页
通常在登录成功之后,后端会返回token跟用户信息,我们需要对token跟用户信息进行持久化,方便使用,这里我直接存在了sessionStorage。再根据用户角色的不同进入不同的路由
views/adminLogin.vue
try {const res = await this.$http.post(`${this.$api.ADMIN.login}`, this.form.loginModel)sessionStorage.setItem("adminToken", res.Data.Token);const user = res.Data.UsersessionStorage.setItem("user",JSON.stringify({username: user.username,role: user.role,ground: user.ground // 前中后台的标识 如 fore mid back}));switch (user.role) {case "ip_admin": // ip管理员this.$router.push("/admin/ip/index");break;case "admin": // 普通管理员this.$router.push("/admin/user/index");break;case "super_admin": // 超级管理员this.$router.push("/admin/user/index");break;}
} catch (e) {this.$message.error(e.Message)
}
四. 路由守卫——router.beforeEach()
只要是进入登录页,我们需要做两个事。
- 清除存储在sessionStorage的token信息和用户信息
- 使用permission.js提供的createRouter()创建一个新的router实例,替换matcher。
我们这里是使用addRoutes在静态路由的基础上添加新路由,但是文档中没有提供删除路由的api。可以试想一下,如果登录后台再登录中台,则会出现中台可以访问后台路由的情况。为什么替换matcher可以删除addRoutes添加的路由?
注:router.beforeEach一定要放在vue实例创建之前,不然当页面刷新时的路由不会进beforeEach钩子
main.js
router.beforeEach((to, from, next) => {if (to.path === '/login' || to.path === '/agent/login' || to.path === '/admin/login') {sessionStorage.clear();router.matcher = createRouter().matcher // 初始化routes,移除所有dynamicRoutesnext()return}authentication(to, from, next, store, router); //路由鉴权
})
new Vue({router,store,render: h => h(App)
}).$mount('#app')
五. 前中后台鉴权——authentication()
这里的switch函数根据to.path.split("/")[1]判定平台。在登录时成功后我们sessionStorage.setItem()保存token。
为什么要使用token agentToken adminToken三个不同的key来储存呢?而不是只将token作为key呢。这样在axios.interceptors.request.use拦截器中设置token头也不需要通过switch去获取不同的token了。
因为假设我们当前的页面路由是agent/member/index,我们手动修改为admin/xxx/xxx。我们希望它跳转到admin的登录页,而不是404页面。
isAuthentication标识是否完成鉴权,没有鉴权则调用generateRoutes获取有效路由,再通过addRoutes添加新路由
permission.js
export function authentication(to, from, next, store, router) {let token;switch (to.path.split("/")[1]) {case 'agent':token = sessionStorage.getItem('agentToken');if (!token && to.path !== '/agent/login') {next({path: '/agent/login'})return}break;case 'admin':token = sessionStorage.getItem('adminToken');if (!token && to.path !== '/admin/login') {next({path: '/admin/login'})return}break;default:token = sessionStorage.getItem('token');if (!token && to.path !== '/login') {next({path: '/login'})return}break;}const isAuth = sessionStorage.getItem('isAuthentication')if (!isAuth || isAuth === '0') {store.dispatch('getValidRoutes', JSON.parse(sessionStorage.getItem('user')).role).then(validRoutes => {router.addRoutes(validRoutes)sessionStorage.setItem('isAuthentication', '1')})}next();
}
通过user.ground判定平台
store/index.js
getValidRoutes({commit}, role) {return new Promise(resolve => {let validRoutesswitch (JSON.parse(sessionStorage.getItem('user')).ground) {case 'fore':validRoutes = generateRoutes('user', role, commit)resolve(validRoutes);breakcase 'mid':validRoutes = generateRoutes('agent', role, commit)resolve(validRoutes);breakcase 'back':validRoutes = generateRoutes('admin', role, commit)resolve(validRoutes);break}})},
六. 角色筛选——generateRoutes()
这里干了两件最重要的事
- 生成el-menu的菜单数据
- 生成当前角色有效的路由
permission.js
export function generateRoutes(target, role, commit) {let targetRoutes = _.cloneDeep(dynamicRoutes[target]);targetRoutes.forEach(route => {if (route.children && route.children.length !== 0) {route.children = route.children.filter(each => {if (!each.meta || !each.meta.roles) {return true}return each.meta.roles.includes(role) === true})}});switch (target) {case 'admin':commit('SET_BACKGROUD_MENU_DATA', targetRoutes.filter(route => route.children && route.children.length !== 0)) // 菜单数据是不需要404的breakcase 'agent':commit('SET_MIDGROUD_MENU_DATA', targetRoutes.filter(route => route.children && route.children.length !== 0))break}return new Array(...targetRoutes, dynamicRoutes['404'])
}
七.页面刷新后数据丢失
在登录后isAuthentication为1,刷新时不会重新生成路由,导致数据丢失,在main.js监听window.onbeforeunload即可
main.js
window.onbeforeunload = function () {if (sessionStorage.getItem('user')) {sessionStorage.setItem('isAuthentication', '0') // 在某个系统登录后,页面刷新,需重新生成路由}
}
new Vue({router,store,render: h => h(App)
}).$mount('#app')
拓展
这时候差不多就大功告成了,只需将数据渲染到el-menu即可。
1.后台控制权限
当前的路由鉴权基本上由前端控制,后端只需返回平台标识和角色。但实际开发时,肯定都是通过后台控制,菜单角色等信息需要建表入库。来修改栏目名称,一级栏目icon,菜单权限等
我们可以在getValidRoutes时获取一张权限表,将这些数据插入到dynamicRoutes中。后端返回的数据大致如下:
[{id: 1,name: '用户管理',icon: 'el-icon-user-solid',children: [{id: 3,name: '用户列表',meta: {roles: [1, 2]},},{id: 4,path: 'detail',name: '用户详情',meta: {roles: [1]},},]},{id: 2,name: 'IP管理',icon: 'el-icon-s-promotion',children: [{id: 5,name: 'IP列表',meta: {roles: [1, 2, 3]},}, ]},
]
2.安全性方面
前端:
- 跨平台进入路由,直接跳到该平台登录页。
- 当前平台访问没有权限的页面报404错误。
后端:
- 一定要保证相应平台的token只能调对应接口,否则报错。
- 如果能做到角色接口鉴权就更好了,从接口层面拒绝请求
3.axios封装
在请求拦截器中根据用户信息拿不同的token,设置头部信息
在响应拦截器中,如果token过期,再根据用户信息跳转到不同的登录页
4.api管理
如果后端也是一套代码。那api也可以这样进行管理,但如果没有一个统一的前缀。可以在axios设置一个统一的前缀例如proxy,这样就解决了跨域的问题。
const USER = 'api'
const AGENT = 'agent'
const ADMIN = 'admin'
export default {USER: {login: `${USER}/User/login`,},AGENT: {login: `${AGENT}/User/login`,uploadFile: `${AGENT}/Utils/uploadFile`,},ADMIN: {login: `${ADMIN}/User/login`,},
}
devServer: {proxy: {'/proxy': {target: 'http://localhost:8848',changeOrigin: true,pathRewrite: {'^proxy': '' //将url中的proxy子串去掉}}}},
vue从零搭建一个前中后台权限管理模板相关推荐
- IT:前端进阶技术路线图(初级→中级→高级)之初级(研发工具/HTML/CSS/JS/浏览器)/中级(研发链路/工程化/库/框架/性能优化/工作原理)/高级(搭建/中后台/体验管理等)之详细攻略
IT:前端进阶技术路线图(初级→中级→高级)之初级(研发工具/HTML/CSS/JS/浏览器)/中级(研发链路/工程化/库/框架/性能优化/工作原理)/高级(搭建/Node/IDE/中后台/体验管理/ ...
- go html vue,用Go+Vue.js快速搭建一个Web应用(初级demo)
Vue.js做为目前前端最热门的库之一,为快速构建并开发前端项目多了一种思维模式.本文给大家介绍用Go+Vue.js快速搭建一个Web应用(初级demo). 环境准备: 1. 安装go语言,配置go开 ...
- 深度学习笔记:利用numpy从零搭建一个神经网络
很多人说深度学习就是个黑箱子,把图像预处理之后丢进 tensorflow 就能出来预测结果,简单有效又省时省力.但正如我在上一篇推送中所说,如果你已是一名功力纯厚的深度学习工程师,这么做当然没问题.但 ...
- 【华为云技术分享】从零搭建一个灰度发布环境
DevUI是一支兼具设计视角和工程视角的团队,服务于华为云DevCloud平台和华为内部数个中后台系统,服务于设计师和前端工程师. 官方网站:devui.design Ng组件库:ng-devui(欢 ...
- 从零搭建一个 Spring Boot 开发环境!Spring Boot+Mybatis+Swagger2 环境搭建
从零搭建一个 Spring Boot 开发环境!Spring Boot+Mybatis+Swagger2 环境搭建 本文简介 为什么使用Spring Boot 搭建怎样一个环境 开发环境 导入快速启动 ...
- [渝粤教育] 宁波城市职业技术学院 Web服务器运维(从零搭建一个企业网站) 参考 资料
教育 -Web服务器运维(从零搭建一个企业网站)-章节资料考试资料-宁波城市职业技术学院[] 作业:购买阿里云ECS 作业:在万网注册域名 作业:ICP备案 微测验:准备主机 1.[单选题]ECS是阿 ...
- 自己搭服务器 做购物网站成本,从零搭建一个购物网站,实操经验
对于很多不懂代码不懂技术的人来说,想要搭建自己的购物网站非常的困难.难道不懂计算机基础,不会写代码就真的不能进行购物网站开发了吗?事实上并非如此.接下来HiShop小编就跟大家分享一下,如何从零搭建一 ...
- 对中国商业银行前中后台分离的反思
前中后台分离是建设流程银行的核心问题之一.前中后台分离固然给中国商业银行带来很多思想.组织.流程.行为和文化上的保障和转变,但是,在实现这一目标的过程中也产生一些问题. 为适应向零售银行转型,我国商业 ...
- 如何从零搭建一个hexo博客网站01
title: 如何从零搭建一个hexo博客网站01 #文章標題 categories: "Hexo教程" #文章分類目錄 可以省略 categories: "Hexo教程 ...
- 从零搭建一个基于React+Nextjs的SSR网站(四):如何搭建服务器并部署Nextjs项目
个人博客源码:https://github.com/shaotianyu/blog-front PS: 如果你有疑惑,可以给我留言,咱们一起解决它. 从零搭建一个基于React+Nextjs的SSR网 ...
最新文章
- 2022-2028年中国文化产业园投资分析及前景预测报告(全卷)
- 套接字选项SO_KEEPALIVE
- 笔记-高项案例题-2013年下-计算题
- zblog php版调用代码,zblog PHP分类列表调用方法
- java 处理表单_java – 处理multipart / form-data输入
- CSS绘制放大缩小关闭按钮
- 学习Numpy,看这篇文章就够啦
- python io操作有什么_Python文件IO操作
- Mosquito的优化——订阅树优化(八)
- 许家印大笔押注新能源:恒大集团1200亿沈阳投资建厂造车
- 阿里云蒋江伟:什么是真正的云原生?
- OC基础--OC中类的定义
- 邓白氏编码申请经验分享
- Java、某天是星期几
- 空间和时间 ----节选《时间简史》 霍金
- 浮窗---创建Activity浮窗(可拖动)
- 狄拉克函数- dirac 分布
- JACK——TeamsMaual6 Team Formation
- Keychron Q1:客制化机械键盘|体验
- 2022.08.12 第三组 高小涵
热门文章
- 0x00F749F6 处(位于 基于多态实现职工管理系统.exe 中)引发的异常: 0xC0000005: 读取位置 0x00000004 时发生访问冲突。
- ntdll.dll处引发的异常: 0xC0000005: 写入位置 0x00000004 时发生访问冲突
- 输入下载页面链接自动获取ipa下载地址,支持本地下载,支持蒲公英和fir及绝大多数自定义下载页
- Pycharm使用技巧:Split Vertically/Horizontally(垂直/水平拆分窗口)
- java 主动释放_java的手动释放资源
- 解读神经网络十大误解,再也不会弄错它的工作原理
- 计算机提示资源管理器停止,电脑开机黑屏并弹出Windows 资源管理器已停止工作该怎么办?...
- 手机卫星定位系统_如何判断自己的手机是否支持北斗卫星定位功能,如何使用?...
- 为何quot;矮矬穷quot;出身的成功…
- C -- OC with RunTime