## 权限控制 #### 前端权限控制 在商城运营时,我们可能是多个人员共同操作我们的系统,但是每个操作人员所具备的权限应该不同,权限的不同主要表现在两个部分,即导航菜单的查看权限和页面增删改操作按钮的操作权限。我们的把页面导航菜单查看权限和页面操作按钮统一存储在菜单数据库表中,菜单类型页面资源的类型。类型包括目录 、菜单 、按钮。 #### 权限标识 权限标识用来进行权限控制的唯一标识,主要是进行增删改查的权限控制。 权限标识包括:新增 编辑 删除 查看等,格式结构类似**xxx:xxx:xxx** 如:**admin:user:update**。 #### 导航菜单权限流程 用户登录之后,跳转至首页,前端发送请求到后台获取该用户下的所有菜单权限与认证权限数据,认证权限为约束用户增删改查操作,在路由导航守卫路由时加载用户导航菜单并存储到本地存储中。导航栏从本地存储读取菜单列表并进行渲染。 #### 页面按钮权限实现 用户登录系统之后,跳转到首页,在路由导航守卫路由时加载用户权限标识集合。返回结果是用户权限标识的集合,页面操作按钮提供权限标识,查询该权限标识是否在用户权限标识集合中,如有存在,则将按钮为可见状态,如不存在,则将按钮为不可见状态,根据需求,也可以设置成禁用状态。 #### 加载导航菜单权限与页面按钮权限数据 ##### 动态路由与导航栏 在`router/index.js`中,从后台加载导航菜单、页面按钮权限数据,并将数据保存到本地存储中,如下所示: ```javascript router.beforeEach((to, from, next) => { // 添加动态(菜单)路由 if (router.options.isAddDynamicMenuRoutes || fnCurrentRouteType(to, globalRoutes) === 'global') { next() } else { http({ url: http.adornUrl('/sys/menu/nav'), method: 'get', params: http.adornParams() }).then(({ data }) => { sessionStorage.setItem('authorities', JSON.stringify(data.authorities || '[]')) fnAddDynamicMenuRoutes(data.menuList) router.options.isAddDynamicMenuRoutes = true sessionStorage.setItem('menuList', JSON.stringify(data.menuList || '[]')) next({ ...to, replace: true }) }).catch((e) => { console.log(`%c${e} 请求菜单列表和权限失败,跳转至登录页!!`, 'color:blue') router.push({ name: 'login' }) }) } }) ``` 通过`fnAddDynamicMenuRoutes()`方法,动态加载菜单到路由中保存到本地存储`sessionStorage`中。但是现在只有路由,还需要将导航栏展示出来。在`main-sidebar.vue`中,我们将本地存储中菜单数据取出来,然后对导航栏动态渲染出来,并通过**menuId**与动态(菜单)路由进行匹配跳转至指定路由,这样,当我们点击菜单的时候,就会跳转至特定的路由。 ```javascript created () { this.menuList = JSON.parse(sessionStorage.getItem('menuList') || '[]') this.dynamicMenuRoutes = JSON.parse(sessionStorage.getItem('dynamicMenuRoutes') || '[]') this.routeHandle(this.$route) } <sub-menu v-for="menu in menuList" :key="menu.menuId" :menu="menu" :dynamicMenuRoutes="dynamicMenuRoutes"> </sub-menu> ``` `sub-menu`组件的部分代码 ```html <template> <el-submenu v-if="menu.list && menu.list.length >= 1" :index="menu.menuId + ''" :popper-class="'site-sidebar--' + sidebarLayoutSkin + '-popper'"> <template slot="title"> <icon-svg :name="menu.icon || ''" class="site-sidebar__menu-icon"></icon-svg> <span>{{ menu.name }}</span> </template> <sub-menu v-for="item in menu.list" :key="item.menuId" :menu="item" :dynamicMenuRoutes="dynamicMenuRoutes"> </sub-menu> </el-submenu> <el-menu-item v-else :index="menu.menuId + ''" @click="gotoRouteHandle(menu)"> <icon-svg :name="menu.icon || ''" class="site-sidebar__menu-icon"></icon-svg> <span>{{ menu.name }}</span> </el-menu-item> </template> ``` ##### 按钮权限 在组件中根据外部方法传入的权限标识进行权限判断,如果权限存在,则显示为可见状态,否则不可见。 ```html <el-button type="primary" icon="el-icon-plus" size="small" v-if="isAuth('admin:indexImg:save')" @click.stop="addOrUpdateHandle()">新增</el-button> ``` 通过`isAuth(“权限标识”)`,判断按钮是否有相同的标识,如果有则可见,否则不可见 ```javascript /** * 是否有权限 * @param {*} key */ export function isAuth (key) { let authorities = JSON.parse(sessionStorage.getItem('authorities') || '[]') if (authorities.length) { for (const i in authorities) { const element = authorities[i] if (element.authority === key) { return true } } } return false } ``` 注:后台通过`@PreAuthorize("@pms.hasPermission('admin:user:update')")`来定义请求所需要的权限,如果用户没有该权限,后台就会抛出401未授权状态码,前端捕获到该状态码后,会登出当前的账号,让用户重新登陆。 ![img](https://box.kancloud.cn/627e371fbaf45d74782a99fb888026ec_546x519.png) #### 后台菜单管理、角色管理与管理员列表 ##### 菜单管理 在【系统管理】-【菜单管理】中,我们可以通过类配置的方式,更直观的对菜单列表增删改查进行管理。 菜单类型包括目录 、菜单 、按钮。 目录为导航栏的大的分类,菜单为分类下的每一项,每个菜单需要绑定上级及填写对应跳转的路由,路由路径对应工程的目录如下图: ![img](https://box.kancloud.cn/fc23477a687599f9e01dee8f45f3b161_706x608.png) 在新增按钮权限时,注意授权标识要与后台一致,新增完之后需要重启刷新生效。 ![img](https://box.kancloud.cn/6ffabbbbf2b4f641126dd946f5d79f99_699x432.png) ![img](https://box.kancloud.cn/0e22aebd4c42f3a48e2bf3a7b4e43548_770x82.png) ##### 角色管理 在【系统管理】-【角色管理】中,管理员可以新增角色,并且赋予该角色可以访问的权限项。 ![img](https://box.kancloud.cn/2da280c4cb37ac290875afba404c15be_1374x959.png) ##### 管理员管理 在【系统管理】- 【管理员列表】中,拥有该权限的管理员可以对其进行管理,该管理员可添加或修改管理权限,并可分配列表中的用户角色。 ![img](https://box.kancloud.cn/6e086f97d7d3adfa80cb1d25ff078c9e_677x429.png)