The front-end implements dynamic routing (the front-end controls all routes, and the back-end returns user roles)

insert image description here

Advantages and disadvantages

advantage:

  • The routing table is maintained on the front end without the help of the back end
  • The logic is relatively simple and easy to use
  • Front-end authentication is more convenient for systems with fewer permissions

shortcoming:

  • Every time the permission page is modified in the online version, the project needs to be repackaged
  • Not suitable for large projects
  • If you need to add roles to the page and control the pages that can be accessed, you cannot use front-end authentication

Specific ideas

1. The front end defines static routing and dynamic routing. When creating a vue instance, vue-router mounts static routing (pages that do not require permissions such as login) 2. Obtain user
information when logging in, store it in vuex, and store token
3. In routing In the interceptor, use the token to obtain the user role, and use the role to obtain all accessible routes
4. Call router.addrouters(store.state.addRouters) to add accessible routes
5. Clear user information when exiting, clear roles, and clear routing

Step 1: Define static routing and dynamic routing on the front end

router/index.js

import Vue from "vue"
import VueRouter from "vue-router"
import Layout from "@/layout"

Vue.use(VueRouter)

// 解决重复点击路由报错的BUG
// 下面这段代码主要解决这个问题 :Uncaught (in promise) Error: Redirected when going from "/login" to "/index" via a navigation guard.
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
    
    
  return originalPush.call(this, location).catch((err) => err)
}

// 定义好静态路由
export const constantRoutes = [
  {
    
    
    path: "/login",
    name: "login",
    component: () => import("../views/login"),
    hidden: true,
  },
]

// 定义动态路由,以及每个页面对应的roles(写在meta中,不写代表都可以访问)
export const asyncRoutes = [
  {
    
    
    id: 1,
    name: "/",
    path: "/",
    component: Layout,
    redirect: "/index",
    hidden: false,
    children: [
      {
    
    
        name: "index",
        path: "/index",
        meta: {
    
     title: "index" },
        component: () => import("@/views/index"),
      },
    ],
  },
  {
    
    
    id: 2,
    name: "/form",
    path: "/form",
    component: Layout,
    redirect: "/form/index",
    hidden: false,
    children: [
      {
    
    
        name: "/form/index",
        path: "/form/index",
        meta: {
    
     title: "form" },
        component: () => import("@/views/form"),
      },
    ],
  },
  {
    
    
    id: 3,
    name: "/example",
    path: "/example",
    component: Layout,
    redirect: "/example/tree",
    meta: {
    
     title: "example" },
    hidden: false,
    children: [
      {
    
    
        name: "/tree",
        path: "/example/tree",
        meta: {
    
     title: "tree" },
        component: () => import("@/views/tree"),
      },
      {
    
    
        name: "/copy",
        path: "/example/copy",
        meta: {
    
     title: "copy" },
        component: () => import("@/views/tree/copy"),
      },
    ],
  },
  {
    
    
    id: 4,
    name: "/table",
    path: "/table",
    component: Layout,
    redirect: "/table/index",
    hidden: false,
    meta: {
    
     roles: ["admin"] },
    children: [
      {
    
    
        name: "/table/index",
        path: "/table/index",
        meta: {
    
     title: "table", roles: ["admin"] },
        component: () => import("@/views/table"),
      },
    ],
  },
  {
    
    
    id: 5,
    name: "/admin",
    path: "/admin",
    component: Layout,
    redirect: "/admin/index",
    hidden: false,
    meta: {
    
     roles: ["admin"] },
    children: [
      {
    
    
        name: "/admin/index",
        path: "/admin/index",
        meta: {
    
     title: "admin", roles: ["admin"] },
        component: () => import("@/views/admin"),
      },
    ],
  },
  {
    
    
    id: 6,
    name: "/people",
    path: "/people",
    component: Layout,
    redirect: "/people/index",
    hidden: false,
    meta: {
    
     roles: ["admin", "common_user"] },
    children: [
      {
    
    
        name: "/people/index",
        path: "/people/index",
        meta: {
    
     title: "people", roles: ["admin", "common_user"] },
        component: () => import("@/views/people"),
      },
    ],
  },
  {
    
    
    id: 7,
    name: "/404",
    path: "/404",
    component: () => import("@/views/404"),
  },
  // 注意404页面要放到最后
  {
    
     path: "*", redirect: "/404", hidden: true },
]

const router = new VueRouter({
    
    
  mode: "history",
  base: process.env.BASE_URL,
  routes: constantRoutes,
})

export default router

metaHere we use tags to indicate the permissions that the modified page can access according to the method officially recommended by vue-router . For example, meta: { role: ['admin', 'super_editor'] } indicates that only admins and super editors are eligible to enter this page.

注意事项: One thing to pay attention to here is that the 404 page must be loaded last. If you declare 404 together with constantRoutes, all subsequent pages will be blocked to 404

Step 2: Obtain user information when logging in, store it in vuex, and store token

login/index.vue

methods: {
    
    
    login () {
    
    
      this.$refs.userForm.validate((valid) => {
    
    
        if (valid) {
    
    
          // 模拟登录接口去请求用户数据
          setTimeout(() => {
    
    
            // 这里的res就是模拟后台返回的用户数据(不包含用户角色,一般角色是由单独的一个接口返回)
            const res = dynamicUserData.filter((item) => item.username === this.user.username)[0]
            console.log(res)
            // 存储用户的信息及token到vuex,并做sessionStorage持久化处理
            this.$store.commit('User/saveUserInfo', res)
            Message({
    
     type: 'success', message: "登录成功", showClose: true, duration: 3000 })
            this.$router.push({
    
     path: "/index" })
          }, 1000)
        } else return false
      })
    }
  }

