Dynamic routing Vue and Springsecurity button-level access control

Ideas :

Dynamic routing implemented: the navigation guard determined whether the user has the user information by calling the interface, the background to get the user's character generation menu tree, Format menu tree structure information and the recursion level route table and use Vuex saved by  router.addRoutes dynamic linked loaded into  router the button-level access control, you need to use custom commands to achieve.

achieve:

Navigation guard Code:

 

router.beforeEach((to, from, next) => {
  NProgress.start() // start progress bar
  to.meta && (typeof to.meta.title !== 'undefined' && setDocumentTitle(`${to.meta.title} - ${domTitle}`))
  if (getStore('ACCESS_TOKEN')) {
    /* has token */
    if (to.path === '/user/login') {
      next({ path: '/other/list/user-list' })
      NProgress.done()
    } else {
      if (store.getters.roles.length === 0) {
        store
          .dispatch('GetInfo')
          .then(res => {
            const username = res.principal.username
            store.dispatch('GenerateRoutes', { username }).then(() =>{ 
              // Roles generated according to a routing table accessible
              // add the dynamic routing table accessible 
              router.addRoutes (store.getters.addRouters) 
              const = decodeURIComponent the redirect (from.query.redirect || to.path) 
              IF (to.path === the redirect) { 
                // Method to ensure hack addRoutes has been completed, the SET Replace: the SO to true Will Not Leave A Navigation History Record 
                Next ({... to, Replace: to true}) 
              } the else { 
                // jump to the destination route 
                Next ({path: the redirect}) 
              } 
            }) 
          }) 
          .catch (() => { 
            notification.error ({ 
              message: 'error', 
              Description: 'requesting user information failed. Please try again "
            })
            store.dispatch('Logout').then(() => {
              next({ path: '/user/login', query: { redirect: to.fullPath } })
            })
          })
      } else {
        next()
      }
    }
  } else {
    if (whiteList.includes(to.name)) {
      // 在免登录白名单,直接进入
      next()
    } else {
      next({ path: '/user/login', query: { redirect: to.fullPath } })
      NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
    }
  }
})

 Vuex save routers

const permission = {
  state: {
    routers: constantRouterMap,
    addRouters: []
  },
  mutations: {
    SET_ROUTERS: (state, routers) => {
      state.addRouters = routers
      state.routers = constantRouterMap.concat(routers)
    }
  },
  actions: {
    GenerateRoutes ({ commit }, data) {
      return new Promise(resolve => {
        generatorDynamicRouter(data).then(routers => {
          commit('SET_ROUTERS', routers)
          resolve()
        })
      })
    }
  }
}

 Routing tool, get access to the backend interface menu tree, and then processes the menu tree, the menu tree component string by converting components such as a front end:

userlist: () => import ( '@ / views / other / UserList'), is thus generated route we want a.

{} from Axios Import '@ / utils / Request' 
Import {UserLayout,, BasicLayout, RouteView, BlankLayout, PageView} from '@ / Layouts' 

// distal routing table 
const constantRouterComponents = { 
  // underlying page layout to be introduced 
  BasicLayout: BasicLayout, 
  BlankLayout: BlankLayout, 
  RouteView: RouteView, 
  PageView: PageView, 

  // dynamically introduced page components 
  Analysis: () => Import ( '@ / views / Dashboard / the Analysis'), 
  Workplace: () => Import (' @ / views / Dashboard / Workplace '), 
  Monitor: () => Import (' @ / views / Dashboard / Monitor '), 
  UserList: () => Import (' @ / views / OTHER / the UserList ') 
  // ... More 
} 

// distal route page not found (no change fixed)  
const = {notFoundRouter
  path: '*', the redirect:'/404', hidden: true
}
 
/ ** 
 * Get the rear end of the API routing information Axios 
 * Promise @Returns {} 
 * / 
Export getRouterByUser = const (Parameter) => { 
  return Axios ({ 
    URL: '/ mENU /' + parameter.username, 
    Method: 'GET' 
  }) 
} 

/ ** 
 * menu obtaining routing information 
 * 
 * 1. call getRouterByUser () to get access to the routing structure of the backend interface array 
 * 2. calling 
 * @ {Promise Returns < the any > } 
 * / 
Export generatorDynamicRouter = const (Data) => { 
  return new new Promise ((Resolve, Reject) => { 
    // Ajax 
    getRouterByUser (Data) .then (RES =>{
      // const result = res.result
      Generator Routers = const (RES) 
      routers.push (notFoundRouter) 
      Resolve (Routers) 
    }). the catch (ERR => {
      Reject (ERR) 
    }) 
  }) 
} 

