权限控制方案

既然是后台权限管理系统,当然少不了权限控制啦,至于权限控制,前端方面当然就是对页面资源的访问和操作控制啦。

前端资源权限主要又分为两个部分,即导航菜单的查看权限和页面增删改操作按钮的操作权限。

菜单类型
菜单类型代码页面资源的类型。类型包括,0:目录 1:菜单 2:按钮’。


根据为不同角色分配相应的资源进行展示不同的导航菜单 以及按钮形式。角色分配的有哪些菜单或者按钮,页面显示出相应的菜单或者按钮。(只解释前端部分,以及数据结构样式,可让后端根据数据结构进行处理数据。)

数据结构例子:
在这里插入图片描述

登录的时候将该账号对应的角色所具有的权限返回出来 存到sessionStorage 或者vuex里边进行存储,注意:为了区分开菜单资源和按钮资源 防止登录的时候资源对菜单照成影响 导致按钮资源出现在菜单上 用menuTypeCn 这个资源类型字段区分。登录的时候只需要将所有的菜单权限展示出来就行了。然后根据菜单 查出来该菜单下的按钮。

权限标识
权限标识即是代表此页面资源,用来进行权限控制的唯一标识,主要是进行增删改查的权限控制。

权限标识包括,sys:user:add:新增 sys:user:edit:编辑 sys:user:delete:删除 sys:user:view:查看。
注:权限标识符可以根据实际情况 定义 例如
在这里插入图片描述
以上简单解释一下 下边直接上代码 提供过代码解释

第一步 我们需要具有一个权限资源列表 去管理我们所有的权限资源 包括 菜单 按钮等
这里我采用的是一个树形表格控件
在这里插入图片描述
menu 的数据结构对应上边的数据结构 是一个带有层级关系的一个集合

<el-table :data="menu" row-key="id" height='760' :tree-props="{children: 'children'}" :header-cell-style="{'text-align': 'left'}"  border class='tabel' v-loading='tabLoading'>
        <el-table-column label='名称' >
            <template slot-scope="scope">
                {{scope.row.menuName}}
            </template>
        </el-table-column>
        <el-table-column label='类型' >
            <template slot-scope="scope">
                {{scope.row.menuTypeCn}}
            </template>
        </el-table-column>
        <el-table-column label='值' >
            <template slot-scope="scope">
                {{scope.row.menuCode}}
            </template>
        </el-table-column>
        <el-table-column label='图标' >
            <template slot-scope="scope">
                {{scope.row.menuIcon}}
            </template>
        </el-table-column>
        <el-table-column label='排序' >
            <template slot-scope="scope">
                {{scope.row.menuSequence}}
            </template>
        </el-table-column>
        <el-table-column label='操作'>
            <template slot-scope="scope" v-if='scope'>
                <kt-button label='添加' size='mini' perms='acc:rule:add1' type='primary' @click='addMenu(scope.row)'></kt-button>
                <kt-button label='编辑' size='mini' perms='acc:rule:edt' type='primary' @click='edit(scope.row)'></kt-button>
                <kt-button label='删除' size='mini' perms='acc:rule:del' type='danger' @click='remove(scope.row)'></kt-button>
            </template>
        </el-table-column>
    </el-table>

这里 添加 编辑的表单不在解释 就是简单的表单提交在这里插入图片描述
这里的值 以及表格上的值 有两个作用 如果是菜单的话 这个值就是路由的最后一级。例如 :http://localhost:8080/#/index 我们的这个值就对应index
如果是按钮的话 这个值就是主要用作标识 去识别是否显示问题

管理好我们的这个列表之后

第二步:
有自己的账号管理模块 以及角色管理模块 为自己的账号 分配角色 一般情况下 一个账号对应一个角色
这里不做主要解释

第三步:
对账号分配完角色,需要对角色分配资源,保证我们菜单以及按钮的可控性。
这里不再做过多解释,就是正常的分配权限 包括 按钮权限和菜单权限 可以同步分配。

PS:如果各位需要 第二步 第三步详细解释 我之后可以进行补充

第四步:
之前已经在登录的时候将所有的菜单权限返回出来了,不返回按钮资源是为了防止 数据往菜单显示的时候按钮资源也会显示出来 ,如若后端数据一起返回按钮资源 前端同样可以通过判断 去除按钮资源

