前两篇关于vue权限路由文章的填坑,说了一堆理论,是时候操作一波了。

vue权限路由实现方式总结

vue权限路由实现方式总结二

选择d2-admin是因为element-ui的相关开源项目里,d2-admin的结构和代码是让我感到最舒服的,而且基于d2-admin实现RBAC权限管理也很方便,对d2-admin没有大的侵入性的改动。

预览地址

Github

相关概念

不了解RBAC,可以看这里企业管理系统前后端分离架构设计 系列一 权限模型篇

  • 实现了RBAC模型权限控制
  • 菜单与路由独立管理,完全由后端返回
  • user存储用户
  • admin标识用户是否为系统管理员
  • role存储角色信息
  • roleUser存储用户与角色的关联关系
  • menu存储菜单信息,类型分为菜单功能,一个菜单下可以有多个功能,菜单类型的permission字段标识访问这个菜单需要的功能权限,功能类型的permission字段相当于此功能的别称,所以菜单类型的permission字段为其某个功能类型子节点的permission
  • permission存储角色与功能的关联关系
  • interface存储接口信息
  • functionInterface存储功能与接口关联关系,通过查找用户所属角色,再查找相关角色所具备的功能权限,再通过相关功能就可以查出用户所能访问的接口
  • route存储前端路由信息,通过permission字段过滤出用户所能访问的路由

运行流程及相关API

使用d2admin的原有登录逻辑,全局路由守卫中判断是否已经拉取权限信息,获取后标识为已获取。

const token = util.cookies.get('token')
if (token && token !== 'undefined') {
//拉取权限信息
if (!isFetchPermissionInfo) {
await fetchPermissionInfo();
isFetchPermissionInfo = true;
next(to.path, true)
} else {
next()
}
} else {
// 将当前预计打开的页面完整地址临时存储 登录后继续跳转
// 这个 cookie(redirect) 会在登录后自动删除
util.cookies.set('redirect', to.fullPath)
// 没有登录的时候跳转到登录界面
next({
name: 'login'
})
}
//标记是否已经拉取权限信息
let isFetchPermissionInfo = false let fetchPermissionInfo = async () => {
//处理动态添加的路由
const formatRoutes = function (routes) {
routes.forEach(route => {
route.component = routerMapComponents[route.component]
if (route.children) {
formatRoutes(route.children)
}
})
}
try {
let userPermissionInfo = await userService.getUserPermissionInfo()
permissionMenu = userPermissionInfo.accessMenus
permissionRouter = userPermissionInfo.accessRoutes
permission.functions = userPermissionInfo.userPermissions
permission.roles = userPermissionInfo.userRoles
permission.interfaces = util.formatInterfaces(userPermissionInfo.accessInterfaces)
permission.isAdmin = userPermissionInfo.isAdmin == 1
} catch (ex) {
console.log(ex)
}
formatRoutes(permissionRouter)
let allMenuAside = [...menuAside, ...permissionMenu]
let allMenuHeader = [...menuHeader, ...permissionMenu]
//动态添加路由
router.addRoutes(permissionRouter);
// 处理路由 得到每一级的路由设置
store.commit('d2admin/page/init', [...frameInRoutes, ...permissionRouter])
// 设置顶栏菜单
store.commit('d2admin/menu/headerSet', allMenuHeader)
// 设置侧边栏菜单
store.commit('d2admin/menu/fullAsideSet', allMenuAside)
// 初始化菜单搜索功能
store.commit('d2admin/search/init', allMenuHeader)
// 设置权限信息
store.commit('d2admin/permission/set', permission)
// 加载上次退出时的多页列表
store.dispatch('d2admin/page/openedLoad')
await Promise.resolve()
}

后端需要返回的权限信息包括权限过滤后的角色编码集合,功能编码集合,接口信息集合,菜单列表,路由列表,以及是否系统管理员标识。格式如下

