后臺管理權(quán)限

前端權(quán)限:因為前端本來就是不安全的,真正的安全還是需要后端去把關(guān),所以后端也必須按做權(quán)限控制!我們前端的權(quán)限校驗主要的目的是過濾不該有的請求和操作,減少服務(wù)端壓力
一般來說前端權(quán)限在三個方面:接口權(quán)限、按鈕權(quán)限,路由權(quán)限(菜單權(quán)限)

菜單權(quán)限實現(xiàn)步驟:

1.拆分動態(tài)路由和靜態(tài)路由

原因:由于不同用戶登錄以后應(yīng)該有不同的權(quán)限,可以看到不同的內(nèi)容,因此需要給不同的用戶在菜單上分配不同的權(quán)限。
菜單權(quán)限最重要的一點就是:當(dāng)獲取用戶信息的時候,服務(wù)器會把響應(yīng)的用戶擁有菜單的權(quán)限信息返回,需要根據(jù)用戶身份對比出,當(dāng)前這個用戶需要展示哪些菜單。
靜態(tài)路由:不管用戶是什么角色,都可以看到的路由,比如登錄、首頁、404;
動態(tài)路由:不同的用戶需要過濾選出的路由;

//靜態(tài)路由
import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

import Layout from '@/layout'
// 靜態(tài)路由:不管是什么角色都可以看見:登錄頁面,404,首頁
export const constantRoutes = [
  {
    path: '/login',
    component: () => import('@/views/login/index'),
    hidden: true
  },
  {
    path: '/404',
    component: () => import('@/views/404'),
    hidden: true
  },
  {
    path: '/',
    component: Layout,
    // 重定向
    redirect: '/dashboard',
    children: [
      {
        path: 'dashboard',
        name: 'Dashboard',
        component: () => import('@/views/dashboard/index'),
        meta: { title: '首頁', icon: 'dashboard' }
      }
    ]
  },

  // 404 page must be placed at the end !!!
  { path: '*', redirect: '/404', hidden: true }
]
// 動態(tài)路由:不同角色需要過濾篩選出的路由
export const asyncRoutes = [
  // 權(quán)限路由
  {
    name: 'Acl',
    path: '/acl',
    component: Layout,
    redirect: '/acl/user/list',
    meta: {
      title: '權(quán)限管理',
      icon: 'el-icon-lock'
    },
    children: [
      {
        name: 'User',
        path: 'user/list',
        component: () => import('@/views/acl/user/list'),
        meta: {
          title: '用戶管理'
        }
      }
    ]
  },
  {
    path: '/product',
    component: Layout,
    name: 'Product',
    // title:左側(cè)側(cè)邊欄名稱 icon小圖標(biāo)
    meta: { title: '商品管理', icon: 'el-icon-goods' },
    children: [
      {
        path: 'trademark',
        name: 'TradeMark',
        component: () => import('@/views/product/tradeMark'),
        meta: { title: '品牌管理' }
      },
     ]
    },
  {
    path: '/test',
    component: Layout,
    name: 'Test',
    // title:左側(cè)側(cè)邊欄名稱 icon小圖標(biāo)
    meta: { title: '測試管理', icon: 'el-icon-goods' },
    children: [
      {
        path: 'test1',
        name: 'test1',
        component: () => import('@/views/Test/Test1'),
        meta: { title: '測試管理1' }
      },

2.分析登錄業(yè)務(wù)中的內(nèi)容

<el-button  type="primary" @click.native.prevent="handleLogin">登錄</el-button>

// 登錄業(yè)務(wù):發(fā)請求,帶著用戶名和密碼給服務(wù)器(成功與失?。?    handleLogin() {
      // 驗證表單元素(用戶名與密碼)的是否符合規(guī)則
      this.$refs.loginForm.validate(async valid => {
        // 如果符合驗證規(guī)則
        if (valid) {
          // 按鈕會有一個loading效果
          this.loading = true
          // 派發(fā)一個action:user/login,帶著用戶名和密碼的載荷
          await this.$store.dispatch('user/login', this.loginForm)
          // 登陸成功進行路由跳轉(zhuǎn)
          this.$router.push({ path: this.redirect || '/' })
          // loading效果結(jié)束
          this.loading = false
        } else {
          console.log('error submit!!')
          return false
        }
      })
    }

用戶信息模塊,主要功能:
1、登錄獲取token,存儲到Cookies中
2、獲取用戶信息,存儲到Vuex中

import { login, logout, getInfo } from '@/api/user'
// 獲取token|設(shè)置token|刪除token的函數(shù)
import { getToken, setToken, removeToken } from '@/utils/auth'
// 引入路由中重置路由的方法
import { resetRouter, asyncRoutes, constantRoutes, anyRoutes } from '@/router'
import router from '@/router'
import cloneDeep from 'lodash/cloneDeep'

const getDefaultState = () => {
  return {
    // 獲取token
    token: getToken(),
    // 存儲用戶名
    name: '',
    // 存儲用戶頭像
    avatar: '',
    // 存儲菜單標(biāo)記(根據(jù)不同的角色,返回的標(biāo)記信息,數(shù)組里面的元素是字符串)
    routes: [],
    // 存儲角色
    roles: [],
    // 按鈕權(quán)限
    buttons: [],
    // 項目中已有的異步路由和服務(wù)器返回的標(biāo)記信息對比之后,最終需要展示的路由
    resultAsyncRoutes: [],
    // 用戶最終需要展示的全部路由
    resultAllRoutes: []
  }
}
const state = getDefaultState()
const mutations = {
// 存儲token
  SET_TOKEN: (state, token) => {
    state.token = token
  },
  // 存儲用戶信息
  SET_USERINFO: (state, userInfo) => {
    // 用戶名
    state.name = userInfo.name
    // 用戶頭像
    state.avatar = userInfo.avatar
    // 菜單權(quán)限的標(biāo)記
    state.routes = userInfo.routes
    // 按鈕權(quán)限的標(biāo)記
    state.button = userInfo.button
    // 角色
    state.roles = userInfo.roles
  },
}
const actions = {
  // user login
  // 處理登陸的業(yè)務(wù)
  async login({ commit }, userInfo) {
    // 解構(gòu)出用戶名和密碼
    const { username, password } = userInfo
    const result = await login({ username: username.trim(), password: password })
    // 注意:當(dāng)前登陸的請求 使用的是mock數(shù)據(jù),mock數(shù)據(jù)code是20000
    if (result.code === 20000) {
      // vuex存儲token,修改state必須通過mutations
      commit('SET_TOKEN', result.data.token)
      // 本地持久化存儲token
      setToken(result.data.token)
      return 'ok'
    } else {
      return Promise.reject(new Error('faile'))
    }
},
// 獲取用戶信息
getInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
      getInfo(state.token).then((response) => {
        // 獲取用戶信息:返回數(shù)據(jù)包括用戶名,用戶頭像,routes(返回的標(biāo)志:不同的用戶應(yīng)該展示哪些菜單的標(biāo)記)
        // (roles:用戶角色信息)(button:按鈕信息,按鈕權(quán)限使用的標(biāo)記)

        const { data } = response
        // vuex存儲用戶的全部信息
        commit('SET_USERINFO', data)
        // 提交數(shù)據(jù)
        commit('SET_RESULTASYNCROUTES', computedAsyncRoutes(cloneDeep(asyncRoutes), data.routes))
        resolve(data)
      })
        .catch((error) => {
          reject(error)
        })
    })
  }

