项目中权限功能

记录两个项目中的权限校验实现方式

1.OKR(一个项目)权限校验

使用github上开源框架ant-design-pro

路由权限的实现方式是通过获取当前用户的权限去比对路由表,生成当前用户具有的权限可访问的路由表,通过 router.addRoutes 动态挂载到 router 上。

OKR中每个页面的权限都是动态从后端配置的,可以在后台通过一个 tree 或者其它展现形式给每一个页面动态配置权限,之后将这份路由表存储到后端。该项目中是使用tree实现路由控制。

1.1后台返回数据结构

/me 接口,得到用户所有信息

 

 1 //返回的JSON格式
 2 {
 3   data:{
 4     ...//基本信息
 5     permissions:[{
 6     permissionId:"页面权限id/系统id",
 7     title:'HolliOne',
 8     children:[{
 9          permissionId:'user', //页面及权限
10           title:'用户管理',
11         children:[{
12             action: "resetPassword" //资源按钮级权限
13               title:'重置密码',
14             ....
15      },...]
16   },...]
17   },
18   message:'请求成功',
19   success:true,
20 }

1.2 前端页面菜单级权限校验

当用户登录后得到 permissions,动态生成可访问页面

 1 // 前端路由表
 2 const constantRouterComponents = {
 3   // 基础页面 layout 必须引入
 4   BasicLayout: BasicLayout,
 5   BlankLayout: BlankLayout,
 6   RouteView: RouteView,
 7   PageView: PageView,
 8 
 9   // 需要动态引入的页面组件
10   analysis: () => import('@/views/dashboard/Analysis'),
11   workplace: () => import('@/views/dashboard/Workplace'),
12   monitor: () => import('@/views/dashboard/Monitor')
13   // ...more
14 }
View Code
 1 getInfo().then(response => {
 2      const result = response.data
 3      result.role = {}
 4      if (result.permissions[0].children.length > 0) {
 5      let temp = []
 6      const role = result.role
 7      role.permissions = util.findActionArr(result.permissions[0].children,temp)
 8      // const role = result.role
 9      // role.permissions = result.permissions[0].children
10      role.permissions.map(per => {
11      if (per.children != null && per.children.length > 0) {
12             const action = per.children.map(action => { return action.action })
13             per.actionList = action
14        }
15     })
16     // role.permissionList = role.permissions.map(permission => {  return permission.permissionId })
17     let permissionList = [];
18     role.permissionList = util.findArr(result.permissions[0].children, permissionList, 'permissionId')
19     commit('SET_ROLES', result.role)
20     commit('SET_INFO', result)
21     } else {
22     reject(new Error('getInfo: roles must be a non-null array !'))
23     }commit('SET_AVATAR', result.avatar)
24       resolve(response)
25     }).catch(error => {
26        reject(error)
27  })
View Code
 1 //动态生成菜单
 2 router.beforeEach((to, from, next) => {
 3   NProgress.start() // start progress bar
 4 
 5   if (Vue.ls.get(ACCESS_TOKEN)) {
 6     /* has token */
 7     if (to.path === '/user/login') {
 8       next({ path: '/user/userList' })
 9       NProgress.done()
10     } else {
11       if (store.getters.roles.length === 0) {
12         store
13           .dispatch('GetInfo')
14           .then(res => {
15             const roles = res.data && res.data.role
16             store.dispatch('GenerateRoutes', { roles }).then(() => {
17               // 根据roles权限生成可访问的路由表
18               // 动态添加可访问路由表
19               router.addRoutes(store.getters.addRouters)
20               const redirect = decodeURIComponent(from.query.redirect || to.path)
21               if (to.path === redirect) {
22                 // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
23                 next({ ...to, replace: true })
24               } else {
25                 // 跳转到目的路由
26                 next({ path: '/'+ roles.permissionList[0] })
27               }
28             })
29           })
30           .catch((err) => {
31             console.log(err);
32             notification.error({
33               message: '错误',
34               description: '您暂时没有权限,请联系管理员开启权限'
35             })
36             store.dispatch('Logout').then(() => {
37               next({ path: '/user/login', query: { redirect: to.fullPath } })
38             })
39           })
40       } else {
41         next()
42       }
43     }
44   } else {
45     if (whiteList.includes(to.name)) {
46       // 在免登录白名单,直接进入
47       next()
48     } else {
49       next({ path: '/user/login', query: { redirect: to.fullPath } })
50       NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
51     }
52   }
53 })
54 
55 //vuex store modules
56 /**
57  * 过滤账户是否拥有某一个权限,并将菜单从加载列表移除
58  *
59  * @param permission
60  * @param route
61  * @returns {boolean}
62  */
63 function hasPermission (permission, route) {
64   if (route.meta && route.meta.permission) {
65     let flag = false
66     for (let i = 0, len = permission.length; i < len; i++) {
67       flag = route.meta.permission.includes(permission[i])
68       if (flag) {
69         return true
70       }
71     }
72     return false
73   }
74   return true
75 }
76 //格式化 后端 结构信息并递归生成层级路由表
77 function filterAsyncRouter (routerMap, roles) {
78   const accessedRouters = routerMap.filter(route => {
79     if (hasPermission(roles.permissionList, route)) {
80       if (route.children && route.children.length) {
81         route.children = filterAsyncRouter(route.children, roles)
82       }
83       return true
84     }
85     return false
86   })
87   return accessedRouters
88 }
89   actions: {
90     GenerateRoutes ({ commit }, data) {
91       return new Promise(resolve => {
92         const { roles } = data
93         const accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
94         commit('SET_ROUTERS', accessedRouters)
95         resolve()
96       })
97     }
98   }
View Code

1.3 资源/按钮级权限校验

封装了一个按钮级别权限的自定义指令, v-action

 

 1 <!-- eg: 当前页面为 user -->
 2 
 3 <template>
 4     <!-- 校验是否有 user 权限下的 add 操作权限 -->
 5     <a-button v-action:add >添加用户</a-button>
 6 
 7     <!-- 校验是否有 user 权限下的 del 操作权限 -->
 8     <a-button v-action:del>删除用户</a-button>
 9 
10     <!-- 校验是否有 user 权限下的 edit 操作权限 -->
11     <a v-action:edit @click="edit(record)">修改</a>
12 </template>
View Code

需要注意的是,指令权限默认从 store 中获取当前已经登陆的用户的角色和权限信息进行比对,所以也要对指令权限的获取和校验 Action 权限部分进行自定义。

在某些情况下,不适合使用 v-action,例如 Tab 组件,只能通过手动设置 v-if 来实现。

 1 <template>
 2     <a-tabs>
 3         <a-tab-pane v-if="$auth('user.add')" tab="Tab 1">
 4             some context..
 5         </a-tab-pane>
 6         <a-tab-pane v-if="$auth('user.del')" tab="Tab 2">
 7             some context..
 8         </a-tab-pane>
 9         <a-tab-pane v-if="$auth('user.edit')" tab="Tab 3">
10             some context..
11         </a-tab-pane>
12     </a-tabs>
13 </template>
View Code

在 Vue 初始化时,@/utils/helper/permission.js 作为插件注册到 Vue 原型链上,在 Vue 实例中就可以用 this.$auth() 方法进行权限判断。 这里也要对权限的获取和校验 Action 权限部分进行自定义。

2.另一个项目的权限校验

使用iview-admin开源框架

2.1后台返回的数据结构

/userInfo接口

 

 1 //接口返回的JSON格式数据
 2 {
 3   data:{
 4     btnSet:['按钮级权限集合(例如增删该查以及各种操作)'],
 5     menuSet:['页面级权限集合,返回字段和前端路由配置一致的页面就有权限'],
 6     soleSet:['角色集合,该用户是什么角色'],
 7     user:{'登录用户的基本信息'},
 8   },
 9   msg:'操作成功',
10   state:1,
11   success:true
12 }

2.2前端页面级权限操作

路由权限的实现方式是通过获取当前用户的权限去比对路由表,生成当前用户具有的权限可访问的路由表

前端登录时调用userInfo接口,将权限信息分开存入本地localStorage或者cookies/vuex-store,(就是便于拿到信息的地方)

第一步:过滤页面权限

 1   /**
 2      * 对一个对象或者数组进行深复制,保证不是以引用赋值
 3     */
 4  cloneObj(obj) {
 5       var str = obj.constructor === Array ? [] : {};
 6       var newobj = obj.constructor === Array ? [] : {};
 7       if (typeof obj !== 'object') {
 8             return;
 9       } else if (window.JSON) {
10           str = JSON.stringify(obj); // 首先将对象序列化
11           newobj = JSON.parse(str); //  然后将对象还原
12         }
13       return newobj;
14  },
15 //过滤路由
16 updateMenulist (state) {
17             // 权限过滤 、
18             let permissions = Cookies.get('permissions');
19             if (permissions === undefined) {
20                 return;
21             }
22             debugger
23             let menuList = [];
24             appRouter.forEach((child, index) => {
25                 // 深复制对象
26             var item = Utils.cloneObj(child);
27             let len = menuList.push(item);
28             // 父类是否存在
29             let parent = JSON.parse(permissions).filter(p => {
30                     if (p === item.name) {
31                 return p;
32             }
33         });
34             if (parent.length > 0) {
35                 let childrenArr = [];
36                 childrenArr = item.children.filter(child => {
37                         var p = JSON.parse(permissions);
38                 for (var i = 0; i < p.length; ++i) {
39                     // 放开测试页面
40                     if (p[i] === child.name || child.name === 'hollsysTestPage') {
41                         return child;
42                     }
43                 }
44             });
45                 item.children = childrenArr;
46                 // if (childrenArr === undefined || childrenArr.length === 0) {
47                 //     menuList.splice(len - 1, 1);
48                 // }
49             } else {
50                 menuList.splice(len - 1, 1);
51             }
52         });
53             let resut = [];
54             let pArr = JSON.parse(permissions);
55             /**循环遍历menuList*/
56             if (menuList != null && menuList.length>0) {
57                 for (var i=0;i<menuList.length;i++) {
58                     var temp = menuList[i];
59                     let childarr = [];
60                     childarr = temp.children;/**二级*/
61                     for (var j=0;j<childarr.length;j++) {
62                         var tempj = childarr[j];
63                         /**遍历三级路由*/
64                         var sanArr = [];
65                         sanArr = tempj.children;
66                         if (sanArr !== undefined) {
67 
68                             for (var k=0;k<sanArr.length;k++) {
69                                 var tempK = sanArr[k];
70                                 var flag = 0;
71                                 for (var q = 0; q < pArr.length; ++q) {
72                                     // 放开测试页面
73                                     if (pArr[q] === tempK.name) {
74                                         /**删除sanArr中这个权限*/
75                                         flag=1;
76                                     }
77                                 }
78                                 if (flag == 0) {
79                                     sanArr.splice(k,1);
80                                 }
81                             }
82                         }
83                     }
84                 }
85             }
86             state.menuList = menuList;
87             // 上面routers加所有的appRouters报错  改为过滤连接时动态添加 
88             // state.routers.push(...appRouter);
89             state.routers.push(...menuList);
90  }
View Code

2.3前端按钮级权限过滤

页面路由生成,生成按钮及的权限

在每个页面中通过if/v-if判断按钮是否显示

 1  //此角色有什么操作按钮权限功能
 2  permissionsBtnSet (name) {
 3      var permissionsBtnSet = Cookies.get('permissionsBtnSet');
 4      var p = JSON.parse(permissionsBtnSet);
 5      for (var i = 0; i < p.length; ++i) {
 6       if (p[i] === name) {
 7          return true;
 8        }
 9     }
10     return false;
11  }
View Code

哈哈哈哈,到此就结束了,可以生成菜单页面和操作按钮,自己记录一下。

posted @ 2019-06-14 10:03  ichthyo-plu  阅读(316)  评论(0编辑  收藏  举报