{
"statusCode": 200,
"msg": "",
"data": {
"userName": "MenuManager",
"userRoles": [
"R_MENUADMIN"
],
"userPermissions": [
"p_menu_view",
"p_menu_edit",
"p_menu_menu"
],
"accessMenus": [
{
"title": "系统",
"path": "/system",
"icon": "cogs",
"children": [
{
"title": "系统设置",
"icon": "cogs",
"children": [
{
"title": "菜单管理",
"path": "/system/menu",
"icon": "th-list"
}
]
},
{
"title": "组织架构",
"icon": "pie-chart",
"children": [
{
"title": "部门管理",
"icon": "html5"
},
{
"title": "职位管理",
"icon": "opencart"
}
]
}
]
}
],
"accessRoutes": [
{
"name": "System",
"path": "/system",
"component": "layoutHeaderAside",
"componentPath": "layout/header-aside/layout",
"meta": {
"title": "系统设置",
"cache": true
},
"children": [
{
"name": "MenuPage",
"path": "/system/menu",
"component": "menu",
"componentPath": "pages/sys/menu/index",
"meta": {
"title": "菜单管理",
"cache": true
}
},
{
"name": "RoutePage",
"path": "/system/route",
"component": "route",
"componentPath": "pages/sys/route/index",
"meta": {
"title": "路由管理",
"cache": true
}
},
{
"name": "RolePage",
"path": "/system/role",
"component": "role",
"componentPath": "pages/sys/role/index",
"meta": {
"title": "角色管理",
"cache": true
}
},
{
"name": "UserPage",
"path": "/system/user",
"component": "user",
"componentPath": "pages/sys/user/index",
"meta": {
"title": "用户管理",
"cache": true
}
},
{
"name": "InterfacePage",
"path": "/system/interface",
"component": "interface",
"meta": {
"title": "接口管理"
}
}
]
}
],
"accessInterfaces": [
{
"path": "/menu/:id",
"method": "get"
},
{
"path": "/menu",
"method": "get"
},
{
"path": "/menu/save",
"method": "post"
},
{
"path": "/interface/paged",
"method": "get"
}
],
"isAdmin": 0,
"avatarUrl": "https://api.adorable.io/avatars/85/abott@adorable.png"
}
}

设置菜单

将固定菜单(/menu/header/menu/aside)与后端返回的权限菜单(accessMenus)合并后,存入相应的vuex store模块中

...
let allMenuAside = [...menuAside, ...permissionMenu]
let allMenuHeader = [...menuHeader, ...permissionMenu]
...
// 设置顶栏菜单
store.commit('d2admin/menu/headerSet', allMenuHeader)
// 设置侧边栏菜单
store.commit('d2admin/menu/fullAsideSet', allMenuAside)
// 初始化菜单搜索功能
store.commit('d2admin/search/init', allMenuHeader)

处理路由

默认使用routerMapComponents 的方式处理后端返回的权限路由

//处理动态添加的路由
const formatRoutes = function (routes) {
routes.forEach(route => {
route.component = routerMapComponents[route.component]
if (route.children) {
formatRoutes(route.children)
}
})
}
...
formatRoutes(permissionRouter)
//动态添加路由
router.addRoutes(permissionRouter);
// 处理路由 得到每一级的路由设置
store.commit('d2admin/page/init', [...frameInRoutes, ...permissionRouter])

路由处理方式及区别可看vue权限路由实现方式总结二

设置权限信息

将角色编码集合,功能编码集合,接口信息集合,以及是否系统管理员标识存入相应的vuex store模块中

...
permission.functions = userPermissionInfo.userPermissions
permission.roles = userPermissionInfo.userRoles
permission.interfaces = util.formatInterfaces(userPermissionInfo.accessInterfaces)
permission.isAdmin = userPermissionInfo.isAdmin == 1
...
// 设置权限信息
store.commit('d2admin/permission/set', permission)

接口权限控制以及loading配置

支持使用角色编码,功能编码以及接口权限进行控制,如下

export function getMenuList() {
return request({
url: '/menu',
method: 'get',
interfaceCheck: true,
permission:["p_menu_view"],
loading: {
type: 'loading',
options: {
fullscreen: true,
lock: true,
text: '加载中...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.8)'
}
},
success: {
type: 'message',
options: {
message: '加载菜单成功',
type: 'success'
}
}
})
}

interfaceCheck: true表示使用接口权限进行控制,如果vuex store中存储的接口信息与当前要请求的接口想匹配,则可发起请求,否则请求将被拦截。

permission:["p_menu_view"]表示使用角色编码和功能编码进行权限校验,如果vuex store中存储的角色编码或功能编码与当前表示的编码相匹配,则可发起请求,否则请求将被拦截。

