Train of thought


  • The login: After the user fills in the account and password, the server checks whether the account and password are correct. After the authentication succeeds, the server returns onetokenAnd gettokenAnd then (I’ll put thistokenStorage tolocalStoreTo ensure that the user login status can be remembered after the page is refreshedtokenGo get another oneuser_infoTo obtain detailed information about the user (such as user permissions, user names, and so on).
  • Permission to verifyThrough:tokenObtain the corresponding userrole, dynamically based on the user’sroleFigure out the route to which it has permission and passrouter.addRoutesMount these routes dynamically.

Route definition


There are two types of routes: constantRoutes and asyncRoutes

ConstantRoutes: Represents routes that do not require dynamic permissions, such as login pages, common pages, and so on.

AsyncRoutes: Represents pages that need to dynamically determine permissions and add them via addRoutes.

To create the router. Js


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

Vue.use(VueRouter);

// Common page: no guard required, can be accessed directly
export const constRoutes = [
  {
    path: "/login".component: () = > import("@/views/Login.vue"),
    hidden: true // The navigation menu ignores this item
  },
  {
    path: "/".component: Layout, // Application layout
    redirect: "/home".alwaysShow: true.meta: {
      title: "Customer Management".// Navigate to menu item titles
      icon:"kehu" // Navigate to menu item ICONS
    },
    children: [{path: "/home".component: () = > import("@/views/Home.vue"),
        name: "home".meta: {
          title: "Customer List"}}]}];// Permission page: the protected page can be accessed only by the role that the user has logged in to
export const asyncRoutes = [
  {
    path: "/system_manage".component: Layout,
    redirect: "/system_set".meta: {
      title: "System Settings".icon: "set"
    },
    children: [{path: "/system_set".component: () = > import("@/views/system_set.vue"),
        name: "system_set".meta: {
          title: "System Settings".roles: ["admin"."editor"] // Set the access permission of the route. Multiple permissions can be added together}}, {path: "/system_organiza".component: () = > import("@/views/system_origaniza.vue"),
        name: "system_origaniza".meta: {
          title: "Organizational structure".roles: ["admin"]},children: [
       
      
          {path:'/custom_link'.name:'custom_link'.component:() = > import("@/views/custom_link.vue"),meta: {title:'Customer Contact'}},
          {path:'/tracking'.name:'tracking'.component:() = > import("@/views/tracking.vue"),meta: {title:'Track record'}}}, {path: "/system_data".component: () = > import("@/views/system_data.vue"),
        name: "system_data".meta: {
          title: "Data dictionary".roles: ["admin"}}];const router = new VueRouter({
  mode: "history".base: process.env.BASE_URL,
  routes: constRoutes
});

export default router;


Copy the code

The login


Create Login page views/ login. vue

<template>
  <div class="container">
    <h2>The user login</h2>
    <input type="text" v-model="username" />
    <button @click="login">The login</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      username: ""
    };
  },
  methods: {
    login() {
      /*this.$store .dispatch("user/login", { username: Enclosing the username}). Then (() = > {this. $router. Push ({/ / accept routing parameters and then jump path: this. $route. The query. The redirect | | "/"}); }) .catch(error => { alert(error); }); * /
        // Call the API to obtain the token}}};</script>

Copy the code

User login status maintenance


Vuex root module implementation,./store/index.js

import Vue from "vue";
import Vuex from "vuex";
import user from "./modules/user";
import permission from "./modules/permission";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  modules: { user, permission },
  getters: {
    roles: state= > {
      returnstate.user.roles; }}});Copy the code

User module – Stores token and roles. /store/modules/user.js

const state = { token: localStorage.getItem("token"), roles: [] }; const mutations = { SET_TOKEN: (state, token) => { state.token = token; }, SET_ROLES: (state, roles) => { state.roles = roles; }}; const actions = { login({ commit }, userinfo) { const { username } = userinfo; return new Promise((resolve, reject) => { setTimeout(() => { if (username === "admin" || username === "jerry") { commit("SET_TOKEN", username); localStorage.setItem("token", username); resolve(); } else {reject(" username, password error "); }}, 1000); }); }, getInfo({ commit, state }) { return new Promise(resolve => { setTimeout(() => { const roles = state.token === "admin" ? ["admin"] : ["editor"]; commit("SET_ROLES", roles); resolve(roles); }, 1000); }); }}; export default { namespaced: true, state, mutations, actions };Copy the code

Routing guard


Create. / SRC/permission. Js

import router from "./router";
import store from "./store";

const whiteList = ["/login"]; // No token whitelist is required

router.beforeEach(async (to, from, next) => {
  //to and from are Route Object,next() must be called to resolve the hook
  // Get the token to determine whether the user is logged in
  const hasToken = localStorage.getItem("token");
  if (hasToken) {
    / / have to log in
    if (to.path === "/login") {
      // If the login page is not necessary to display, redirect back to the home page
      next({ path: "/" });
    } else {
      // Go to another route
      const hasRoles =
        store.state.user.roles && store.state.user.roles.length > 0;
      if (hasRoles) {
        // If the user role has been paid, the permission is specified to determine that the dynamic route has been added
        next();
      } else {
        try {
          // Request to obtain user information
          const roles = await store.dispatch("user/getInfo");
          console.log(roles);
          // Dynamically generate routes based on the current user role
          const accessRoutes = await store.dispatch(
            "permission/generateRoutes",
            roles
          );
          console.log(accessRoutes);
          // Add these to the router
          router.addRoutes(accessRoutes);
          // Continue route switching to ensure that addRoutes is completenext({ ... to }); }catch (error) {
          // The token needs to be reset (token expired, network error, etc.)
          //await store.dispatch('user/resetToken')
          next(`/login? redirect=${to.path}`);
          alert(error || "Unknown error"); }}}}else {
    / / not logged in
    if(whiteList.indexOf(to.path) ! = = -1) {
      // Routes in the whitelist pass by
      next();
    } else {
      // Redirect to the login page
      next(`/login? redirect=${to.path}`); }}});Copy the code

Adding a Dynamic Route


According to user role to access the dynamic routing and added to filter out the router to create permission module, store/modules/permission. Js

import { constRoutes, asyncRoutes } from "@/router";

const state = {
  routes: [].// Complete routing table
  addRoutes: [] // The user can access the routing table
};

const mutations = {
  SET_ROUTES: (state, routes) = >{ state.addRoutes = routes; state.routes = constRoutes.concat(routes); }};const actions = {
  // Route generation: called as soon as the user role is obtained
  generateRoutes({ commit }, roles) {
    return new Promise(resolve= > {
      // Filter by role
      const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles);
      commit("SET_ROUTES", accessedRoutes); resolve(accessedRoutes); }); }};/** * Recursive filtering AsyncRoutes routing table *@routes For a filtered routing table, the first incoming is AsyncRoutes *@roles  The user has a role */
export function filterAsyncRoutes(routes, roles) {
  const res = [];
  routes.forEach(route= > {
    // Make a copy
    consttmp = { ... route };// If the user has access, join the result routing table
    if (hasPermission(roles, tmp)) {
      // Filter recursively if there are subpaths
      if(tmp.children) { tmp.children = filterAsyncRoutes(tmp.children, roles); } res.push(tmp); }});return res;
}

/** * Determine whether the current user has access permission * based on the route meta@roles User owned roles *@route Route to be determined */

export function hasPermission(roles, route) {
  if (route.meta && route.meta.roles) {
    // If any of the roles owned by the user are included in the routing role table, the user has the access right
    return roles.some(role= > route.meta.roles.includes(role));
  } else {
    // When roles is not set, access is done without decision
    return true; }}export default {
  namespaced: true,
  state,
  mutations,
  actions
};

Copy the code

Asynchronously obtaining the routing table


After logging in, the user requests the accessible routing table from the back end to dynamically generate the accessible page. The operation is the same as before, but there is an additional step to return the back end to the component name in the routing table and the local component mapping step:

// The front-end map is the previous asyncRoutes map
// The map returned by the server looks like this
const serviceMap = [
	{path:'/login'.component:'login'.hidden:true}]// Iterate over the serviceMap, replacing Component with map[Component], dynamically generating asyncRoutes
function mapComponent(serviceMap){
	serviceMap.forEach(route= > {
    route.component = map[route.component];
    if(route.children){
    	route.children.map(child= > mapComponent(child))
      }
	})
}
mapComponent(serviceMap)
Copy the code

Button permissions

Encapsulates an instruction v – permission to achieve button level access control, to create the SRC/directtive/permission. Js

For custom instructions, see cn.vuejs.org/v2/guide/cu…

import store from "@/store";
const permission = {
  inserted(el, binding) {
    // Get the value of the directive: the array of roles required by the button
    const { value: pRoles } = binding;
    // Get the user role
    const roles = store.getters && store.getters.roles;
    if (pRoles && pRoles instanceof Array && pRoles.length > 0) {
      const hasPermission = roles.some(role= > {
        return pRoles.includes(role);
      });
      // Delete the current DOM if you do not have permission
      if (!hasPermission) {
        el.parentNode && el.parentNode.removeChild(el);
      }
    } else {
      throw new Error(
        'You need to specify that the button requires an array of roles, such as V-Permission ="['admin','editor']"'); }}};export default permission;

Copy the code

Register the directive main.js

import vPermission from "./directive/permission";
Vue.directive("permission", vPermission);
Copy the code

test

<button v-permission="['admin', 'editor']">admin editor</button>
<button v-permission="['admin']">admin</button>
Copy the code

This directive can only delete elements that are attached to the directive, and does not remove elements that are generated independently of the directive. For example, if you mount a TAB, you can only delete the TAB, not the panel.You can use the global permission determination function, implemented using V-if

<template>
	<el-tab-pane v-if="checkPermission(['admin'])"></el-tab-pane>
</template>
<script>
export default{
	methods: {checkPermission(permissionRoles){
        	return roles.some(role= > {
            	returnpermissionRoles.include(role); }); }}}</script>
Copy the code