一、前言
在后台管理系统中,权限管理是很重要的一部分。一般权限管理分为两大类:
1.接口访问权限控制
2.页面访问权限控制
菜单中的页面能否被访问
页面中的按钮的权限
今天先来看页面访问权限的控制
二、页面访问权限的控制
首先页面访问权限,可以分为两种:
1.菜单栏展示全部菜单,没有权限的菜单点击时提示没有权限
2.菜单栏只展示用户能访问的菜单,通过URL强行进入页面时,进入的是404
比较倾向于第二种,一眼就让用户知道自己有哪些权限
大致流程如下:
登录的时候,获取权限菜单--------->存储到session---------->渲染
1.肯定是要搭建vue项目,封装axios,就不说了,可以参考之前写的文章,Vue项目搭建
2.我们可以先获取权限信息,再继续其他操作
import { login, getAside } from '@/api/user-api'
import { StatusCode } from '@/utils/constant'
import { saveRouterInfoFromStorage, transfer } from '@/utils/utils'
function sLogin (data) {
return new Promise((resolve, reject) => {
login(data).then(res => {
if (res.code === StatusCode.Success) {
// 登录成功,请求接口获取权限信息
let routerInfo
getAside().then(res => {
if (res.data.code === StatusCode.Success) {
routerInfo = res.data.data
// 因为没有一个公共的共有的权限页面,所以需要保存下权限信息里的第一个页面,并在登录成功之后跳转到该页面
sessionStorage.setItem('redirectPath', routerInfo[0].children[0].path)
// 权限信息存储到session中,此处是把存储封方法装了一下
saveRouterInfoFromStorage(routerInfo)
this.$router.push(sessionStorage.getItem('redirectPath'))
if (routerInfo.length === 0) {
// 如果该用户没有任何菜单权限,进入指定的提示页面
this.$router.push('/noPerssion')
}
}
})
return transfer(res.data)
} else {
// eslint-disable-next-line prefer-promise-reject-errors
reject()
}
})
})
}
export {
sLogin
}
我这里把接口请求封装之后,做了个api层和service层,api层管理接口请求,service层管理数据,这样view层拿到数据直接渲染就可以,不需要在view里过多的去处理数据。
这里其实就是调用登录接口,成功之后,调用获取权限信息的接口,把拿到的数据存储到session中。
3.接下来要创建路由表了,需要把权限页面和登录页、404等页面分开
import Vue from 'vue'
import Router from 'vue-router'
// 登录页以及404等不需要权限的页面
import Index from '../views/index/index.vue'
import Login from '../views/login/login.vue'
// 权限页面,这里写所有需要权限的页面
const Commodity = () => import('../views/commodityManagement/commodity')
const ProductGroup = () => import('../views/commodityManagement/productGroup')
const Live = () => import('../views/courseManage/live')
Vue.use(Router)
const constRouterMap = [
{
path: '/',
name: 'index',
component: Index
},
{
path: '/login',
name: 'Login',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: Login
}]
const router = new Router({
routes: constRouterMap
})
const routerMap = {
Commodity,
ProductGroup,
Live
}
export {
router,
routerMap,
constRouterMap
}
接下来,我们利用返回数据匹配之前写的路由表,将匹配结果和静态路由表结合,开成最终的实际路由表。在这里我写了个权限文件,在main.js中引入就可以了。
以下是权限文件的代码 permission.js文件
import { router, routerMap } from '../routers/router'
import Layout from '@/layout'
import { clearStorage, getRouterInfoFromStorage, getToken, saveRouterInfoFromStorage } from './utils'
import { getAside } from '../api/system-api'
import { StatusCode } from './constant'
let routerInfo
const whiteList = ['/login']
router.beforeEach((to, from, next) => {
// 判断是否登录
if (getToken()) {
if (to.path === '/login') {
next({ path: '/' })
} else {
if (!routerInfo) {
// 判断localStorage中是否有路由信息
if (!getRouterInfoFromStorage()) {
clearStorage()
// 重新获取路由
getAside().then(result => {
console.log(result)
if (result.data.code === StatusCode.Success) {
routerInfo = result.data.data
sessionStorage.setItem('redirectPath', routerInfo[0].children[0].path)
saveRouterInfoFromStorage(routerInfo)
addRouterAndNext(to, next)
}
})
} else {
routerInfo = getRouterInfoFromStorage()
addRouterAndNext(to, next)
}
} else {
next()
}
}
} else {
if (whiteList.indexOf(to.path) !== -1) {
next()
} else {
next({ path: '/login' })
}
}
})
function addRouterAndNext (to, next) {
routerInfo = filterRouter(routerInfo)
router.addRoutes(routerInfo) // 添加动态路由
next({ ...to, replace: true })
}
function filterRouter (routerInfo) {
return routerInfo.filter(router => {
if (router.component) {
if (router.component === 'Layout') {
router.component = Layout
} else {
router.component = routerMap[router.component]
}
}
if (router.children && router.children.length) {
router.children = filterRouter(router.children)
}
return true
})
}
到此,页面的权限控制已经完成了。
三、按钮权限控制
按钮权限控制,我是写了个方法放到了utils.js中,直接上代码
// Get all the buttons in this page by specified privileges
function getButtonsOfPage (pageName) {
const routeInfo = getRouterInfoFromStorage()
let routeName = this.$route.name
if (routeInfo) {
if (pageName != null) {
routeName = pageName
}
const currentPermission = getPermission(routeInfo, routeName)
this.allButtons = getButtons(currentPermission.children)
}
}
function getPermission (routerInfo, name) {
let result
for (let i = 0; i < routerInfo.length; i++) {
if (routerInfo[i].name !== name && routerInfo[i].children) {
result = getPermission(routerInfo[i].children, name)
} else if (routerInfo[i].name === name) {
result = routerInfo[i]
}
if (result != null) {
return result
}
}
return result
}
使用的时候,直接在要使用的页面先引入
import { getButtonsOfPage } from '../../utils/utils‘
data(){
return{
getButtonsOfPage
}
}
然后在页面创建的时候,调用一下
created () {
this.getButtonsOfPage()
},
这里区分了是页面按钮还是表单按钮,可以根据buttonType去做个区分,页面渲染是一样的
<fragment v-for="(item, index) in allButtons" :key="index">
<el-button v-if="item.subType === StatusButton.PageButton" @click="clickBtn">{{ item.name }}</el-button>
</fragment>
到此为止,页面和按钮的权限控制就完成了,下面附上后台返回的权限的数据格式
meta里边定义了一个用于配置子页面时,当前菜单栏的高亮显示
type:定义了时页面还是按钮
subType:定义了是页面按钮还是表单按钮
注意⚠️:path不能为null,如果没有path,随便写个字符串就可以,不然会报错
此处定义了菜单栏的icon的名称,在渲染的时候,名称和路径对应过来就可以了
"data": [
{
"id": 1,
"parentId": 0,
"path": "none",
"meta": {
"activePath": null
},
"name": "首页",
"img": null,
"imgHover": null,
"component": "Layout",
"type": 1,
"subType": null,
"sort": 1,
"children": [
{
"id": 6,
"parentId": 1,
"path": "/partnerRelated/partner",
"meta": {
"activePath": "/partnerRelated/partner"
},
"name": "管理1",
"img": "Partner",
"imgHover": "PartnerHover",
"component": "Partner",
"type": 1,
"subType": null,
"sort": 6,
"children": [
{
"id": 25,
"parentId": 6,
"path": "none",
"meta": {
"activePath": null
},
"name": "详情",
"img": null,
"imgHover": null,
"component": null,
"type": 2,
"subType": 2,
"sort": 25,
"children": []
},
{
"id": 52,
"parentId": 6,
"path": "none",
"meta": {
"activePath": null
},
"name": "分配",
"img": null,
"imgHover": null,
"component": null,
"type": 2,
"subType": 2,
"sort": 52,
"children": []
},
{
"id": 53,
"parentId": 6,
"path": "none",
"meta": {
"activePath": null
},
"name": "导出",
"img": null,
"imgHover": null,
"component": null,
"type": 2,
"subType": 1,
"sort": 53,
"children": []
}
]
}
]
}
],