源码位置在libs/permission.js,可根据自己需求进行修改

loading配置相关源码在libs/loading.js,根据自己需求进行配置,success也是如此,源码在libs/loading.js。 照此思路可以自行配置其它功能,比如请求失败等。

页面元素权限控制

使用指令v-permission

 <el-button
v-permission:function.all="['p_menu_edit']"
type="primary"
icon="el-icon-edit"
size="mini"
@click="batchEdit"
>批量编辑</el-button>

参数可为functionrole,表明以功能编码或角色编码进行校验,为空则使用两者进行校验。

修饰符all,表示必须全部匹配指令值中所有的编码。

源码位置在plugin/permission/index.js,根据自己实际需求进行修改。

使用v-if+全局方法:

<el-button
v-if="canAdd"
type="primary"
icon="el-icon-circle-plus-outline"
size="mini"
@click="add"
>添加</el-button>
data() {
return {
canAdd: this.hasPermissions(["p_menu_edit"])
};
},

默认同时使用角色编码与功能编码进行校验,有一项匹配即可。

类似的方法还要hasFunctionshasRoles

源码位置在plugin/permission/index.js,根据自己实际需求进行修改。

不要使用v-if="hasPermissions(['p_menu_edit'])"这种方式,会导致方法多次执行

也可以直接在组件中从vuex store读取权限信息进行校验。

开发建议

  • 页面级别的组件放到pages/目录下,并且在routerMapCompnonents/index.js中以key-value的形式导出

  • 不需要权限控制的固定菜单放到menu/aside.jsmenu/header.js

  • 不需要权限控制的路由放到router/routes.js frameIn

  • 需要权限控制的菜单与路由通过界面的管理功能进行添加,确保菜单的path与路由的path相对应,路由的name与页面组件的name一致才能使keep-alive生效,路由的componentrouterMapCompnonents/index.js中能通过key匹配到。

  • 开发阶段菜单与路由的添加可由开发人员自行维护,并维护一份清单,上线后将清单交给相关的人去维护即可。

如果觉得麻烦,不想菜单与路由由后端返回,可以在前端维护一份菜单和路由(路由中的component还是使用字符串,参考mock/permissionMenuAndRouter.js),并且在菜单和路由上面维护相应的权限编码,一般都是使用功能编码。后端就不需要返回菜单和路由信息了,但是其他权限信息,比如角色编码,功能编码等还是需要的。通过后端返回的功能编码列表,在前端过滤出用户具备权限的菜单和路由,过滤处理后后的菜单与路由格式与之前由后端返回的格式一致,然后将处理后的菜单与路由当做后端返回的一样处理即可。

数据mock与代码生成

数据mock使用lazy-mock修改而来的d2-admin-server,数据真实来源于后端,相比其他工具,支持数据持久化,存储使用的是json文件,不需要安装数据库。简单的配置即可自动生成增删改查的接口。

后端使用中间件控制访问权限,比如:

 .get('/menu', PermissionCheck(), controllers.menu.getMenuList)

PermissionCheck默认使用接口进行校验,校验用户所能访问的API中是否匹配当前API,支持使用功能编码与角色编码进行校验PermissionCheck(["p_menu_edit"],["r_menu_admin"],true),第一个参数为功能编码,第二个为角色编码,第三个为是否使用接口进行校验。

更多详细用法可看lazy-mock文档

前端代码生成还在开发中...

