The business requirements

Based on the vue-admin-template scaffolding, the user presents the corresponding page according to different roles. Back-end interface services, using Go Gin framework.

Implement ideas

There are two ways to implement user role permission authentication:

  1. A static routing table is written in the front end. When a user logs in, the user requests the back-end interface to obtain the user’s role information and display the corresponding page based on the role.
  2. The front-end routing table only keeps whitelisted lists, such as /login. When a user logs in, the user asynchronously requests for routing information and sends the returned routing table to the front end to display the corresponding page.

Code implementation

For the complete project, please refer to Github repository project

Vue – gin – template front end

Gin – vue – template backend

Front-end routing table data structure

The back-end interface will return the data format as follows:

[{path: '/'.component: Layout,
    redirect: '/dashboard'.children: [{
      path: 'dashboard'.name: 'page 1'.alwaysShow: true.component: () = > import('@/views/dashboard/index'),
      meta: { title: 'page 1'.icon: 'dashboard'}}}, {path: '/example'.component: Layout,
    redirect: '/example'.name: 'Permission management -test'.alwaysShow: true.meta: { title: 'Permission management -test'.icon: 'el-icon-s-help'},
    children: [{path: 'table'.name: 'users'.component: () = > import('@/views/permission/user'),
        meta: { title: 'users'.icon: 'table'}}, {path: 'tree'.name: The 'role'.component: () = > import('@/views/tree/index'),
        meta: { title: The 'role'.icon: 'tree'}}]},]Copy the code

The front end

Added permission handling mechanism

Create a SRC/store/modules/permission. Js file

import { constantRoutes } from '@/router'
import { getRoutes } from '@/api/role' // Get the interface method of the route
import Layout from '@/layout'
​
​
// Map the routing table
const componentsMap = {
  '/views/dashboard/index': () = > import('@/views/dashboard/index'),
  '/views/permission/role': () = > import('@/views/permission/role'),
  '/views/permission/user': () = > import('@/views/permission/user'),};/** * Assemble the background return menu into the format required by Routes *@param {*} routes* /
export function getAsyncRoutes(routes) {
  const res = []
  const keys = ['path'.'name'.'children'.'redirect'.'alwaysShow'.'meta'.'hidden']
  routes.forEach(item= > {
    const newItem = {}
    if (item.component) {
      if (item.component == 'Layout') {
        newItem.component = Layout
      }else {
        newItem['component'] = componentsMap[item.component]
      }
    }
​
    for (const key in item) {
      if (keys.includes(key)) {
        newItem[key] = item[key]
      }
    }
​
    if (newItem.children) {
      newItem.children = getAsyncRoutes(item.children)
    }
    res.push(newItem)
  })
  return res
}
​
const state = {
  routes: [].addRoutes: []}const mutations = {
  SET_ROUTES: (state, routes) = > {
    state.addRoutes = routes // Route access
    state.routes = constantRoutes.concat(routes) // Menu display}}const actions = {
  generateRoutes({ commit }, roles) {
    return new Promise(async resolve => {
      const routes = await getRoutes() // The background route is obtained
      const asyncRoutes = getAsyncRoutes(routes.data) // Process the routing format
      commit('SET_ROUTES', asyncRoutes)
      resolve(asyncRoutes)
    })
  }
}
​
export default {
  namespaced: true,
  state,
  mutations,
  actions
}
Copy the code

Adjust the getters. Js

src/store/getters.js

const getters = {
  sidebar: state= > state.app.sidebar,
  device: state= > state.app.device,
  token: state= > state.user.token,
  avatar: state= > state.user.avatar,
  name: state= > state.user.name,
  // Dynamic routing
  permission_routes: state= > state.permission.routes,
}
export default getters
Copy the code
Modifying the Layout

Adjust the SRC/layout/components/Sidebar/index. The vue

<template>
  <div :class="{'has-logo':showLogo}">
    <logo v-if="showLogo" :collapse="isCollapse" />
    <el-scrollbar wrap-class="scrollbar-wrapper">
      <el-menu
        :default-active="activeMenu"
        :collapse="isCollapse"
        :background-color="variables.menuBg"
        :text-color="variables.menuText"
        :unique-opened="false"
        :active-text-color="variables.menuActiveText"
        :collapse-transition="false"
        mode="vertical"
      >
        <sidebar-item v-for="route in routes" :key="route.path" :item="route" :base-path="route.path" />
        <! -- Dynamic routing -->
        <sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
      </el-menu>
    </el-scrollbar>
  </div>
</template>
​
<script>
import { mapGetters } from 'vuex'
import Logo from './Logo'
import SidebarItem from './SidebarItem'
import variables from '@/styles/variables.scss'export default {
  components: { SidebarItem, Logo },
  computed: {
    ...mapGetters([
      'permission_routes'.// Dynamic routing
      'sidebar',]),routes() {
      return this.$router.options.routes
    },
    activeMenu() {
      const route = this.$route
      const { meta, path } = route
      // if set path, the sidebar will highlight the path you set
      if (meta.activeMenu) {
        return meta.activeMenu
      }
      return path
    },
    showLogo() {
      return this.$store.state.settings.sidebarLogo
    },
    variables() {
      return variables
    },
    isCollapse() {
      return !this.sidebar.opened
    }
  }
}
</script>
Copy the code
Permission interceptor adjustment

Adjust the SRC/permission. Js

import router, { constantRoutes } from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'
​
NProgress.configure({ showSpinner: false }) // NProgress Configurationconst whiteList = ['/login'] // no redirect whitelist
​
router.beforeEach(async(to, from, next) => {
  // start progress bar
  NProgress.start()
​
  // set page title
  document.title = getPageTitle(to.meta.title)
​
  // determine whether the user has logged in
  const hasToken = getToken()
​
  if (hasToken) {
    if (to.path === '/login') {
      // if is logged in, redirect to the home page
      next({ path: '/' })
      NProgress.done()
    } else {
      const hasGetUserInfo = store.getters.name
      if (hasGetUserInfo) {
        next()
      } else {
        try {
          // get user info
          const { roles } =  await store.dispatch('user/getInfo')
          // Get the asynchronous route here
          const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
          // Call router.addRoutes to add the asynchronous routerouter.options.routers = constantRoutes.concat(accessRoutes) router.addRoutes(accessRoutes) next({ ... to,replace: true})}catch (error) {
          // remove token and go to login page to re-login
          await store.dispatch('user/resetToken')
          Message.error(error || 'Has Error')
          next(`/login? redirect=${to.path}`)
          NProgress.done()
        }
      }
    }
  } else {
    /* has no token*/
    if(whiteList.indexOf(to.path) ! = = -1) {
      // in the free login whitelist, go directly
      next()
    } else {
      // other pages that do not have permission to access are redirected to the login page.
      next(`/login? redirect=${to.path}`)
      NProgress.done()
    }
  }
})
​
router.afterEach(() = > {
  // finish progress bar
  NProgress.done()
})
Copy the code
Modifying a Routing Table