附代码 菜单部分,(可提供到三级菜单) data 对象里的代码不在展示

         <el-menu
            :default-active='routter'
            :collapse='isShow'
            class="sidebar-el-menu"
            :style="isShow ? 'text-align:center' : 'text-align:left' "
            :text-color="fontColor"
            active-text-color="#fff"
            :background-color="color"
            unique-opened
            router
          >
				  <!-- 一级菜单 -->
				  <template v-for="(item, index) in menu" >
            <template v-if="item.children.length > 0">
              <el-menu-item v-if="item.children[0].menuTypeCn == '按钮'" :index="item.menuCode" :key="index"><template><i :class="item.menuIcon" :style="'color:' + fontColor "></i><span slot="title">{{item.menuName}}</span></template></el-menu-item>
              <template v-else>
                <el-submenu v-if="item.children && item.children.length" :index="item.menuCode" :key="index">
				  	  	  <template slot="title"><i :class="item.menuIcon" :style="'color:' + fontColor "></i><span>{{item.menuName}}</span></template>

				  	  	  <!-- 二级菜单 -->
				  	  	  <template v-for="(itemChild, indey) in item.children">
                    <template v-if="itemChild.children.length > 0">
                      <el-menu-item v-if="itemChild.children[0].menuTypeCn == '按钮'" :index="itemChild.menuCode" :key="indey"><span slot="title">{{itemChild.menuName}}</span></el-menu-item>
                      <el-submenu :index="itemChild.menuCode" :key="indey" class="erji" v-else>
				  	  	  		<template slot="title"><span>{{itemChild.menuName}}</span></template>

				  	  	  		  <!-- 三级菜单 -->
				  	  	  		  <el-menu-item v-for="(itemChild_Child, indez) in itemChild.children" :index="itemChild_Child.menuCode" :key="indez">
				  	  	  		  	<span slot="title">{{itemChild_Child.menuName}}</span>
				  	  	  		  </el-menu-item>
				  	  	  	  </el-submenu>
                    </template>
                    <template v-else>
                      <el-menu-item :index="itemChild.menuCode" :key="indey"><span slot="title">{{itemChild.menuName}}</span></el-menu-item>
                    </template>
				  	  	  </template>
				  	    </el-submenu>
            </template>
            </template>
            <template v-else>
              <el-menu-item :index="item.menuCode" :key="index"><template><i :class="item.menuIcon" :style="'color:' + fontColor "></i><span slot="title">{{item.menuName}}</span></template></el-menu-item>
            </template>
				  </template>
	      </el-menu>
	      

fontColor 默认颜色 ffffff
这个color 监听可要可不要 ,这个监听主要是监听主题颜色变换,因为如果是白色主题 配上白色字体的话白色字体显示不出来 就去监听下 主题变换 切换字体颜色 防止出现 字体和主题颜色相近 显示不出来的情况,可以去除,只留路由监听即可。

 mounted(){
      this.menu = JSON.parse(sessionStorage.getItem('MenuList'))
      this.routter = this.$router.currentRoute.path.slice(1)
    },
    watch: {
      color(curVal,oldVal){
        let col  = curVal.replace('#',"")
        if(this.hex2int(col) > this.hex2int('66FFFF')){
          this.fontColor = '#000000'
        }else{
          this.fontColor = '#ffffff'
        }
      },
      $route(to,form){
          this.routter = to.path.slice(1)
          this.getBtn(this.routter)
      }
    },
    methods: {
		getBtn(router){
        queryMenuButton({userId:JSON.parse(sessionStorage.getItem('userInfo')).id,menuCode:router}).then(res => {
            if(res.status == 200){
              sessionStorage.setItem('btnList',JSON.stringify(res.data))
            }
          })
      },
	}

通过监听路由 去获取该路由页面所有的被分配按钮资源,

页面操作按钮提供权限标识,查询是否在用户权限标识集合中。
在:有权限,可见或可用,不在:无权限,不可见或禁用。
目前本系统采用的是 按钮标识如果不在权限集合中则不显示 ,反之 则显示。

KtButton.vue 公共按钮组件

