Vue后台管理系统权限控制与管理

权限相关的概念

某个用户是否有对某个数据的增删改查等操作的权限

后端权限(对数据库中的数据的控制)

1.权限的核心在于服务器中数据的变化,后端权限可以控制某个用户是否可以对数据进行增删改查的操作
后端如何知道请求是哪个用户发送的??
状态保持:

cookie,session,token

一般而言前后端分离开发采用token来鉴权
2.后端权限设计RBAC(基于角色的权限控制)
用户==>角色==>权限

前端权限(视觉层面的控制)

主要用来控制前端视图层的展示与前端所发送的请求
意义:
- 降低非法操作的可能性
- 排除不必要的请求,减轻服务器的压力
- 提高用户的体验

前端权限控制的思路:(不限制某一技术,是个解决方案)
1.菜单的控制(后台管理系统中侧边栏的展示)
在登陆请求中,会得到权限的数据,需要后端来返回数据作为支持,前端根据后端返回的数据展示对应的菜单,点击菜单,查看对应的界面
2.界面的控制
如果用户没有登陆,手动在地址栏输入管理界面的地址,需要跳转到登陆界面
如果用户已经登陆,手动输入会权限内的地址,则需要跳转到404界面
3.按钮的控制
在某个菜单的界面中,需要根据权限数据展示可操作的按钮
4.请求与响应的控制
如果用户通过非常规的操作,如通过浏览器的调试工具将禁用的按钮变为可用,发送请求,也应该被拦截,尽管后端会返回错误的状态码,目的是减轻服务器的请求次数

Vue权限控制的实现:

1.菜单的控制
前提:登陆成功后拿到的对应的数据

{
    "data":{
        "email": "[email protected]",
        "id": 500,
        "mobile": "13999999999",
        "rid": 0,
        "token": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjUwMCwicmlkIjowLCJpYXQiOjE1MTI1NDQyOTksImV4cCI6MTUxMjYzMDY5OX0.eGrsrvwHm-tPsO9r_pxHIQ5i5L1kX9RX444uwnRGaIM",
        "username": "admin"
    },
    "meta":{
        "msg": "登录成功",
        "status": 200
    },
    "right":[
        {
            "authName": "事件管理",
            "children":[
                {
                  "authName": "事件添加",
                    "id": 111,
                    "path": "eventAdd",
                    "rights":["view","edit","add","delete"]
                },
                ],
            "icon": "icon-user",
            "id": 110
        },  
        {
             "authName": "系统管理",
            "children": [
                {
                "authName": "角色管理",
                "id": 172,
                "path": "role",
                "rights":["view","edit","add","delete"]
                },
            ],
            "icon": "icon-shangpin",
            "id": 170,
        },
    ]
}

- token用于前端页面的用户状态的保持
- right数组,是该用户具备的权限数据,一级权限对应一级菜单,二级权限对应二级菜单
注意:这些数据是在登陆界面获取到,如果想在首页使用,需要用到vuex来做全局的状态管理
vuex代码:

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
//引入并使用vux,抛出vuex new的对象
export default new Vuex.Store({
  state: {
    rightList: JSON.parse(sessionStorage.getItem('rightList') || '[]'),
    username: sessionStorage.getItem('username')
    //存储的值位于本地存储中,在这里读取出来存储在state中,页面中也可能用到当前用户的信息,需要的话进行存储,这里只用到了用户名
    //此处需要注如果没有对state中的值进行本地的存储会出现一个bug:页面刷新,vuex中的数据初始化,会变为空,只需要将数据存储在本地存储中,使其与vuex中的数据保持同步即可解决
  },
//state存储值,不建议在这里面进行更改,要更改使用mutations来定义方法进行更改
  mutations: {
    setRightList(state, data) {
      state.rightList = data
      sessionStorage.setItem('rightList', JSON.stringify(data))
    },
    setUsername(state, data) {
      state.username = data
      sessionStorage.setItem('username', data)
    }
  },
  actions: {
  },
  modules: {
  }
})

login代码:

//登陆成功后的回调中进行返回信息的存储
              this.$store.commit("setRightList", res.right);
              this.$store.commit("setUsername", res.data.username);
              sessionStorage.setItem("token", res.data.token);
//token就不需要存储在vuex中,只需要存储在本地即可,在页面需要退出登陆时清空本地存储的值,进行路由的跳转并刷新页面即可将本地存储与vuex中存储的值清空

index代码:

import { mapState } from "vuex";
//引入mapState函数并对vuex中的数据做一个映射
created() {
    this.menulist = this.rightList;
    this.msg=this.username
    //映射出来的值直接赋值使用即可
  },
   computed: {
    ...mapState(['rightList', 'username']),
  },
//退出登陆清空vuex与本地存储的数据,在退出登陆的方法中写入一下即可:
logout() {
      sessionStorage.clear();
      setTimeout(() => {
        this.$router.push("/login");
        this.$message.success("退出登陆成功");
      }, 1000);
    //设置延迟执行函数为了用户体验
    },

2.界面的控制

  • 未登录直接访问
    如何判断用户是否登陆?(token)
    前面已经进行token的存储
    什么时机进行判断?(路由导航守卫)
    router.js代码:
