Vue实现后台管理的权限控制

一、前言

在后台管理系统中,权限管理是很重要的一部分。一般权限管理分为两大类:
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": []
            }
          ]
        }
      ]
    }
  ],

博客迁移至GitHub

猜你喜欢

转载自blog.csdn.net/gladysdrea/article/details/107663058