/ ** 
 * formatted configuration information and the rear end recursion level route table 
 * 
 * @param routerMap 
 * @param parent 
 * @Returns {*} 
 * / 
Export = const Generator (routerMap, parent ) => { 
  return routerMap.map (Item => { 
    const currentRouter = { 
      // routing address dynamically generated as splicing / Dashboard / Workplace 
      path: $ { `Item && item.path || ''`}, 
      // route name , the only recommended 
      name: item.name item.key || || '', 
      // the route corresponding components of the page 
      the component: constantRouterComponents [item.component || item.key], 
      // Meta: page title, menu icons, page permissions (permission instruction for use, can be removed)
      Meta: {title: item.name, for, icon: item.icon || undefined, permission: item.key && [item.key] || null} 
    } 
    // return the result in order to prevent the rear end is not standardized, there may be processed splicing two backslash 
    currentRouter.path = currentRouter.path.replace ( '//', '/') 
    // redirection 
    item.redirect && (currentRouter.redirect = item.redirect) 
    // if there are sub-menu, and recursively processed 
    IF (item.children && item.children.length> 0) { 
      // the recursion 
      currentRouter.children = Generator (item.children, currentRouter) 
    } 
    return currentRouter 
  }) 
}

Back-end menu tree generation tools

/ ** 
 * Tools menu tree structure 
 * @author Dang 
 * 
 * / 
public  class TreeUtil { 

    protected TreeUtil () { 

    } 

    Private  Final  static Long TOP_NODE_ID = ( Long ). 1 ;
     / * 
     * distal routing configured 
     * @param routes 
     * @ return 
     * / 
    public  static the ArrayList <MenuEntity> buildVueRouter (List <MenuEntity> routes) {
         IF (routes == null ) {
             return  null ; 
        } 
        List<MenuEntity> topRoutes = new ArrayList<>();
        routes.forEach(route -> {
            Long parentId = route.getParentId();
            if (TOP_NODE_ID.equals(parentId)) {
                topRoutes.add(route);
                return;
            }
            for (MenuEntity parent : routes) {
                Long id = parent.getId();
                if (id != null && id.equals(parentId)) {
                    if (parent.getChildren() == null) {
                        parent.initChildren();
                    }
                    parent.getChildren().add(route);
                    return;
                }
            }
        });

        ArrayList<MenuEntity> list = new ArrayList<>();
        MenuEntity root = new MenuEntity();
        root.setName("首页");
        root.setComponent("BasicLayout");
        root.setPath("/");
        root.setRedirect("/other/list/user-list");
        root.setChildren(topRoutes);
        list.add(root);
        return list;
    }
}

Menu entities (using lombok plug-in)

/**
 * 菜单实体
 * @author dang
 *
 */

public class MenuEntity extends CoreEntity {

    private static final long serialVersionUID = 1L;
    @TableField("FParentId")
    private Long parentId;
    @TableField("FNumber")
    private String number;
    @TableField("FName")
    private String name;
    @TableField("FPerms")
    private String perms;
    @TableField("FType")
    private int type;
    @TableField("FLongNumber")
    private String longNumber;
    @TableField("FPath")
    private String path;
    @TableField("FComponent")
    private String component;
    @TableField("FRedirect")
    private String redirect;
    
    @TableField(exist = false)
    private List<MenuEntity> children;
    @TableField(exist = false)
    private MenuMeta meta;
    @TableField(exist = false)
    private List<PermissionEntity> permissionList;
    
    @Override
    public int hashCode() {
        return number.hashCode();
    }
    
    @Override
    public boolean equals(Object obj) {
        return super.equals(obj(obj);
    }

    public void initChildren() {
        
        this.children = new ArrayList<>();
    }
}

Routing menu is based on the user's role to get in, a user has multiple roles, a role with more menus

Ideas:

Said access control achieved under the button: Front vue main display with a custom instruction to achieve control buttons and hide, I use the back-end SpringSecurity framework, it is used @PreAuthorize注解,在菜单实体的to identify perms property rights records, such as: sys: user: add , recorded menu marked its authority should be parentId the previous menu, and then get perms collection of users, passed to the front when the user is logged and saved with Vuex, in a custom command to compare the user is required to contain the permissions button.

achieve:

When acquiring the user information, the authority to save the Vuex commit ( 'SET_PERMISSIONS', result.authorities) 

 

 