router.beforeEach((to, from, next) => {
 if (to.path === '/login'||to.path==='/register') {
 //如果是跳转到登陆或者注册是不用进行判断
   next()
 } else {
   const token = sessionStorage.getItem('token')
   if(!token) {
   //跳转除登陆注册组件时进行token的判断,如果不存在跳转登陆页面进行登陆
     next('/login')
   } else {
     next()
   }
 }
})
  • 用户已经登陆但是在地址栏输入不具备权限的页面仍然可以访问!
    解决:
    (1).使用路由导航守卫
    固然是可以的,可以在每次地址栏变化时从vuex中取出right进行判断用户将要访问的界面这个用户是否有权限进行访问。
    如果用户不具备权限的路由是否应该是不存在的??
    (2).动态路由的使用
    router.js代码:
import Users from '@/components/user/Users.vue'
import Roles from '@/components/role/Roles.vue'
import GoodsCate from '@/components/goods/GoodsCate.vue'
import GoodsList from '@/components/goods/GoodsList.vue'
import store from '@/store'
//引入组件
const userRule = { path: '/users', component: Users }
const roleRule = { path: '/roles', component: Roles }
const goodsRule = { path: '/goods', component: GoodsList }
const categoryRule = { path: '/categories', component: GoodsCate }
//声明变量与vuex中的二级权限的path进行映射关系
const ruleMapping = {
  'users': userRule,
  'roles': roleRule,
  'goods': goodsRule,
  'categories': categoryRule
}

export function initDynamicRoutes() {
//根据二级权限对路由规则的动态添加
  console.log(router)  //这个是路由下的所有路由规则,打印出来更清晰的使用里面的方法
  const currentRoutes = router.options.routes
  const rightList = store.state.rightList
  rightList.forEach(item => {
    item.children.forEach(item => {
      //对二级权限的路由遍历和动态添加
      const itemRule = ruleMapping[item.path]
      itemRule.meta = item.rights
      //对路由新建规则并赋值,返回数据的按钮权限
      currentRoutes[2].children.push(itemRule)
    })
  })
  router.addRoutes(currentRoutes)
}

注意BUG:此处写完路由文件后,页面刷新为空
原因:页面刷新,路由重新加载,路由规则变成空,动态路由不存在了
解决:登陆后添加动态路由,可以放在根组件App.vue中
App.vue代码:

import { initDynamicRoutes } from '@/router.js'
 created() {
    initDynamicRoutes()
  }
//在组件创建时执行这个方法即可解决

login.vue代码:

import { initDynamicRoutes } from '@/router.js'
//登陆成功后调用下这个方法即可
initDynamicRoutes()

3.按钮的控制
原理:根据返回的数据存储在子路由的权限(增删改查)
实现: 使用自定义指令来实现
在src目录下的utils新建permission文件,并且在main.js中调用
permission.js代码:

import Vue from 'vue'
import router from '@/router.js'
Vue.directive('permission', {
//注册自定义指令'v-permission'
  inserted: function (el, binding) {
  //当指令插入元素调用时,可以传递两个值:el:指令所在的元素;binding:一个对象,包含当前元素的propety属性
    console.log(el)
    console.log(binding)
    const action = binding.value.action
     //此按钮指令中的值
    const currentRight = router.currentRoute.meta
    //此变量为路由动态添加后的权限的值
    console.log(currentRight)
    if (currentRight) {
      if (currentRight.indexOf(action) === -1) {
        // 不具备权限,按钮应该为禁用或者不存在
        const type = binding.value.effect
        if (type === 'disabled') {
          el.disabled = true  //激活禁用
          el.classList.add('is-disabled')
        } else {
          el.parentNode.removeChild(el)   //删除
        }
      }
    }
  }
})

页面中使用:

删除
//注:如果存在effect:'disabled表示为禁用状态,否则为当前按钮为删除状态,不显示

4.请求响应的控制

  • 请求的控制
    (1)除了登陆注册外的请求都要携带token,用于数据库鉴别身份
    实现:设置请求头
    (2)如果发出了非权限内的请求,应该在前端进行阻断
    原理:根据请求方式的映射关系进行判断
    请求方式:
    查看 get请求
    增加 post请求
    修改 put请求
    删除 delete请求
    http.js代码:
import router from '../router'
const actionMapping = {
  get: 'view',
  post: 'add',
  put: 'edit',
  delete: 'delete'
}
axios.interceptors.request.use(function (req) {
  const currentUrl = req.url
  if (currentUrl !== 'login') {
    req.headers.Authorization = sessionStorage.getItem('token')
    // 当前模块中具备的权限
    const method = req.method
    // 根据请求, 得到是哪种操作
    const action = actionMapping[method]
    // 判断action是否存在当前路由的权限中
    const rights = router.currentRoute.meta
    if (rights && rights.indexOf(action) == -1) {
      // 没有权限
      alert('没有权限')
      return Promise.reject(new Error('没有权限'))
    }
  }
  return req
})
  • 响应的控制
    如果得到服务器的返回状态码为401,代表token超时或被篡改,此时应跳转到登陆页面
    http.js代码:
axios.interceptors.response.use(function (res) {
  if (res.data.meta.status === 401) {
    router.push('/login')
    sessionStorage.clear()
    window.location.reload()
  }
  return res
})

你可能感兴趣的:(Vue后台管理系统权限控制与管理)