使用vue开发项目时进行权限判断

使用vue开发项目时进行权限判断

本文通过使用vue-element-admin以及iview-admin整理得出,侵删。

路由表

  1. 路由表
    路由目录结构:
    在这里插入图片描述
    systemUser.js
//封装好的localstorge方法
import { getLocalStorage } from '../../utils/auth'

//声明两个变量
var loginPath = ''
var loginPathPlus = ''

//获取用户信息,通过用户不同权限控制重定向的路由
if(getLocalStorage('userInfo')){
  if(getLocalStorage('userInfo').roles[0] == 'admin'){
    loginPath = '/xxx'
    loginPathPlus = '/ttt'
  }else if(getLocalStorage('userInfo').roles[0] == 'tourist'){
    loginPath = 'yyy'   
  }
}

//创建路由
const asyncRoutes = [
    {
      path: '/',
      // 这个地方有需求进行重定向不同页面可以进行引用变量
      redirect: loginPath,
      component: () => import('@/layout/index'),
      children:[
        {
          path: '/xxx',
          //进入后如若还需进行权限判断展示不同页面再次引用另一个变量
          redirect: loginPathPlus,
          name: 'xxx',
          component: () => import('@/views/common/xxx/index'),
          //meta对象存放次路由的信息,role为进入或显示此路由需要的权限、title为路由显示时的标题,icon为显示时的icon
          meta: {
            role: ['admin'],
            title:'xxx',
            icon:'iconfont icon-huoche'
          },
          children:[
            {
              path: '/xxx',
              name: 'xxx',
              component: () => import('@/views/common/xxx'),
              meta: {
                role: ['admin']
              },
            },
            {
              path: '/xxx',
              name: 'xxx',
              component: () => import('@/views/common/xxx'),
              meta: {
                role: ['admin']
              },
            }
          ]
        },
        {
          path: '/xxx',
          name: 'xxx',
          redirect: '/xxx',
          component: () => import('@/views/common/xxx/index'),
          meta: {
            role: ['admin'],
            title:'xxx',
            icon:'iconfont icon-huojian'
          },
          children:[
            {
              path: '/xxx',
              name: 'xxx',
              component: () => import('@/views/common/xxx'),
              meta: {
                role: ['admin']
              }
            },
            {
              path: '/xxxl/:id',
              name: 'xxx',
              component: () => import('@/views/common/xxx'),
              meta: {
                role: ['admin']
              }
            }
          ]
        },
        {
          path: '/xxx',
          name: 'xxx',
          component: () => import('@/views/common/xxx'),
          meta: {
            role: ['admin'],
            title:'xxx',
            icon:'iconfont icon-xiaoxi'
          },
        },
        {
          path: '/xxx',
          name: 'xxx',
          component: () => import('@/views/common/xxx'),
          meta: {
            role: ['admin'],
            title:'xxx',
            icon:'iconfont icon-huaban'
          },
        },
        {
          path: '/xxx',
          redirect: 'xxx',
          name: 'shape',
          component: () => import('@/views/common/xxx'),
          meta: {
            role: ['admin'],
            title:'xxx',
            iocn:''
          },
          children:[
            {
              path: '/xxx',
              name: 'xxx',
              component: () => import('@/views/common/xxx'),
              meta: {
                role: ['admin']
              },
            },
            {
              path: '/xxx',
              name: 'xxx',
              component: () => import('@/views/common/xxx'),
              meta: {
                role: ['admin']
              },
            }
          ]
        }
      ]
    }
  ]
//抛出
  export default asyncRoutes

index.js

import Vue from 'vue'
import Router from 'vue-router'
// 引入系统路由
import systemsRouter from './modules/systemUser'
Vue.use(Router)
//建立基本路由
export const publicRoutes = [
  {
    path: '/login',
    name: 'login',
    component: () => import('@/components/login')
  },
  {
    path: '/401',
    name: '401',
    component: () => import('@/components/ErrPage/401')
  },
  {
    path: '/404',
    name: '404',
    component: () => import('@/components/ErrPage/404')
  }
]
const $router =  new Router({
  mode: 'hash', // 开启 history 模式需要服务端支持   history
  scrollBehavior: () => ({ y: 0 }),
  routes: publicRoutes
})
export default $router;
//若有多个路由可以多个引入拼接
var personsRouter = []
export const asyncRoutes = systemsRouter.concat(personsRouter)
  

  1. vuex
    目录结构:
    store目录
    state.js:
export default {
  myToken: '', // 要存储的 token的值
  roles: [],
  userInfo: {}, // 用户信息
  NavWidth:true,  // 二级菜单样式
}

index.js

import Vue from 'vue'
import Vuex from 'vuex'

import state from './state'
import mutations from './mutations'
import actions from './actions'
import getters from './getters'

import app from './modules/app'
import pression from './modules/pression'


Vue.use(Vuex)

export default new Vuex.Store({
  modules: {
    app,
    pression
  },
  state,
  mutations,
  actions,
  getters
})

getter.js

export default {
  sidebar: state => state.app.sidebar,
  device: state => state.app.device, 
  myToken: state => state.myToken, 
  permission_routes: state => state.pression.routers,
  NavWidth: state => state.NavWidth
}

pression.js

import { publicRoutes, asyncRoutes } from '../../router';

function hasPermission(roles, route) {
  if (route.meta && route.meta.role) {
    return roles.some(role => route.meta.role.indexOf(role) >= 0)
  } else {
    return true
  }
}

var concat_ = function(arr1,arr2){
  //不要直接使用var arr = arr1,这样arr只是arr1的一个引用,两者的修改会互相影响
  var arr = arr1.concat();
  //或者使用slice()复制,var arr = arr1.slice(0)
  for(var i=0;i<arr2.length;i++){
      arr.indexOf(arr2[i]) === -1 ? arr.push(arr2[i]) : 0;
  }
  return arr;
}

const pression = {
  state: {
    routers: publicRoutes,
    addRouters: []
  },
  mutations: {
    SET_ROUTERS: (state, routers) => {
      state.addRouters = routers;
      // state.routers = constantRouter.concat(routers)
      state.routers = concat_(routers,publicRoutes)
    }
  },
  actions: {
    GenerateRoutes ({ commit }, { roles }) {
      const accessedRouters = asyncRoutes.filter(v => {
        if (hasPermission(roles, v)) {
          if (v.children && v.children.length > 0) {
            v.children = v.children.filter(child => {
              if (hasPermission(roles, child)) {
                return child
              }
              return false
            });
            return v
          } else {
            return v
          }
        }
        return false
      })
      commit('SET_ROUTERS', accessedRouters)
    }
  }
}

export default pression

actions.js

/*
  包含间接更新状态数据的一类方法的对象
*/

// md5 加密的库
import crypto from 'crypto'

import { reqGetUserInfo, reqLogin, checkCode } from '../api/common'


import {
  SET_TOKEN_DATA,
  SAVE_USER_ROLES,
} from '../store/mutation-types'

import { setToken, setLocalStorage } from "../utils/auth"

export default {
  async GetUserInfo ({ commit }) {
    const result = await reqGetUserInfo();
    if (result.resultCode === 0) {
      const userInfo = result.data;
      setLocalStorage('userInfo', userInfo)
      commit(SAVE_USER_ROLES, userInfo)
    }
  },
  async Login ({ commit }, reqData) {
    const {account, verificationCode} = reqData
    // md5 加密
    const md5 = crypto.createHash("md5");
    // 需要加密的密码
    md5.update(reqData.password);
    // password 加密完的密码
    reqData.password = md5.digest('hex');
    // 获取异步请求后的数据
    // 首先校验验证码,成功之后在进行登录
    var checkCodeData = {
      account,
      verificationCode
    }
    const res = await checkCode(checkCodeData);
    if(res.resultCode === 0 ){
      const result = await reqLogin(reqData);
      if (result.resultCode === 0) {
        // 取出数据中的 token的值
        const { token } = result.data;
        // 数据获取成功
        setToken(token);
        commit(SET_TOKEN_DATA, { token })
      }
    }else{
      this.$Message.error(res.message);
    }
    
  }
}

  1. 全局路由守卫
    在src目录下创建一个js文件,并在main.js中引入
    main.js
    文件内容如下
import router from './router'
import store from './store'
//封装获取token
import { getToken} from './utils/auth'

// 是否有权限
function hasPermission(roles, permissionRoles) {
  if (roles.indexOf('admin') >= 0) return true; // admin 角色拥有所有权限
  if (!permissionRoles) return true;
  return roles.some(role => permissionRoles.indexOf(role) >= 0)
}


// 白名单,设置不需权限的路由
const whiteList = ['/frontPage', '/login', '/registry', '/servicePage'];