下載cookie插件

import Cookies from 'js-cookie'

const TokenKey = 'vue_admin_template_token' // 設(shè)置一個獨一無二的key

export function getToken() {
  return Cookies.get(TokenKey) //get獲取token
}

export function setToken(token) {
  return Cookies.set(TokenKey, token) // set設(shè)置token
}

export function removeToken() {
  return Cookies.remove(TokenKey) //remove移除token
}

3.在全局路由前置守衛(wèi)中判斷是否有token,要訪問的是否是登錄頁面,是否有用戶信息

// 全局前置守衛(wèi)
router.beforeEach(async(to, from, next) => {
  //  開啟進度條
  NProgress.start()

  document.title = getPageTitle(to.meta.title)

  const hasToken = getToken()
  // 如果有token
  if (hasToken) {
    // 如果要訪問的是登錄頁面
    if (to.path === '/login') {
      // 跳到主頁,有token不用處理
      next({ path: '/' })
      NProgress.done()
    } else {
      const hasGetUserInfo = store.getters.name
      // 如果有用戶信息
      if (hasGetUserInfo) {
        next()
      } else {
        try {
          // 獲取用戶信息
          await store.dispatch('user/getInfo')

          next()
        } catch (error) {
          // 移除token
          await store.dispatch('user/resetToken')
          Message.error(error || 'Has Error')
          next(`/login?redirect=${to.path}`)
          NProgress.done()
        }
      }
    }
  } else {
    // 沒有token

    if (whiteList.indexOf(to.path) !== -1) {
      // 在白名單登錄頁面中,直接進入
      next()
    } else {
      next(`/login?redirect=${to.path}`)
      NProgress.done()
    }
  }
})

4.對比異步路由,相同則展示對應(yīng)路由

// 定義一個函數(shù):兩個數(shù)組進行對比,對比出當(dāng)前用戶顯示哪些異步路由
const computedAsyncRoutes = (asyncRoutes, routes) => {
  // 過濾出當(dāng)前用戶(超級管理員,普通員工)需要展示的異步路由
  return asyncRoutes.filter(item => {
    // 數(shù)組中沒有這個元素返回索引值-1,如果有,返回的索引值一定不是-1
    if (routes.indexOf(item.name) !== -1) {
      // 遞歸:還有2,3,4,級多級路由的情況
      if (item.children && item.children.length) {
        item.children = computedAsyncRoutes(item.children, routes)
      }
      return true
    }
  })
}

5.菜單權(quán)限設(shè)置成功

const mutations = {
  // 最終計算出來的異步路由
  SET_RESULTASYNCROUTES: (state, asyncRoutes) => {
    // vuex保存當(dāng)前用戶的異步路由,用戶需要展示的完整路由包括常量,異步,任意路由
    state.resultAsyncRoutes = asyncRoutes
    // 計算出當(dāng)前用戶需要展示的所有路由,合并
    state.resultAllRoutes = constantRoutes.concat(state.resultAsyncRoutes, anyRoutes)
    // 給路由器添加新的路由
    router.addRoutes(state.resultAllRoutes)
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容