RBAC permission management

I have been using Ant Financial’s Ant Design UI framework and its out-of-the-box mid-platform front-end/Design solution Ant Design PRO for nearly two years (the Christmas storm last year had some influence, I hope similar things will not happen again). The framework is constantly updated and iterated. However, the usage scenarios of the part related to permission management are still limited and not compatible with the specific action scenarios that need to be refined into each module. It’s better to teach a man how to fish than to give him a fish.

Design idea

Even if it’s your own wank, you still have to stand on the shoulders of your predecessors, and the code you’re designing is not elegant enough. Learn from the president of our company (bold nickname, specific why called president can be found on www.luweitech.cn/, may be able to find (#^.^#)) – write code as elegant as poetry.

I searched around and finally chose a design idea — RBAC, RBAC role-based access control (English: Role-based access control (RBAC) can be simply summarized as who, What and how. That is, who performs how operation on what, which can be translated into Cantonese as “there is a pretty boy doing left drops in a field”.

A simple picture (the picture is stolen ~) understanding:

That is, Joe and Joe are the “sales roles”, and the “sales roles” have the rights to view the “customer list” and “Edit customer”. Naturally, Joe and Joe have the rights to view the “customer list” and “edit Customer”.

To complete the picture (also stolen) :

As can be seen from the above, there are three core steps:

  1. Define roles
  2. Authorization Specifies the rights of the role
  3. Specify roles for users

To prepare

I think the core is still the above design ideas, the specific code implementation is just the expression of ideas, the subsequent packaging more general release of the complete version of it.

Ps: The version of Ant-Design-Pro used is 2.2.1, which is quite old and has not been upgraded to the latest one. You can use the latest version to polish

Authorization Specifies the rights of the role

The role definition step is relatively easy, so I skip it

The second step is to grant privileges to the role. First, the effect picture:

There are several key steps in this step:

  1. therouter.config.jsTo display the data in the figure above
  2. Build the interaction logic shown above
  3. Sends the user selected permissions to the background in a specific format

A section of router.config.js is shown below

export default [
  // user. Save space, omit// app
  {
    path: '/'.component: '.. /layouts/BasicLayout'.Routes: ['src/pages/Authorized'].routes: [{path: '/'.redirect: '/welcome'}, {name: 'welcome'.path: '/welcome'.icon: 'smile'.component: './Welcome/Welcome'.power: ['MENU'],}, {name: 'revenueManagement'.path: '/revenueManagement'.icon: 'pay-circle'.power: ['MENU'].routes: [{name: 'userDeposit'.path: '/revenueManagement/userDeposit'.component: './UserDeposit/UserDeposit'.power: ['MENU'.'CONTENT'.'EXPORT'],}, {name: 'userConsumptions'.path: '/revenueManagement/userConsumptions'.component: './UserConsumptions/UserConsumptions'.power: ['MENU'.'CONTENT'.'EXPORT'],}, {name: 'staffTuningLogs'.path: '/revenueManagement/staffTuningLogs'.component: './StaffTuningLogs/StaffTuningLogs'.power: ['MENU'.'CONTENT'.'EXPORT'],}, {name: 'userAccount'.path: '/revenueManagement/userAccount'.component: './UserAccount/UserAccount'.power: ['MENU'.'CONTENT'.'EXPORT'.'GIVE_COIN'[,},]},],},];Copy the code

The key is to prepare these data :(you’re smart enough to know that _ is lodash)

Param {Array} data router.config.js: /** * {param {Array} data router.config.js RouterConfig[1]. Routes. Do not pass RouterConfig[1]. Routes. @returns {Array} RouterConfig[1]. Routes. Filter item */ without power attribute
function filterRouter(data) {
  return data.filter((item) = > {
    if (item.routes) {
      item.routes = filterRouter(item.routes);
    }

    returnitem.power; })}/** * Change the power property of the data after filterRouter and memoizeOneFormatter comes out to [{label: "view menu ", value: * @param {Array} data RouterConfig[1]. Routes Running filterRouter and memoizeOneFormatter * @returns {Array} Data after modifying the power property */
function setPowerText(data) {
  return data.map((item) = > {
    if (item.children) {
      item.children = setPowerText(item.children);
    }

    item.power = item.power.map((powerItem) = > {
      return {
        label: powerName[powerItem],
        value: powerItem,
      }
    });

    return item;
  });
}

/** ** if path is key and power is value, filterRouter and memoizeOneFormatter will be created. @param {Array} data RouterConfig[1]. Routes filterRouter and memoizeOneFormatter * @returns {Object} * For example: {'/list': ['MENU'], '/list/basic-list': ['MENU', 'CONTENT', 'ADD', 'UPDATE', 'DELETE'], '/exception': ['MENU'], } */
function getAllPowerKeyValue(data) {
  let result = {};

  const recursion = (data) = > {
    data.forEach((item) = > {
      result[item.path] = item.power;

      if(item.children) { recursion(item.children); }}); } recursion(data);return result;
}

const powerOriginData = filterRouter(_.cloneDeep(RouterConfig[1].routes)); // Filter items that have no power attribute
const localePowerOriginData = memoizeOneFormatter(powerOriginData, undefined); // Change the name to the corresponding language. Note that after this function, the original routes is changed to children
const powerTextData = setPowerText(_.cloneDeep(localePowerOriginData));
const allPowerKeyValueData = getAllPowerKeyValue(_.cloneDeep(localePowerOriginData));
Copy the code

PowerOriginData filters out items that do not have power attributes to reduce the number of subsequent computations.

PowerTextData is purely for display, converting the action identifier into Chinese for the user to look at when selecting

AllPowerKeyValueData is used to facilitate subsequent calculations. Remove all superfluous fields from router.config.js, leaving the key (using the module’s path as the key) and the corresponding power.

Prepare the presentation data, the interaction logic behind and send to the background is simple, no wordy ~

use

  1. Define roles
  2. Authorization Specifies the rights of the role
  3. Specify roles for users

After completing the above three steps, the next module is directly used, which is divided into two parts:

  1. Get the user’s permissions at login and initialize the sidebar
  2. Lock the actions in each module

Logon-time interception

  1. After login, the user requests data from the background interface based on the current user information to obtain all permissions of the current user
    • For example, if the background returns the following data (below step 2)
    • After obtaining the data returned from the background, format the above data into:{/authority: ["MENU"], /authority/role: ["MENU", "CONTENT", "ADD", "UPDATE", "TRIGGER", "RESOURCE_AUTHORIZE"]}, marks the permissions of the role matching the current user in each route (page), and saves the permission in the local storage. The field is named as:curStaffAuthorized
  2. After entering the main page, loadsrc/layouts/BasicLayout.jsComponent will construct the sidebar insrc/models/menu.jsgetMenuDataPut the above cachecurStaffAuthorizedTo convert the data in the sidebar as follows:V2.0 permission control)
    • getMenuDatapayloadOne of the parametersroutesconfig/router.config.jsAll routes in
    • In combination with the cachecurStaffAuthorizedTo determine which routes the current user has permission for, and which routes do not, and to delete routes that do not have permission from the data to render to the sidebar
Background return format :("/authority"-- This key is a route, representing the route or the permissions on the page) {"/authority":[{permission_id: 1, action: "MENU", name: "Role Permission Management - Role Management -MENU", description: ""}]."/authority/role":[
    {permission_id: 2, action: "MENU", name: "Role Permission Management - Role Management -MENU", description: ""},
    {permission_id: 3, action: "CONTENT", name: "Role rights Management - Role Management -CONTENT", description: ""]}},Copy the code

Lock the actions in each module

This step is relatively easy (but very troublesome, wondering if there is a better way)

In Pages, check whether the permission is authorized according to path and curStaffAuthorized, and then control whether the corresponding function is displayed according to the identification, for example:

let path = props.match.path;
this.contentPower = checkPower(CONTENT, path);
this.addPower = checkPower(ADD, path);
this.updatePower = checkPower(UPDATE, path);
this.deletePower = checkPower(DELETE, path);
this.triggerPower = checkPower(TRIGGER, path);
Copy the code

{this.addPower && <Button icon="plus" type="primary" onClick={this.handleAddClick}> Create </Button>}

Wu Qin hair

Reed Technology Web front-end development engineer, COO

Good at website construction, public number development, micro channel small program development, small games, public number development, focus on front-end framework, server-side rendering, SEO technology, interaction design, image rendering, data analysis and other research, interested partners to flirt us ~ [email protected]

Visit www.luweitech.cn/ to learn more