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": []
            }
          ]
        }
      ]
    }
  ],
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容