vue基于d2-admin的RBAC权限管理解决方案的更多相关文章

  1. ThinkPHP中RBAC权限管理的简单应用

    RBAC英文全称(Role-Based Access Controller)即基于角色的权限访问控制,简单来讲,一个用户可以拥有若干角色,每一个角色拥有若干权限.这样,就构造成“用户-角色-权限”的授 ...

  2. 基于RBAC权限管理的后台管理系统

    在摸爬滚打中渐渐理解了RBAC权限管理是个什么玩意. RBAC的基本概念: **RBAC认为权限授权实际上是Who.What.How的问题.在RBAC模型中,who.what.how构成了访问权限三元 ...

  3. [转]Asp.Net大型项目实践(11)-基于MVC Action粒度的权限管理【续】【源码在这里】(在线demo,全部源码)

    本文转自:http://www.cnblogs.com/legendxian/archive/2010/01/25/1655551.html 接上篇Asp.Net大型项目实践(10)-基于MVC Ac ...

  4. Spring Security实现RBAC权限管理

    Spring Security实现RBAC权限管理 一.简介 在企业应用中,认证和授权是非常重要的一部分内容,业界最出名的两个框架就是大名鼎鼎的 Shiro和Spring Security.由于Spr ...

  5. PHP中RBAC权限管理

    1.RBAC概念和原理          RBAC:全称叫做Role-Based Access Control,中文翻译叫做基于角色的访问控制.其主要的作用是实现项目的权限控制.            ...

  6. spring boot:spring security用mysql数据库实现RBAC权限管理(spring boot 2.3.1)

    一,用数据库实现权限管理要注意哪些环节? 1,需要生成spring security中user类的派生类,用来保存用户id和昵称等信息, 避免页面上显示用户昵称时需要查数据库 2,如果需要在页面上显示 ...

  7. RBAC权限管理模型 产品经理 设计

    RBAC权限管理模型:基本模型及角色模型解析及举例 | 人人都是产品经理http://www.woshipm.com/pd/440765.html RBAC权限管理 - PainsOnline的专栏 ...

  8. yii2 rbac权限管理学习笔记

    下面介绍一个 yii2 的 Rbac 权限管理设置,闲话少说,直接上代码, 1.首先我们要在组件里面配置一下 Rbac ,如下所示(common/config/main-local.php或者main ...

  9. 基于K2 BPM的航空业核心业务管理解决方案

    基于K2 BPM平台的航空业解决方案,专注航空公司运行类.营销类.管理类所有解决方案. 查看完整版,请访问K2官网http://www.k2software.cn/zh-hans/aviation-i ...

随机推荐

  1. Ajax异步请求阻塞情况的解决办法

    最近使用ExtJs4的mvc模式在开发了在线漫画的后台,因为异步请求比较多,有的回应时间长,有点短.我发现在多次并发的情况下,会造成阻塞的情况.也就是说如果回应时间长的请求还在进行中,短的请求却被挂起 ...

  2. UWP开发---通过委托跨页面导航

    -前言 做过.Net开发的都了解,当二级窗口操作主窗口的控件时通常用委托的方式.那么在UWP开发中,常常会遇到MainPage的二级Frame里面的内容去操作MainPage的导航跳转,具体看下图: ...

  3. Day 28面向对象的进阶-内置函数(__new__,__del__)

     元类 创造 类  所有类的type 都是他的元类 类创造 对象   具体创造对象的方法 __new__方法 class 类名(classmata = type)#默认是 class 类名(class ...

  4. Day 19 re 模块 random模块,正则表达式

    https://www.cnblogs.com/Eva-J/p/7228075.html#_label10 findall search match方法 和 search相比 match自带 ^ se ...

  5. git code 初次上传

    http://blog.csdn.net/hanhailong726188/article/details/46738929 1 cd到当前项目的跟目录 执行 git init 2 将当前项目的所有文 ...

  6. 二,mysql优化——sql优化基本概念

    1,SQL优化的一般步骤 (1)通过show status命令了解各种SQL执行效率. (2)通过执行效率较低的SQL语句(重点select). (3)通过explain分析低效率的SQL语句的执行情 ...

  7. 解决ORA-21561: OID generation failed

    解决ORA-21561 在linux上使用sqlplus连接oracle数据库 [root@china ~]# sqlplus test/test@ORCL SQL Production :: Cop ...

  8. linux中进程和计划任务管理

    进程和计划任务管理 1. 程序和进程的关系 程序:保存在硬盘.光盘等介质中的可执行代码和数据:静态保存的代码 进程:在 CPU 及内存中运行的程序代码:动态执行的代码:父.子进程:每个进程可以创建一个 ...

  9. QuantLib 金融计算——修复 BatesProcess 中的两个 Bug

    QuantLib 金融计算--修复 BatesProcess 中的两个 Bug 我发现了 BatesProcess 中的两个 Bug: 基类 HestonProcess::factors 的返回值取决 ...

  10. Java之Socket网络编程实践

    转自:http://my.oschina.net/leejun2005/blog/104955#comments 一.TCP/IP协议 既然是网络编程,涉及几个系统之间的交互,那么首先要考虑的是如何准 ...