Adjust the SRC/router/index. Js

import Vue from 'vue'
import Router from 'vue-router'
​
Vue.use(Router)
​
export const constantRoutes = [
  {
    path: '/login'.component: () = > import('@/views/login/index'),
    hidden: true
  },
  {
    path: '/ 404'.component: () = > import('@/views/404'),
    hidden: true},]export const asyncRoutes = [
  // 404 page must be placed at the end !!!
  { path: The '*'.redirect: '/ 404'.hidden: true}]const createRouter = () = > new Router({
  // mode: 'history', // require service support
  scrollBehavior: () = > ({ y: 0 }),
  routes: constantRoutes
})
​
const router = createRouter()
​
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // reset router
}
​
export default router
Copy the code

The back-end

Database table structure
package models
​
import (
  "github.com/jinzhu/gorm"
)
​
/ / character table
type Role struct {
  gorm.Model
  Name string `gorm:"type:varchar(128); not null; Comment :' role name '"
  RoleID string `gorm:"not null; unique_index; Comment :' role id '"
  Rid uint `gorm:"not null; Comment: 'character ID' "`
  Remark string `gorm:"size:255; Comment: 'description' "`
}
​
func (Role) TableName(a) string {
  return "t_role"
}
​
/ / table
type Permission struct {
  gorm.Model
  PermissionId uint `gorm:"not null; unique_index; Comment: 'authorization ID' "`
  IsGroup string `gorm:"not null; Comment :' group '"
  
  Title string  `gorm:"not null; Comment :' module name '"
  Path string `gorm:"not null; Comment :' module path '"
  Redirect string `gorm:"not null; Comment :' redirect path '"
  Component string `gorm:"not null; Comment :' Module component '"
  Icon string `gorm:"not null; Comment: 'module icon' "`
  
  SubTitle string 'gorm:"comment:' submodule title '"
  SubComponent string 'GORm :"comment:' Sub-module components '"'
  SubPath string 'gorm:"comment:' submodule path '"
  SubIcon string 'GORm :"comment:' submodule icon'"
}
​
func (Permission) TableName(a) string {
  return "t_permission"
}
​
// Role permission association table
type RoleBePermission struct {
  gorm.Model
  Pid uint `gorm:"not null; Comment: 'authorization ID' "`
  Rid uint `gorm:"not null; Comment: 'character ID' "`
}
​
func (RoleBePermission) TableName(a) string {
  return "t_role_permission"
}
Copy the code
Gets the routing information function
/** * @funcName obtains routing information * @describe returns in vue router JS format * @param role Role id ** @return array format */
func RouterInfo(rid uint) []map[string]interface{} {
  router := models.GetPermissionFormRid(rid)
  routerList := make([]map[string]interface{},0)
  menuMap := models.GetMenu()
  / / a single page
  pageList := make([]string.0)
  pageList = menuMap["page"([]].string)
  for _, menu := range pageList{
    for _, r := range router {
      if r.Redirect == menu {
        singleMap := make(map[string]interface{})
        singleMap["path"] = r.Path
        singleMap["component"] = r.Component
        singleMap["redirect"] = r.Redirect
        
        childrenList := make([]map[string]interface{},0)
        childrenMap := make(map[string]interface{})
        meta := make(map[string]string)
        childrenMap["path"] = r.SubPath
        childrenMap["name"] = r.Title
        childrenMap["component"] = r.SubComponent
        meta["title"] = r.Title
        meta["icon"] = r.Icon
        childrenMap["meta"] = meta
        childrenList = append(childrenList, childrenMap)
        singleMap["children"] = childrenList
​
        if len(singleMap) ! =0 {
          routerList = append(routerList, singleMap)
        }
      }
    }
  }
  / / set of pages
  groupList := make([]string.0)
  groupList = menuMap["group"([]].string)
  for _, group := range groupList {
    childrenList := make([]map[string]interface{},0)
    groupMap := make(map[string]interface{})
    for _, r := range router {
      if r.IsGroup == "Yes" && r.Path == group {
        subChildren := make(map[string]interface{})
        meta := make(map[string]interface{})
        subMeta := make(map[string]interface{})
​
        groupMap["path"] = r.Path
        groupMap["component"] = r.Component
        groupMap["redirect"] = r.Redirect
        groupMap["name"] = r.Title
        meta["title"] = r.Title
        meta["icon"] = r.Icon
        groupMap["meta"] = meta
        
        subChildren["path"] = r.SubPath
        subChildren["name"] = r.SubTitle
        subChildren["component"] = r.SubComponent
        subMeta["title"] = r.SubTitle
        subMeta["icon"] = r.SubIcon
        subChildren["meta"] = subMeta
        childrenList = append(childrenList, subChildren)
        groupMap["children"] = childrenList
      }
    }
    if len(groupMap) ! =0 {
      routerList = append(routerList, groupMap)
    }
  }
  return routerList
}
Copy the code
Gets the routing table interface
// GetRouter
// @summary Gets routing table information
// @description provides the front-end routing table
// @tags user authentication
// @Success 20000 {object} response.Response "{"code": 20000, "data": [...] }"
// @Router /api/role/router [get]
// @Security 
func GetRouter(c *gin.Context) {
  headerMap := c.Request.Header
  token, _ := jwt.ParseToken(headerMap["X-Token"] [0])
  data := service.RouterInfo(token.RoleID)
  c.JSON(http.StatusOK, gin.H{
    "code": 20000."message": "Obtaining routing table succeeded"."data": data,
  })
}
Copy the code