Vue 动态路由的实现以及 Springsecurity 按钮级别的权限控制

2019/9/4 20:54:28 人评论 次浏览 分类:vue.js

广告开始--------------------------

全网最全17套vue.js视频教程

文件格式:视频      大小:160G左右

预览网址:【直接浏览网址即可】http://nicethemes.cn/product/view14.html

从入门到精通全套vue.js实战视频教程。

广告结束--------------------------

思路

动态路由实现:在导航守卫中判断用户是否有用户信息,通过调用接口,拿到后台根据用户角色生成的菜单树,格式化菜单树结构信息并递归生成层级路由表并使用Vuex保存,通过 router.addRoutes 动态挂载到 router 上,按钮级别的权限控制,则需使用自定义指令去实现。

实现:

导航守卫代码:

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生成可访问的路由表               // 动态添加可访问路由表               router.addRoutes(store.getters.addRouters)               const redirect = decodeURIComponent(from.query.redirect || to.path)               if (to.path === redirect) {                 // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record                 next({ ...to, replace: true })               } else {                 // 跳转到目的路由                 next({ path: redirect })               }             })           })           .catch(() => {             notification.error({               message: '错误',               description: '请求用户信息失败,请重试'             })             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保存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()         })       })     }   } }

路由工具,访问后端接口获得菜单树,然后对菜单树进行处理,把菜单树的组件字符串进行转换为前端的组件如:

userlist: () => import('@/views/other/UserList'),这样生成的路由就是我们所要的了。

import { axios } from '@/utils/request' import { UserLayout, BasicLayout, RouteView, BlankLayout, PageView } from '@/layouts'  // 前端路由表 const constantRouterComponents = {   // 基础页面 layout 必须引入   BasicLayout: BasicLayout,   BlankLayout: BlankLayout,   RouteView: RouteView,   PageView: PageView,    // 需要动态引入的页面组件   analysis: () => import('@/views/dashboard/Analysis'),   workplace: () => import('@/views/dashboard/Workplace'),   monitor: () => import('@/views/dashboard/Monitor'),   userlist: () => import('@/views/other/UserList')   // ...more }  // 前端未找到页面路由(固定不用改) const notFoundRouter = {   path: '*', redirect: '/404', hidden: true }  /**  * 获取后端路由信息的 axios API  * @returns {Promise}  */ export const getRouterByUser = (parameter) => {   return axios({     url: '/menu/' + parameter.username,     method: 'get'   }) }  /**  * 获取路由菜单信息  *  * 1. 调用 getRouterByUser() 访问后端接口获得路由结构数组  * 2. 调用  * @returns {Promise<any>}  */ export const generatorDynamicRouter = (data) => {   return new Promise((resolve, reject) => {     // ajax     getRouterByUser(data).then(res => {       // const result = res.result       const routers = generator(res)       routers.push(notFoundRouter)       resolve(routers)     }).catch(err => {       reject(err)     })   }) }  /**  * 格式化 后端 结构信息并递归生成层级路由表  *  * @param routerMap  * @param parent  * @returns {*}  */ export const generator = (routerMap, parent) => {   return routerMap.map(item => {     const currentRouter = {       // 路由地址 动态拼接生成如 /dashboard/workplace       path: `${item && item.path || ''}`,       // 路由名称,建议唯一       name: item.name || item.key || '',       // 该路由对应页面的 组件       component: constantRouterComponents[item.component || item.key],       // meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)       meta: { title: item.name, icon: item.icon || undefined, permission: item.key && [ item.key ] || null }     }     // 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠     currentRouter.path = currentRouter.path.replace('//', '/')     // 重定向     item.redirect && (currentRouter.redirect = item.redirect)     // 是否有子菜单,并递归处理     if (item.children && item.children.length > 0) {       // Recursion       currentRouter.children = generator(item.children, currentRouter)     }     return currentRouter   }) }

后端菜单树生成工具类

/**  * 构造菜单树工具类  * @author dang  *  */ public class TreeUtil {      protected TreeUtil() {      }      private final static Long TOP_NODE_ID = (long) 1;     /**      * 构造前端路由      * @param routes      * @return      */     public static ArrayList buildVueRouter(List routes) {         if (routes == null) {             return null;         }         List 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 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;     } }

菜单实体 (使用了lombok插件)

/**  * 菜单实体  * @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 children;     @TableField(exist = false)     private MenuMeta meta;     @TableField(exist = false)     private List 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<>();     } }

路由菜单是根据用户的角色去获得的,一个用户具有多个角色,一个角色具有多个菜单

思路:

说下按钮权限控制的实现:前端vue主要用自定义指令实现控制按钮的显示与隐藏,后端我用的是SpringSecurity框架,所以使用的是@PreAuthorize注解,在菜单实体的perms属性记录权限的标识,如:sys:user:add,记录有权限标识的菜单其 parentId 应为上级菜单,然后获取用户的perms集合,在用户登录的时候传给前端并用Vuex保存,在自定义指令中去比较用户是否含有按钮所需要的权限。

实现:

获取用户信息的时候,把权限存到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)         })       })     }

前端自定义指令

// 定义一些和权限有关的 Vue指令 // 必须包含列出的所有权限,元素才显示 export const hasPermission = {   install (Vue) {     Vue.directive('hasPermission', {       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)) {             flag = false           }         }         if (!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)) {             flag = false           }         }         if (!flag) {           if (!el.parentNode) {             el.style.display = 'none'           } else {             el.parentNode.removeChild(el)           }         }       }     })   } } // 只要包含列出的任意一个权限,元素就会显示 export const hasAnyPermission = {   install (Vue) {     Vue.directive('hasAnyPermission', {       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 = false         for (const v of value) {           if (per.includes(v)) {             flag = true           }         }         if (!flag) {           if (!el.parentNode) {             el.style.display = 'none'           } else {             el.parentNode.removeChild(el)           }         }       }     })   } } // 必须包含列出的所有角色,元素才显示 export const hasRole = {   install (Vue) {     Vue.directive('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)           }         }       }     })   } } // 只要包含列出的任意一个角色,元素就会显示 export const hasAnyRole = {   install (Vue) {     Vue.directive('hasAnyRole', {       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 = false         for (const v of value) {           if (per.includes(v)) {             flag = true           }         }         if (!flag) {           if (!el.parentNode) {             el.style.display = 'none'           } else {             el.parentNode.removeChild(el)           }         }       }     })   } }

在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>

上一篇:Vue视频学习1

下一篇:步入vue.js世界

相关资讯

    暂无相关的资讯...

共有访客发表了评论 网友评论

验证码: 看不清楚?
    -->