{"id":14973700,"url":"https://github.com/wjkang/d2-admin-pm","last_synced_at":"2025-04-05T09:06:40.543Z","repository":{"id":36127540,"uuid":"164201790","full_name":"wjkang/d2-admin-pm","owner":"wjkang","description":"基于 d2-admin的RBAC权限管理解决方案","archived":false,"fork":false,"pushed_at":"2023-01-26T07:00:03.000Z","size":12733,"stargazers_count":413,"open_issues_count":15,"forks_count":119,"subscribers_count":18,"default_branch":"master","last_synced_at":"2025-03-29T08:06:04.909Z","etag":null,"topics":["admin-template","axios","d2-admin","element","element-ui","rbac","vue-admin","vue2","vuecli3"],"latest_commit_sha":null,"homepage":"http://jaycewu.gitee.io/d2-admin-pm/#/index","language":"Vue","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wjkang.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-01-05T09:45:30.000Z","updated_at":"2024-12-29T04:00:20.000Z","dependencies_parsed_at":"2023-02-14T15:00:38.330Z","dependency_job_id":null,"html_url":"https://github.com/wjkang/d2-admin-pm","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wjkang%2Fd2-admin-pm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wjkang%2Fd2-admin-pm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wjkang%2Fd2-admin-pm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wjkang%2Fd2-admin-pm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wjkang","download_url":"https://codeload.github.com/wjkang/d2-admin-pm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247312077,"owners_count":20918344,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["admin-template","axios","d2-admin","element","element-ui","rbac","vue-admin","vue2","vuecli3"],"created_at":"2024-09-24T13:49:15.910Z","updated_at":"2025-04-05T09:06:40.458Z","avatar_url":"https://github.com/wjkang.png","language":"Vue","funding_links":[],"categories":["Vue"],"sub_categories":[],"readme":"代码太丑，不建议照搬，可以看一看 RBAC 的实现思路，这是通用的。\n\n### 运行使用\n\n```bash\ngit clone https://github.com/wjkang/d2-admin-pm.git\n\nnpm install\n\nnpm start\n```\n需要后端mock服务的支持\n```bash\ngit clone https://github.com/wjkang/d2-admin-server.git\n\nnpm install\n\nnpm start\n```\n### 功能预览\n|  |  |  |\n| ------ | ------ | ------ |\n| ![](img/2019-01-05-22-12-35.png) | ![](img/2019-01-05-22-19-06.png) | ![](img/2019-01-05-22-19-41.png) |\n| ![](img/2019-01-05-22-21-35.png) | ![](img/2019-01-05-22-22-08.png) | ![](img/2019-01-05-22-22-35.png) |\n\n### 对d2-admin-start-kit的修改\n`main.js`中`created`的内容转到`router/index.js`内，并添加相关逻辑\n\n修改了**axios**相关代码`plugin/axios/index.js`，支持接口级权限控制，并且支持配置loading效果\n\nvuex store中添加**menu**模块添加**fullAside**，完整路径`store.state.d2admin.menu.fullAside`\n\nvuex store中添加**permission**模块，存储用户具备的功能权限码，角色编码，具备访问权限的接口以及是否管理员标识\n```js\nexport default {\n    namespaced: true,\n    state: {\n        //功能编码\n        functions: [],\n        //角色编码\n        roles: [],\n        //接口\n        interfaces: {\n            GET: [],\n            POST: [],\n            PUT: [],\n            DELETE: []\n        },\n        //是否管理员\n        isAdmin: false\n    },\n    mutations: {\n        set(state, data) {\n            state.functions = data.functions;\n            state.roles = data.roles;\n            state.isAdmin = data.isAdmin;\n            state.interfaces = data.interfaces;\n        }\n    }\n}\n```\n\nvuex store中的`account`模块的`load`action的这部分:\n```js\n // DB -\u003e store 持久化数据加载上次退出时的多页列表\nawait dispatch('d2admin/page/openedLoad', null, { root: true })\n```\n转到`router/index.js`内,需要在加载完权限路由后才执行\n\n### 相关概念\n\n![权限模型](img/2019-01-05-10-33-03.png)\n\n* 实现了RBAC模型权限控制\n* 菜单与路由独立管理，完全由后端返回\n* **user**存储用户\n* **admin**标识用户是否为系统管理员\n* **role**存储角色信息\n* **roleUser**存储用户与角色的关联关系\n* **menu**存储菜单信息，类型分为`菜单`与`功能`，一个菜单下可以有多个功能，`菜单`类型的`permission`字段标识访问这个菜单需要的功能权限，`功能`类型的`permission`字段相当于此功能的别称，所以`菜单`类型的`permission`字段为其某个`功能`类型子节点的`permission`值\n* **permission**存储角色与功能的关联关系\n* **interface**存储接口信息\n* **functionInterface**存储功能与接口关联关系，通过查找用户所属角色，再查找相关角色所具备的功能权限，再通过相关功能就可以查出用户所能访问的接口\n* **route**存储前端路由信息，通过`permission`字段过滤出用户所能访问的路由\n\n### 运行流程及相关API\n\n使用`d2admin`的原有登录逻辑，全局路由守卫中判断是否已经拉取权限信息，获取后标识为已获取。\n\n后端需要返回的权限信息包括权限过滤后的角色编码集合，功能编码集合，接口信息集合，菜单列表，路由列表，以及是否系统管理员标识。格式如下\n```js\n{\n  \"statusCode\": 200,\n  \"msg\": \"\",\n  \"data\": {\n    \"userName\": \"MenuManager\",\n    \"userRoles\": [\n      \"R_MENUADMIN\"\n    ],\n    \"userPermissions\": [\n      \"p_menu_view\",\n      \"p_menu_edit\",\n      \"p_menu_menu\"\n    ],\n    \"accessMenus\": [\n      {\n        \"title\": \"系统\",\n        \"path\": \"/system\",\n        \"icon\": \"cogs\",\n        \"children\": [\n          {\n            \"title\": \"系统设置\",\n            \"icon\": \"cogs\",\n            \"children\": [\n              {\n                \"title\": \"菜单管理\",\n                \"path\": \"/system/menu\",\n                \"icon\": \"th-list\"\n              }\n            ]\n          },\n          {\n            \"title\": \"组织架构\",\n            \"icon\": \"pie-chart\",\n            \"children\": [\n              {\n                \"title\": \"部门管理\",\n                \"icon\": \"html5\"\n              },\n              {\n                \"title\": \"职位管理\",\n                \"icon\": \"opencart\"\n              }\n            ]\n          }\n        ]\n      }\n    ],\n    \"accessRoutes\": [\n      {\n        \"name\": \"System\",\n        \"path\": \"/system\",\n        \"component\": \"layoutHeaderAside\",\n        \"componentPath\": \"layout/header-aside/layout\",\n        \"meta\": {\n          \"title\": \"系统设置\",\n          \"cache\": true\n        },\n        \"children\": [\n          {\n            \"name\": \"MenuPage\",\n            \"path\": \"/system/menu\",\n            \"component\": \"menu\",\n            \"componentPath\": \"pages/sys/menu/index\",\n            \"meta\": {\n              \"title\": \"菜单管理\",\n              \"cache\": true\n            }\n          },\n          {\n            \"name\": \"RoutePage\",\n            \"path\": \"/system/route\",\n            \"component\": \"route\",\n            \"componentPath\": \"pages/sys/route/index\",\n            \"meta\": {\n              \"title\": \"路由管理\",\n              \"cache\": true\n            }\n          },\n          {\n            \"name\": \"RolePage\",\n            \"path\": \"/system/role\",\n            \"component\": \"role\",\n            \"componentPath\": \"pages/sys/role/index\",\n            \"meta\": {\n              \"title\": \"角色管理\",\n              \"cache\": true\n            }\n          },\n          {\n            \"name\": \"UserPage\",\n            \"path\": \"/system/user\",\n            \"component\": \"user\",\n            \"componentPath\": \"pages/sys/user/index\",\n            \"meta\": {\n              \"title\": \"用户管理\",\n              \"cache\": true\n            }\n          },\n          {\n            \"name\": \"InterfacePage\",\n            \"path\": \"/system/interface\",\n            \"component\": \"interface\",\n            \"meta\": {\n              \"title\": \"接口管理\"\n            }\n          }\n        ]\n      }\n    ],\n    \"accessInterfaces\": [\n      {\n        \"path\": \"/menu/:id\",\n        \"method\": \"get\"\n      },\n      {\n        \"path\": \"/menu\",\n        \"method\": \"get\"\n      },\n      {\n        \"path\": \"/menu/save\",\n        \"method\": \"post\"\n      },\n      {\n        \"path\": \"/interface/paged\",\n        \"method\": \"get\"\n      }\n    ],\n    \"isAdmin\": 0,\n    \"avatarUrl\": \"https://api.adorable.io/avatars/85/abott@adorable.png\"\n  }\n}\n```\n\n#### 设置菜单\n\n将固定菜单(`/menu/header`、`/menu/aside`)与后端返回的权限菜单(`accessMenus`)合并后，存入相应的vuex store模块中\n```js\n...\nlet allMenuAside = [...menuAside, ...permissionMenu]\nlet allMenuHeader = [...menuHeader, ...permissionMenu]\n...\n// 设置顶栏菜单\nstore.commit('d2admin/menu/headerSet', allMenuHeader)\n// 设置侧边栏菜单\nstore.commit('d2admin/menu/fullAsideSet', allMenuAside)\n// 初始化菜单搜索功能\nstore.commit('d2admin/search/init', allMenuHeader)\n```\n#### 处理路由\n默认使用`routerMapComponents` 的方式处理后端返回的权限路由\n```js\n//处理动态添加的路由\nconst formatRoutes = function (routes) {\n    routes.forEach(route =\u003e {\n        route.component = routerMapComponents[route.component]\n        if (route.children) {\n        formatRoutes(route.children)\n        }\n    })\n}\n...\nformatRoutes(permissionRouter)\n//动态添加路由\nrouter.addRoutes(permissionRouter);\n// 处理路由 得到每一级的路由设置\nstore.commit('d2admin/page/init', [...frameInRoutes, ...permissionRouter])\n```\n\u003e路由处理方式及区别可看后面的相关文章\n\n#### 设置权限信息\n将角色编码集合，功能编码集合，接口信息集合，以及是否系统管理员标识存入相应的vuex store模块中\n```js\n...\npermission.functions = userPermissionInfo.userPermissions\npermission.roles = userPermissionInfo.userRoles\npermission.interfaces = util.formatInterfaces(userPermissionInfo.accessInterfaces)\npermission.isAdmin = userPermissionInfo.isAdmin == 1\n...\n// 设置权限信息\nstore.commit('d2admin/permission/set', permission)\n```\n\n#### 接口权限控制以及loading配置\n\n支持使用角色编码，功能编码以及接口权限进行控制，如下\n```js\nexport function getMenuList() {\n    return request({\n        url: '/menu',\n        method: 'get',\n        interfaceCheck: true,\n        permission:[\"p_menu_view\"],\n        loading: {\n            type: 'loading',\n            options: {\n                fullscreen: true,\n                lock: true,\n                text: '加载中...',\n                spinner: 'el-icon-loading',\n                background: 'rgba(0, 0, 0, 0.8)'\n            }\n        },\n        success: {\n            type: 'message',\n            options: {\n                message: '加载菜单成功',\n                type: 'success'\n            }\n        }\n    })\n}\n```\n`interfaceCheck: true`表示使用接口权限进行控制，如果vuex store中存储的接口信息与当前要请求的接口想匹配，则可发起请求，否则请求将被拦截。\n\n`permission:[\"p_menu_view\"]`表示使用角色编码和功能编码进行权限校验，如果vuex store中存储的角色编码或功能编码与当前表示的编码相匹配，则可发起请求，否则请求将被拦截。\n\n源码位置在`libs/permission.js`，可根据自己需求进行修改\n\n`loading`配置相关源码在`libs/loading.js`，根据自己需求进行配置，`success`也是如此，源码在`libs/loading.js`。 照此思路可以自行配置其它功能，比如请求失败等。\n\n#### 页面元素权限控制\n\n使用指令`v-permission`：\n\n```html\n \u003cel-button\n    v-permission:function.all=\"['p_menu_edit']\"\n    type=\"primary\"\n    icon=\"el-icon-edit\"\n    size=\"mini\"\n    @click=\"batchEdit\"\n    \u003e批量编辑\u003c/el-button\u003e\n```\n参数可为`function`、`role`，表明以功能编码或角色编码进行校验，为空则使用两者进行校验。\n\n修饰符`all`，表示必须全部匹配指令值中所有的编码。\n\n源码位置在`plugin/permission/index.js`，根据自己实际需求进行修改。\n\n使用`v-if`+全局方法：\n\n```html\n\u003cel-button\n    v-if=\"canAdd\"\n    type=\"primary\"\n    icon=\"el-icon-circle-plus-outline\"\n    size=\"mini\"\n    @click=\"add\"\n    \u003e添加\u003c/el-button\u003e\n```\n```js\ndata() {\n    return {\n      canAdd: this.hasPermissions([\"p_menu_edit\"])\n    };\n  },\n```\n默认同时使用角色编码与功能编码进行校验，有一项匹配即可。\n\n类似的方法还要`hasFunctions`，`hasRoles`。\n\n源码位置在`plugin/permission/index.js`，根据自己实际需求进行修改。\n\n\u003e不要使用`v-if=\"hasPermissions(['p_menu_edit'])\"`这种方式，会导致方法多次执行\n\n也可以直接在组件中从vuex store读取权限信息进行校验。\n\n### 开发建议\n\n* 页面级别的组件放到`pages/`目录下，并且在`routerMapCompnonents/index.js`中以key-value的形式导出\n\n* 不需要权限控制的固定菜单放到`menu/aside.js`和`menu/header.js`中\n\n* 不需要权限控制的路由放到`router/routes.js` `frameIn`内\n\n* 需要权限控制的菜单与路由通过界面的管理功能进行添加，确保菜单的`path`与路由的`path`相对应，路由的`name`与页面组件的`name`一致才能使`keep-alive`生效，路由的`component`在`routerMapCompnonents/index.js`中能通过key匹配到。\n\n* 开发阶段菜单与路由的添加可由开发人员自行维护，并维护一份清单，上线后将清单交给相关的人去维护即可。\n\n\u003e如果觉得麻烦，不想菜单与路由由后端返回，可以在前端维护一份菜单和路由(路由中的`component`还是使用字符串，参考`mock/permissionMenuAndRouter.js`)，并且在菜单和路由上面维护相应的权限编码，一般都是使用功能编码。后端就不需要返回菜单和路由信息了，但是其他权限信息，比如角色编码，功能编码等还是需要的。通过后端返回的功能编码列表，在前端过滤出用户具备权限的菜单和路由，过滤处理后后的菜单与路由格式与之前由后端返回的格式一致，然后将处理后的菜单与路由当做后端返回的一样处理即可。\n\n### 数据mock与代码生成\n\n数据mock使用[lazy-mock](https://github.com/wjkang/lazy-mock)修改而来的[d2-admin-server](https://github.com/wjkang/d2-admin-server)，数据真实来源于后端，相比其他工具，支持数据持久化，存储使用的是json文件，不需要安装数据库。简单的配置即可自动生成增删改查的接口。详细用法可看[lazy-mock文档](https://github.com/wjkang/lazy-mock)\n\n后端使用中间件控制访问权限，比如：\n```js\n .get('/menu', PermissionCheck(), controllers.menu.getMenuList)\n```\n`PermissionCheck`默认使用接口进行校验，校验用户所能访问的API中是否匹配当前API，支持使用功能编码与角色编码进行校验`PermissionCheck([\"p_menu_edit\"],[\"r_menu_admin\"],true)`,第一个参数为功能编码，第二个为角色编码，第三个为是否使用接口进行校验。\n\n前端代码生成还在开发中...\n\n### 相关文章\n[vue权限路由实现方式总结](https://juejin.im/post/5b5bfd5b6fb9a04fdd7d687a)\n[vue权限路由实现方式总结二](https://juejin.im/post/5c0b2130f265da615c5913d9)\n[企业管理系统前后端分离架构设计 系列一 权限模型篇](https://juejin.im/post/5b59c2956fb9a04faa79af6f)\n\n\n\u003ca href=\"https://github.com/d2-projects/d2-admin\" target=\"_blank\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/FairyEver/d2-admin/master/doc/image/d2-admin@2x.png\" width=\"200\"\u003e\u003c/a\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwjkang%2Fd2-admin-pm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwjkang%2Fd2-admin-pm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwjkang%2Fd2-admin-pm/lists"}