<template>
  <el-button :size="size" :type="type"
    :loading="loading" @click="handleClick" v-if="hasPerms(perms)" :icon="icon" :plain='plain'>
    <!--    v-if="hasPerms(perms)" -->
    {{label}}
  </el-button>
</template>

<script>
// import { hasPermission } from '@/permission/index.js'
export default {
  name: 'KtButton',
  props: {
    label: {  // 按钮显示文本
      type: String,
      default: ''
    },
    size: {  // 按钮尺寸
      type: String,
      default: 'mini'
    },
    type: {  // 按钮类型
      type: String,
      default: null
    },
    loading: {  // 按钮加载标识
      type: Boolean,
      default: false
    },
    disabled: {  // 按钮是否禁用
      type: Boolean,
      default: false
    },
    perms: {  // 按钮权限标识,外部使用者传入
      type: String,
      default: null
    },
    icon:{  // 按钮权限标识,外部使用者传入
      type: String,
      default: null
    },
    plain: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
    }
  },
  methods: {
    handleClick: function () {
      // 按钮操作处理函数
      this.$emit('click', {})
    },
    hasPerms: function (perms) {
      // console.log(perms)
      // if(perms == 1){
      //   return true
      // }else{
      //   return false
      // }
      // 根据权限标识和外部指示状态进行权限判断
      return this.hasPermission(perms)

    },
    hasPermission (perms) {
      let hasPermission = false
      let permissions = JSON.parse(window.sessionStorage.getItem('btnList'))
      for(let i=0; i<permissions.length; i++) {
        if(permissions[i].menuCode == perms) {
            hasPermission = true;
            break
        }
      }
      return hasPermission
    }
  },
  mounted() {
  }
}
</script>

<style scoped>

</style>

组件的引用 注册代码分别是

import KtButton from '@/components/KtButton.vue'
components:{
        KtButton
    }

使用过程
表格里的使用方法 其中 perms 参数 是我们主要传的标识符 perms=‘acc:rule:edt’ 其中 acc:rule:edt是我们自己在菜单权限管理列表的时候实现定义好的 标识符 ,原则上是随便定义 但是最好定义这种大家一眼就能看明白是什么意思。

<kt-button label='编辑' size='mini' perms='acc:rule:edt' type='primary' @click='edit(scope.row)'></kt-button>

在这里插入图片描述
拿这个标识符去跟自己权限资源集合里的按钮 按钮资源进行比对 如果有 就显示 没有就不显示。 具体判断代码是在KtButton.vue组件里。
具体判断 代码

hasPermission (perms) {
      let hasPermission = false
      let permissions = JSON.parse(window.sessionStorage.getItem('btnList'))
      for(let i=0; i<permissions.length; i++) {
        if(permissions[i].menuCode == perms) {
            hasPermission = true;
            break
        }
      }
      return hasPermission
    }

其中 permissions就是所有的该角色所具有的按钮资源的集合,是我们比对的对象。sessionStorage对象里的btnList 在之前我们进行监听路由的时候给存入进去的 该路由(页面)下 角色所具有的按钮资源集合。
通过组件传值 perms 把标识符传过去 进行比对 就可以通过分配权限,正常控制按钮的显示与隐藏。
注意

<kt-button label=‘编辑’ size=‘mini’ perms=‘acc:rule:edt’ type=‘primary’ @click=‘edit(scope.row)’>

这个传值的标识符 一定是我们权限管理模块 里边事先定义好的 我们定义什么标识符 这边传什么标识符。
例如:
在这里插入图片描述
在这里插入图片描述

<kt-button type='primary' label='搜索' perms='mem:owner:search' size='mini' @click='searchClick'/>
<kt-button type='primary' label='编辑' perms='mem:owner:edt' size='mini' @click='edit'/>
<kt-button type='primary' label='删除' perms='mem:owner:del' size='mini' @click='delete'/>

有什么遗漏的地方 欢迎大家批评。
有什么想深入了解的同样可以评论 私信呢!!!

最后希望大家虎年大吉喜气扬,虎头扬扬好运来,虎眼圆圆看祥瑞,虎须翘翘钱满园,虎身摆摆业登高,虎尾扫扫体健安,虎声震震尽欢颜,祝愿虎年多财气,旺虎载运福满天!

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