  // 获取用户信息
    GetInfo ({ commit }) {
      return new Promise((resolve, reject) => {
        getInfo().then(response => {
          const result = response
          if (result.authorities) {
            commit('SET_PERMISSIONS', result.authorities)
            commit('SET_ROLES', result.principal.roles)
            commit('SET_INFO', result)
          } else {
            reject(new Error('getInfo: roles must be a non-null array !'))
          }
          commit('SET_NAME', { name: result.principal.displayName, welcome: welcome() })
          commit('SET_AVATAR', result.principal.avatar)
          resolve(response)
        }).catch(error => {
          reject(error)
        })
      })
    }

 

 

 

Custom distal end instruction

 

// define permissions and instructions related Vue 
// must contain all of the listed rights, before the display elements 
Export const = {the hasPermission 
  the install (Vue) { 
    Vue.directive ( 'the hasPermission', { 
      the bind (EL, Binding, the vnode) { 
        const = vnode.context Permissions. $ store.state.user.permissions 
        const per = [] 
        for (V const of Permissions) { 
          per.push (v.authority) 
        } 
        const value = binding.value 
        the let = In Flag to true 
        for ( value of V const) { 
          IF (! per.includes (V)) { 
            In Flag to false = 
          } 
        } 
        IF (! In Flag) { 
          IF (! el.parentNode) {
            el.style.display = 'none'
          } else {
            el.parentNode.removeChild(el)
          }
        }
      }
    })
  }
}
// 当不包含列出的权限时,渲染该元素
export const hasNoPermission = {
  install (Vue) {
    Vue.directive('hasNoPermission', {
      bind (el, binding, vnode) {
        const permissions = vnode.context.$store.state.user.permissions
        const per = []
        for (const v of permissions) {
          per.push(v.authority)
        }
        const value = binding.value
        let flag = true
        for (const v of value) {
          IF (per.includes (V)) { 
            In Flag to false = 
          } 
        } 
        IF (! In Flag) { 
          IF (! el.parentNode) { 
            el.style.display = 'none' 
          } the else { 
            el.parentNode.removeChild (EL) 
          } 
        } 
      } 
    }) 
  } 
} 
// long as it contains any privileges listed elements displayed 
Export const = {hasAnyPermission 
  the install (Vue) { 
    Vue.directive ( 'hasAnyPermission', { 
      the bind (EL, Binding, the vnode) { 
        const = vnode.context Permissions. $ store.state.user.permissions 
        const per = [] 
        for (V const of Permissions) {
          per.push (v.authority) 
        } 
        const value = binding.value 
        the let = In Flag to false 
        for (value of V const) { 
          IF (per.includes (V)) { 
            In Flag to true = 
          } 
        } 
        IF (! In Flag) { 
          IF ( ! el.parentNode) { 
            el.style.display = 'none' 
          } the else { 
            el.parentNode.removeChild (EL) 
          } 
        } 
      } 
    }) 
  } 
} 
// must include all of the listed characters, display elements only 
export const hasRole = { 
  the install (Vue) { 
    Vue.directive ( 'the hasRole', {
      bind (el, binding, vnode) {
        const permissions = vnode.context.$store.state.user.roles
        const per = []
        for (const v of permissions) {
          per.push(v.authority)
        }
        const value = binding.value
        let flag = true
        for (const v of value) {
          if (!per.includes(v)) {
            flag = false
          }
        }
        if (!flag) {
          if (!el.parentNode) {
            el.style.display = 'none'
          } else {
            el.parentNode.removeChild(el)
          }
        }
      }
    })
  } 
} 
// long as it contains any of the listed character element is displayed 
Export const = {hasAnyRole 
  the install (Vue) { 
    Vue.directive ( 'hasAnyRole', { 
      the bind (EL, Binding, the vnode) { 
        const = Permissions the vnode. context. $ store.state.user.roles 
        const per = [] 
        for (V const of Permissions) { 
          per.push (v.authority) 
        } 
        const value = binding.value 
        the let = In Flag to false 
        for (value of V const) { 
          IF (per.includes (V)) { 
            In Flag to true = 
          } 
        } 
        IF (! In Flag) { 
          IF (! el.parentNode) {
            el.style.display = 'none'
          } else {
            el.parentNode.removeChild(el)
          }
        }
      }
    })
  }
}

 

Introducing the custom instruction main.js

import Vue from 'vue'
import { hasPermission, hasNoPermission, hasAnyPermission, hasRole, hasAnyRole } from './utils/permissionDirect'

Vue.use(hasPermission)
Vue.use(hasNoPermission)
Vue.use(hasAnyPermission)
Vue.use(hasRole)
Vue.use(hasAnyRole)

 

这样就可以在按钮中使用自定义指令,没有权限时,按钮自动隐藏,使用Postman工具测试也会拒绝访问

 <a-button type="primary" @click="handleAddUser()" v-hasPermission="['sys:user:add']" icon="plus">新建</a-button>

 

 

 

 

Guess you like

Origin www.cnblogs.com/dang-/p/11460698.html