Giter Club home page Giter Club logo

vue-admin's Introduction

前言

总结一种比较常见的权限控制方法,主要使用vue-router的addRoutes根据用户权限动态添加路由实现。本文主要在前端对权限进行配置。

主要知识点

  • vue
  • vue-router
  • vuex
  • vue-cli

基本项目项目结构

使用vue-cli生成项目

  • 目录结构
├─public
│      favicon.ico
│      index.html
│      
└─src
    │  App.vue
    │  main.js
    │  
    ├─assets
    │      logo.png
    │      
    ├─components
    │      HelloWorld.vue
    │      
    ├─router
    │      index.js
    │      
    ├─store
    │  │  index.js
    │  │  
    │  └─modules
    │          permission.js
    │          user.js
    │          
    └─views
            About.vue
            Home.vue
            Login.vue
│  babel.config.js
│  package.json
  • 主要文件
  1. src/router/index.js
  2. src/store/user.js
  3. src/store/permission.js

权限设计基本原理

  • 基本原理
根据后台返回的用户角色信息在前端配置当前用户可以访问的路由,然后通过addRoutes动态添加。
  • 后台返回的用户信息
{
    username:"张三",
    role:["admin","kefu"] //用户的角色
}

实现

配置router(src/router/index.js)

  1. routes

配置不需要权限的基础路由如登陆页面,404页面等。如:

export const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('../views/Home.vue'),
  },
  {
    path: '/404',
    name: '404',
    component: () => import('../views/404.vue'),
    hidden: true, // 为true时在页面导航中隐藏
  }
]
  1. asyncRoutes

配置需要权限的路由,将可访问路由的角色信息配置到路由的meta属性中,如:

export const asyncRoutes = [
    {
        path: '/page1',
        name: 'Page1',
        component: () => import('../views/RolePage1.vue'),
        meta: { roles: ['admin'] } // 表示拥有admin角色的用户可以访问当前路由
        meta: { roles: ['admin','kefu'] } // 表示拥有admin和kefu角色的用户可以访问当前路由
    },
    { path: '*', redirect: '/404', hidden: true } // 配置重定向404页面
]
  1. 注意

如果需要配置重定向404页面的话,需要配置在asyncRoutes的最后

  1. 完整示例
import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)
/**
 * routes中设置基础路由
 * asyncRoutes中设置需要根据角色权限动态加载的页面路由
 */
export const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('../views/Home.vue'),
  },
  {
    path: '/404',
    name: '404',
    component: () => import('../views/404.vue'),
    hidden: true, // 为true时在页面导航中隐藏
  }
]

export const asyncRoutes = [
  {
    path: '/page1',
    name: 'Page1',
    component: () => import('../views/RolePage1.vue'),
    meta: { roles: ['super', 'admin'] }
  },
  {
    path: '/page2',
    name: 'Page2',
    component: () => import('../views/RolePage2.vue'),
    meta: { roles: ['super', 'kefu'] }
  },
  {
    path: '/page3',
    name: 'Page3',
    component: () => import('../views/RolePage3.vue'),
    meta: { roles: ['super', 'kefu'] }
  },
  { path: '*', redirect: '/404', hidden: true }
]


export default new VueRouter({
  routes
})

编写permission的module(src/store/modules/permission)

  1. 作用

根据用户角色和配置的路由生成当前用户可以访问的路由配置

  1. 主要函数

getAsyncRoutes获取生成的路由,hasPermission判断是否有,GenerateRoutes根据角色和asyncRoutes生成需要添加的路由

  • getAsyncRoutes
/**根据角色获取路由配置,并保存用户角色 */
getAsyncRoutes({ commit }, roles) {
  let filterRoutes = GenerateRoutes(asyncRoutes, roles)
  let res = routes.concat(filterRoutes)
  commit('SET_ROUTES', res)
  return res
}
  • GenerateRoutes
/**
 * 根据角色和配置生成当前用户的路由
 * @param {array} routes 配置的路由
 * @param {array} roles 用户角色
 */
let GenerateRoutes = (routes, roles) => {
  let res = []
  routes.forEach(route => {
    const tmp = { ...route }
    if (hasPermission(roles, tmp)) {
      if (tmp.children) {
        tmp.children = GenerateRoutes(tmp.children, roles)
      }
      res.push(tmp)
    }
  })
  return res
}
  • hasPermission
/**
 * 通过meta中的roles信息判断用户是否有权限
 * @param roles
 * @param route
 */
function hasPermission(roles, route) {
  if (route.meta && route.meta.roles) {
    return roles.some(role => route.meta.roles.includes(role))
  } else {
    return true
  }
}
  1. 完整示例
import { routes, asyncRoutes } from '../../router/index.js'

/**
 * 通过meta中的roles信息判断用户是否有权限
 * @param roles
 * @param route
 */
function hasPermission(roles, route) {
  if (route.meta && route.meta.roles) {
    return roles.some(role => route.meta.roles.includes(role))
  } else {
    return true
  }
}

/**
 * 根据角色和配置生成当前用户的路由
 * @param {array} routes 配置的路由
 * @param {array} roles 用户角色
 */
let GenerateRoutes = (routes, roles) => {
  let res = []
  routes.forEach(route => {
    const tmp = { ...route }
    if (hasPermission(roles, tmp)) {
      if (tmp.children) {
        tmp.children = GenerateRoutes(tmp.children, roles)
      }
      res.push(tmp)
    }
  })
  return res
}
const permission = {
  state: {
    roles: [],
    routes: routes // 用于配置页面导航等
  },
  mutations: {
    SET_ROLES: (state, roles) => {
      state.roles = roles
    },
    SET_ROUTES: (state, routes) => {
      state.routes = routes
    }
  },
  actions: {
    /**根据角色获取路由配置 */
    getAsyncRoutes({ commit }, roles) {
      commit('SET_ROLES', roles) // 保存roles信息到store中
      let filterRoutes = GenerateRoutes(asyncRoutes, roles)
      let res = routes.concat(filterRoutes)
      commit('SET_ROUTES', res)
      return res
    }
  }
}
export default permission

编写全局路由钩子

  1. 逻辑流程图

2. 实现

const whiteList = ['/login', '/404'] // 免登录白名单

router.beforeEach(async (to, from, next) => {
  let token = localStorage.getItem('JWT_TOKEN')
  // 判断登录状态
  if (token) {
    if (to.path === '/login') {
      next({ path: '/' })
    } else {
      let hasRoles = store.state.user.roles && store.state.user.roles.length > 0
      if (!hasRoles) {
        let userInfo = await store.dispatch('getUser')
        let routes = await store.dispatch('getAsyncRoutes', userInfo.roles)
        router.addRoutes(routes)
        next({ ...to, replace: true }) // 保证路由已挂载
      } else {
        next()
      }
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
      next()
    } else {
      next(`/login?redirect=${to.path}`) // 否则全部重定向到登录页
    }
  }
})
  1. 注意
  • 需要判断store中是否已经保存了roles,否则会死循环
  • 添加新的路由后使用next({ ...to, replace: true })保证路由已挂载完成

vue-admin's People

Stargazers

 avatar 杨新龙 avatar  avatar

Watchers

James Cloos avatar 杨新龙 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.