Attachment: vuex persistence processing

import Vue from 'vue'
import Vuex from 'vuex'
import User from './modules/user'
import permission from './modules/permission'
import createPersistedState from 'vuex-persistedstate'
Vue.use(Vuex)

export default new Vuex.Store({
    
    
  state: {
    
    },
  mutations: {
    
    },
  actions: {
    
    },
  modules: {
    
    
    User,
    permission,
  },
  plugins: [
    createPersistedState({
    
    
      storage: window.sessionStorage, // 可选sessionStorage localStorage
      reducer(val) {
    
    
        return {
    
    
          User: val.User,
        }
      },
    }),
  ],
})

Step [34]: In the route interceptor, use the token to obtain the user role, use the role to obtain all accessible routes, and call router.addrouters(store.state.addRouters) to add accessible routes

Routing hook logic:

是否为白名单页面
  是:   直接进入
  不是: 判断是否有token
  			无token:跳转到login登录页
  			有token: 判断用户是否有角色权限表
  						有权限表:直接进入
  						无权限表:调接口获取用户角色,并存储到vuex
  								根据返回的角色和路由表每个页面的需要的权限对比,生成可访问的路由表
  								使用router.addRouters()添加路由

Routing navigation guard:

import router from "./index"
import NProgress from "nprogress" // progress bar
import store from "@/store"
import menu from "@/mock/menu.js"

NProgress.configure({
    
     showSpinner: false }) // NProgress Configuration

// 白名单页面直接进入
const whiteList = ["/login"]

router.beforeEach((to, from, next) => {
    
    
  NProgress.start()
  // 白名单页面,不管是否有token,是否登录都直接进入
  if (whiteList.indexOf(to.path) !== -1) {
    
    
    next()
    return false
  }
  // 有token(代表了有用户信息,但是不确定有没有角色权限数组)
  if (store.state.User.token) {
    
    
    // 判断当前用户是否有角色权限数组, 是登录状态则一定有路由,直接放行,不是登录状态则去获取路由菜单登录
    // 刷新时hasRoles会重置为false,重新去获取 用户的角色列表
    const hasRoles = store.state.permission.roles && store.state.permission.roles.length > 0
    if (!hasRoles) {
    
    
      setTimeout(async () => {
    
    
        const roles = menu.filter((item) => item.token === store.state.User.token)[0].roles
        // 将该角色权限数组存储到vuex中
        store.commit("permission/setRoles", roles)
        // 根据返回的角色信息去过滤异步路由中该角色可访问的页面
        const accessRoutes = await store.dispatch("permission/generateRoutes", roles)
        // dynamically add accessible routes
        router.addRoutes(accessRoutes)
        // hack方法 router.addRoutes之后的next()可能会失效,因为可能next()的时候路由并没有完全add完成 next(to)解决
        next({
    
     ...to, replace: true })
      }, 500)
    } else {
    
    
      next() //当有用户权限的时候,说明所有可访问路由已生成 如访问没权限的全面会自动进入404页面
    }
  } else {
    
    
    next({
    
     path: "/login" })
  }
})

router.afterEach(() => {
    
    
  // finish progress bar
  NProgress.done()
})

What to do in vuex is: filter the defined dynamic routes through the role permission array (returned in the background), filter out the routes that the user has, and then add the filtered routes to the back of the static route to store/
permission.js

import {
    
     asyncRoutes, constantRoutes } from '@/router'
/**
 * Filter asynchronous routing tables by recursion
 * @param routes asyncRoutes
 * @param roles
 */
export function filterAsyncRoutes(routes, roles) {
    
    
  const res = []
  routes.forEach(route => {
    
    
    const tmp = {
    
     ...route }
    if (hasPermission(roles, tmp)) {
    
    
      if (tmp.children) {
    
    
        tmp.children = filterAsyncRoutes(tmp.children, roles)
      }
      res.push(tmp)
    }
  })
  return res
}

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

const state = {
    
    
  roles: [],
  routes: [],
  addRoutes: [],
}
const mutations = {
    
    
  setRoles(state, val) {
    
    
    state.roles = val
  },
  SET_ROUTES: (state, routes) => {
    
    
    state.addRoutes = routes
    state.routes = constantRoutes.concat(routes)
  },
}
const actions = {
    
    
  generateRoutes({
     
      commit }, roles) {
    
    
    return new Promise(resolve => {
    
    
      let accessedRoutes
      if (roles.includes('admin')) {
    
     // admin直接添加所有权限
        accessedRoutes = asyncRoutes || []
      } else {
    
    
        accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
      }
      commit('SET_ROUTES', accessedRoutes)
      resolve(accessedRoutes)
    })
  },
}
export default {
    
    
  namespaced: true,
  state,
  mutations,
  actions,
}

Step 4: Clear user information, roles, and routes when exiting

methods: {
    
    
    // 退出登录
    handleLogout() {
    
    
      window.localStorage.removeItem("token")
      // 清除用户信息
      this.$store.commit("User/removeUserInfo")
      // 清除角色权限列表
      this.$store.commit("permission/setRoles", [])
      // 清除角色权限数组
      this.$store.commit("permission/SET_ROUTES", [])
      Message({
    
    
        type: "success",
        message: "退出登录",
        showClose: true,
        duration: 3000,
      })
      this.$router.push({
    
     path: "/login" })
    },
  }

hope this helps

Article reference:
Mr. Hua Pants: Mr. Hua Pants
Code of this article: github for star

Guess you like

Origin blog.csdn.net/weixin_44582045/article/details/132576546