GithubHelp home page GithubHelp logo

wjkang / d2-admin-pm Goto Github PK

View Code? Open in Web Editor NEW
419.0 19.0 119.0 12.43 MB

基于 d2-admin的RBAC权限管理解决方案

Home Page: http://jaycewu.gitee.io/d2-admin-pm/#/index

License: MIT License

JavaScript 39.74% HTML 0.91% Vue 43.81% Shell 0.05% SCSS 15.48%
d2-admin rbac vue2 element-ui axios vuecli3 element vue-admin admin-template

d2-admin-pm's Introduction

代码太丑,不建议照搬,可以看一看 RBAC 的实现思路,这是通用的。

运行使用

git clone https://github.com/wjkang/d2-admin-pm.git

npm install

npm start

需要后端mock服务的支持

git clone https://github.com/wjkang/d2-admin-server.git

npm install

npm start

功能预览

对d2-admin-start-kit的修改

main.jscreated的内容转到router/index.js内,并添加相关逻辑

修改了axios相关代码plugin/axios/index.js,支持接口级权限控制,并且支持配置loading效果

vuex store中添加menu模块添加fullAside,完整路径store.state.d2admin.menu.fullAside

vuex store中添加permission模块,存储用户具备的功能权限码,角色编码,具备访问权限的接口以及是否管理员标识

export default {
    namespaced: true,
    state: {
        //功能编码
        functions: [],
        //角色编码
        roles: [],
        //接口
        interfaces: {
            GET: [],
            POST: [],
            PUT: [],
            DELETE: []
        },
        //是否管理员
        isAdmin: false
    },
    mutations: {
        set(state, data) {
            state.functions = data.functions;
            state.roles = data.roles;
            state.isAdmin = data.isAdmin;
            state.interfaces = data.interfaces;
        }
    }
}

vuex store中的account模块的loadaction的这部分:

 // DB -> store 持久化数据加载上次退出时的多页列表
await dispatch('d2admin/page/openedLoad', null, { root: true })

转到router/index.js内,需要在加载完权限路由后才执行

相关概念

权限模型

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

运行流程及相关API

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

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

{
  "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/[email protected]"
  }
}

设置菜单

将固定菜单(/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])

路由处理方式及区别可看后面的相关文章

设置权限信息

将角色编码集合,功能编码集合,接口信息集合,以及是否系统管理员标识存入相应的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文件,不需要安装数据库。简单的配置即可自动生成增删改查的接口。详细用法可看lazy-mock文档

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

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

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

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

相关文章

vue权限路由实现方式总结 vue权限路由实现方式总结二 企业管理系统前后端分离架构设计 系列一 权限模型篇

d2-admin-pm's People

Contributors

fairyever avatar rockprince avatar seasonrui avatar wjkang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

d2-admin-pm's Issues

折叠侧边栏的时候 鼠标hover图标时 控制台报错

element-ui.common.js?5c96:3682 Uncaught RangeError: Maximum call stack size exceeded.
at VueComponent.handleMouseenter (element-ui.common.js?5c96:3682)
at invoker (vue.runtime.esm.js?2b0e:2023)
at HTMLLIElement.fn._withTask.fn._withTask (vue.runtime.esm.js?2b0e:1822)
at VueComponent.handleMouseenter (element-ui.common.js?5c96:3682)
at invoker (vue.runtime.esm.js?2b0e:2023)
at HTMLLIElement.fn._withTask.fn._withTask (vue.runtime.esm.js?2b0e:1822)
at VueComponent.handleMouseenter (element-ui.common.js?5c96:3682)
at invoker (vue.runtime.esm.js?2b0e:2023)
at HTMLLIElement.fn._withTask.fn._withTask (vue.runtime.esm.js?2b0e:1822)
at VueComponent.handleMouseenter (element-ui.common.js?5c96:3682)

登录过期过问题

登录后,token过期,src/plugin/axios/index.js里只删除了uuid,然后跳转到登录页。此时src/router/index.js 中的isFetchPermissionInfo已经是等于true,路由也已经加载该A用户的权限路由跟菜单,如果登录其他账号B,那么B显示的是A的路由跟菜单。所以如果过期的话,应该需要进行刷新吧

无法登录

您好,我这边这个登陆就登陆不了了,说请求错误

后端mock服务 启动不了

MacBook-Pro:d2-sql eagle$ cnpm start

[email protected] start /www/d2-sql
gulp nodemon

