This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!

preface

Before implementing dynamic routing and permission control, consider the following three concepts:

What is dynamic routing?

Most projects today are front and back separate, which results in the front-end taking control of the routing hops that SpringMVC used to do on the back end. Dynamic routing is a route list generated by the front-end based on the routing information returned by the back-end

What is permission control?

Permission control means that after a user initiates a request to invoke an interface, the user determines whether the user has the corresponding permission, and then permits or intercepts the request

What is the relationship between dynamic routing and permission control?

Requests initiated by users are all initiated from the pages pointed to by a certain route. Therefore, the relationship between permissions and routes is many-to-one. That is, a route has multiple permissions.

For example, there are query and edit operations in the list page /list. When user A initiates A request to query the list, the back end can determine whether user A has the query permission in the list page /list.

Past the link

  • Brief account main function introduction
  • Brief introduction and deployment of the accounting backend environment
  • Brief introduction and deployment of account front end environment
  • Solve the small program scan code authorization prompt Token cannot be empty
  • Database design
  • Resolve AOP logging errors
  • Brief Spring Security
  • Integrated Spring Security

First, thinking analysis

The realization is mainly divided into two parts:

  1. Back-end generation Generates route permission information based on user roles
  2. The front-end generates a routing information table based on the returned routing permission information

The routing permission information returned by the backend in the account is as follows:

Because the front end needs to build the permission routing table based on the data in the tree structure, the back end needs to return the obtained list data to the front end as a tree structure

Tips: Modify the AntDesginPro route guard I really changed a long time!

Second, the implementation scheme

Data table design

The routing permission table mainly contains two information: the routing address and the permission character

The routing permission table is designed as follows (TB_menu) :

Route permission information is generated on the backend

The Controller layer

    @LoginRequired
    @apioperation (value = "get user's role and menu ")
    @GetMapping("/roleMenus")
    publicResult<? > getRoleMenus() {// User role
        List<RoleDO> roleDOS = roleService.getByUserId(LocalUserId.get());
        // User menu permissions
        List<MenuDO> menuDOS = menuService.getUserMenus(LocalUserId.get());
        List<MenuBO> menuBOS =  menuService.copyFromMenuDos(menuDOS);
        List<MenuBO> tree = menuService.generatorMenuTree(menuBOS);
        RoleMenuVO roleMenuVO = new RoleMenuVO(LocalUser.get(), roleDOS, tree);
        return Result.success(roleMenuVO);
    }
Copy the code

The Service layer converts the list to a Tree


    @Override
    public List<MenuBO> generatorMenuTree(List<MenuBO> boList) {
        return generatorTree(boList);
    }

    private List<MenuBO> generatorTree(List<MenuBO> voList) {
        List<MenuBO> tree = list2Tree(voList, null);
        sortTree(tree);
        return tree;
    }
    
    /** * list to tree */
    private List<MenuBO> list2Tree(List<MenuBO> list, Integer pId) {
        List<MenuBO> tree = new ArrayList<>();
        Iterator<MenuBO> it = list.iterator();
        while (it.hasNext()) {
            MenuBO m = it.next();
            if (m.getParentId() == pId) {
                tree.add(m);
                // Delete the added elementit.remove(); }}// Look for child elements
        tree.forEach(n -> n.setChildren(list2Tree(list, n.getId())));
        return tree;
    }
Copy the code

The front end generates the permission route list

Generate the main code for dynamic routing

Note: in this case, the identity of the permission is placed in the meta of the route

/** * dynamically generate menu *@returns {Promise<Router>}* /
export const generatorDynamicRouter = (ret) = > {
  return new Promise((resolve, reject) = > {
    const menuNav = []
    rootRouter.children = ret
    menuNav.push(rootRouter)
    const routers = generator(menuNav)
    routers.push(notFoundRouter)
    resolve(routers)
  })
}

/** * Format tree structure data to generate vue-router-level routing table **@param routerMap
 * @param parent
 * @returns {*}* /
export const generator = (routerMap, parent) = > {
  const children = []
  routerMap.forEach(item= > {
    // If it is a button, the loop is terminated
    if(item.menuType ! = ='F') {
      // If it is a menu, find all permissions of the child node and form an array
      if (item.menuType === 'C' && item.children) {
        const arr = findMenuPermissions(item.children)
        if (arr.length > 0) {
          item.permissionSign = arr
        }
      }
      const path = item.outerChain ? `${item.path}` : `${parent && parent.path ! = ='/' && parent.path || ' '}/${item.path === '/' ? ' ' : item.path}`
      // Check whether it is a directory
      if (item.menuType === 'M') {
        item.component = 'RouteView'
      }
      const currentRouter = {
        // Dynamic routing address splicing
        path: path,
        // Route name
        name: item.menuName,
        // Dynamically load the components of the page corresponding to the route
        component: item.outerChain ? undefined : (constantRouterComponents[item.component]) || (() = > import(`@/views${parent && parent.path ! = ='/' && parent.path || ' '}/${item.component}`)),
        meta: {
          title: item.menuTitle,
          icon: item.iconName || undefined.hiddenHeaderContent: false.target: item.outerChain ? '_blank' : undefined.permission: item.permissionSign
        }
      }
      / / redirection
      // item.redirect && (currentRouter.redirect = item.redirect)
      // Change the redirect of the parent node if the current node is home
      if (currentRouter.path === '/home') {
        parent.redirect = currentRouter.path
      }
      if (item.outerChain === 1) {
        currentRouter.redirect = item.path
      }
      // Whether there is a submenu, and recursively processing
      if (item.children && item.children.length > 0) {
        // Recursion
        const t = generator(item.children, currentRouter)
        // If there are no children, no value is assigned
        if (t.length > 0) currentRouter.children = t
      }
      children.push(currentRouter)
    }
  })
  return children
}
Copy the code

The front end determines whether a button has permission

/** * use v-action:[method] on components that need to control Action level permissions, as follows: * <i-button v-action:add > Add user </a-button> * <a-button v-action:delete> Delete user </a-button> * <a v-action:edit@click="edit(record)"> </a> * * - Components that use this command will be hidden when the current user does not have permissions * - When background permissions are different from the mode provided by pro, just modify the permissions filter here * * tips: This has been modified according to the background return value */
const action = Vue.directive('action', {
  inserted: function (el, binding, vnode) {
    const actionName = binding.arg
    // The current user menu list, including permissions
    const permissions = vnode.context.$route.meta.permission
    let hasPermission = false
    for (let i = 0; i < permissions.length; i++) {
      if (permissions[i] && permissions[i] === actionName) {
        hasPermission = true
        break}}if(! hasPermission) { el.parentNode && el.parentNode.removeChild(el) || (el.style.display ='none')}}})Copy the code

The results of

The administrator user’s menu route is as follows:

The menu route for common users is as follows:

test

This assumes that the front end forgot to hide the edit button with the permission character (normally, this button will not be displayed if you do not have the edit permission).

The tester does not have permission to create new users

When you click ok to add a user, a message is displayed indicating that the permission is insufficient, indicating that the function is normal

Third, summary

The above code can be found in the notebook back end and notebook PC end, if you are interested in the warehouse to check.

Thanks for seeing the end, it was a great honor to help you ~❤❤❤❤