// 全局路由导航卫士
router.beforeEach((to, from, next) => {
//首先获取是否有token
  if(getToken()){
  //如果是登录页面,直接进入
    if(to.path === '/login'){
      next({path:'/'})
    }else{
    //如果有注册或其他不需要权限的页面也直接进入
      if(['/registry'].indexOf(to.path) !== -1){
        next()
      }else{
      //判断是否获取到用户权限
        if (store.state.roles.length === 0) {
        //如果没有则执行方法获取用户信息
          store.dispatch('GetUserInfo')
          .then(() => {
            const roles = store.state.roles;
            store.dispatch('GenerateRoutes', { roles }).then(() => { // 生成可访问的路由表
              // console.log(store.state.pression.addRouters, '路由表');
              router.addRoutes(store.state.pression.addRouters); // 动态添加可访问路由表
              next({ ...to, replace: true }) // hack方法 确保 addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
              // 获取所有路由path
              var Alreadyarr = ['/']
              function FindPath(Routers){
                if(Routers.length>0){
                  Routers.forEach((item,index)=>{
                    if(item.children){
                      FindPath(item.children)
                    }else{
                      return Alreadyarr.push(item.path)
                    }
                  })
                }else{
                  return Alreadyarr.push(Routers.path)
                }
              }
              FindPath(store.state.pression.addRouters)
              //判断如果去的path不在已拥有的路由里就去404页面
              if(Alreadyarr.indexOf(to.path) == -1){
                if((to.path.split('/')[1] == 'cashConcentrationCountDetail' || to.path.split('/')[1] == 'StatementDetail') && to.path.split('/').length>2 && to.path.split('/')[2] !==''){
                  next()
                }else{
                  next({ path: '/404', replace: true, query: { noGoBack: true }})
                }
              }
            })
          })
          .catch(err => {
            console.log(err)
          })
        }else{
        //判断如果取得页面权限是否在用户权限之中
          if (hasPermission(store.state.roles, to.meta.role)) {
            // console.log(store.state.roles, to.meta.role)
            next()
          } else {
            next({ path: '/401', replace: true, query: { noGoBack: true }})
          }
        }
      }
    }
  }else{
    if(whiteList.indexOf(to.path) !== -1){
      next()
    }else{
      next('/login')
    }
  }
  
});

router.afterEach(() => {
  // NProgress.done() // 结束 Progress
});
  1. 登录页面
    此处直接从点击登录按钮之后的逻辑说起
//执行login
this.$store.dispatch('Login', reqData).then(() => {
              this.loading = false;
              this.getUserInfo()
            }).catch(() => {
              this.loading = false
            })
//获取用户信息,根据不同用户权限判断进入不同路由
async getUserInfo () {
        const result = await reqGetUserInfo();
        if (result.resultCode === 0) {
          const userInfo = result.data;
          setLocalStorage('userInfo', userInfo)
          if (userInfo.roles.includes('admin')) {
            // this.$router.replace({ name: 'xxx'});
            this.$router.replace({ name: 'xxx'});
          } else if (userInfo.roles.includes('yyy')) {
            this.$router.replace({ name: 'yyy'})
          } else if (userInfo.roles.includes('zzz')) {
            this.$router.replace({ name: 'zzz'})
          }
        }
      },

5.导航菜单
在项目布局里增加导航,这里在layout里使用iview中的Menu组件来进行

<Menu :active-name='active' theme="dark" width="auto" :class="menuitemClasses">
                    <MenuItem v-for="(item, index) in permission_routes[0].children" :key="index" :name='item.name' :to="item.path">
                        <i :class='item.meta.icon'></i>
                        <span>{{item.meta.title}}</span>
                    </MenuItem>
</Menu>

//获取全局状态里的路由表

...mapGetters([
            'permission_routes'
        ])

这是比较简单易懂的一种
6.二级菜单
二级菜单就比较简单了,可以在二级惨淡页面直接加路由,通过权限判断是否渲染到页面即可,比如这样:

<MenuItem name="Cash_withdrawal_management" to="Cash_withdrawal_management" v-if="CashWithdrawalRight.indexOf(role) !== -1">
             <!-- v-if="CashWithdrawalRight.indexOf(role) !== -1" -->
                提现管理
            </MenuItem>

CashWithdrawalRight是这个页面需要的权限,role是获取到的用户权限,不必担心手动输入路由进入该页面,因为在全局收尾里面已经进行了判断,进入401页面

目前先搞这么多,等会后有新发现在做补充,第一次接触权限问题,特此记录。

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值