fs.js:27
const { Math, Object } = primordials;
^

ReferenceError: primordials is not defined
at fs.js:27:26
at req_ (/www/d2-sql/node_modules/_natives@1.1.6@natives/index.js:143:24)
at Object.req [as require] (/www/d2-sql/node_modules/_natives@1.1.6@natives/index.js:55:10)
at Object. (/www/d2-sql/node_modules/_graceful-fs@3.0.12@graceful-fs/fs.js:1:37)
at Module._compile (internal/modules/cjs/loader.js:936:30)
at Module._extensions..js (internal/modules/cjs/loader.js:947:10)
at Object.require.extensions. [as .js] (/www/d2-sql/node_modules/_babel-register@6.26.0@babel-register/lib/node.js:152:7)
at Module.load (internal/modules/cjs/loader.js:790:32)
at Function.Module._load (internal/modules/cjs/loader.js:703:12)
at Module.require (internal/modules/cjs/loader.js:830:19)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] start: gulp nodemon
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR! /Users/eagle/.npm/_logs/2019-11-06T10_10_42_067Z-debug.log

0 info it worked if it ends with ok
1 verbose cli [
1 verbose cli '/usr/local/bin/node',
1 verbose cli '/usr/local/lib/node_modules/cnpm/node_modules/.bin/npm',
1 verbose cli '--userconfig=/Users/eagle/.cnpmrc',
1 verbose cli '--disturl=https://npm.taobao.org/mirrors/node',
1 verbose cli '--registry=https://r.npm.taobao.org',
1 verbose cli 'start'
1 verbose cli ]
2 info using [email protected]
3 info using [email protected]
4 verbose run-script [ 'prestart', 'start', 'poststart' ]
5 info lifecycle [email protected]prestart: [email protected]
6 info lifecycle [email protected]
start: [email protected]
7 verbose lifecycle [email protected]start: unsafe-perm in lifecycle true
8 verbose lifecycle [email protected]
start: PATH: /usr/local/lib/node_modules/cnpm/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/www/d2-sql/node_modules/.bin:/Library/Frameworks/Python.framework/Versions/3.7/bin:/Users/eagle/Library/Application Support/GoodSync:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/Ghostscript/bin:/opt/ImageMagick/bin:/Applications/Wireshark.app/Contents/MacOS:/usr/local/git/bin:/Library/Frameworks/Python.framework/Versions/3.7/bin:/Users/eagle/Library/Application Support/GoodSync:/AIRSDK/bin:/Applications/MAMP/Library/bin:/AIRSDK/bin:/Applications/MAMP/Library/bin
9 verbose lifecycle [email protected]start: CWD: /www/d2-sql
10 silly lifecycle [email protected]
start: Args: [ '-c', 'gulp nodemon' ]
11 silly lifecycle [email protected]start: Returned: code: 1 signal: null
12 info lifecycle [email protected]
start: Failed to exec start script
13 verbose stack Error: [email protected] start: gulp nodemon
13 verbose stack Exit status 1
13 verbose stack at EventEmitter. (/usr/local/lib/node_modules/cnpm/node_modules/npm/node_modules/npm-lifecycle/index.js:332:16)
13 verbose stack at EventEmitter.emit (events.js:209:13)
13 verbose stack at ChildProcess. (/usr/local/lib/node_modules/cnpm/node_modules/npm/node_modules/npm-lifecycle/lib/spawn.js:55:14)
13 verbose stack at ChildProcess.emit (events.js:209:13)
13 verbose stack at maybeClose (internal/child_process.js:1021:16)
13 verbose stack at Process.ChildProcess._handle.onexit (internal/child_process.js:283:5)
14 verbose pkgid [email protected]
15 verbose cwd /www/d2-sql
16 verbose Darwin 17.7.0
17 verbose argv "/usr/local/bin/node" "/usr/local/lib/node_modules/cnpm/node_modules/.bin/npm" "--userconfig=/Users/eagle/.cnpmrc" "--disturl=https://npm.taobao.org/mirrors/node" "--registry=https://r.npm.taobao.org" "start"
18 verbose node v12.10.0
19 verbose npm v6.11.3
20 error code ELIFECYCLE
21 error errno 1
22 error [email protected] start: gulp nodemon
22 error Exit status 1
23 error Failed at the [email protected] start script.
23 error This is probably not a problem with npm. There is likely additional logging output above.
24 verbose exit [ 1, true ]

啥问题呀。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.