How does the front end control permissions according to different users?

For most background management, role permissions are an important link. Through the configuration of role permissions, we can easily adjust the permissions of each module or page owned by each user, so that users can only access the pages with corresponding permissions.

Generally speaking, it means which pages are open to all users, which can only be accessed after logging in, which can only be accessed with xx role permissions, etc. (xx here refers to the roles of administrators and ordinary members).

In the background management system, the scheme design of role permission is very important.

  • First, good design can save a lot of effort for the new modules or pages.
  • Second, good design can provide more maintenance and design ideas for later extended functions (such as permission specific control of a button, etc.).
  • Third, good design can make the code more readable and distinguish the permission code from the business code at a glance.

For role permissions, the real check should be the back end. First, because the relevant code verification of the front end can be passed by data fraud, the security is not high. Secondly, in a system, the interface called by the front end should not be called without permission and return data. Therefore, the back-end of the interface must be controlled strictly according to the permission, and be careful not to call directly without permission to get data return. In short, even if the front end does not control the page and permission, the user cannot obtain the relevant data and operations of the page or module without permission. The back end should be able to judge that he has exceeded his authority to access and refuse to return the data. However, if there is no front-end control, the experience of the whole system will be very bad, such as various error reports when accessing unauthorized pages, etc. "Therefore, the more responsibility of the front end in role permissions should be to improve the user's interactive experience."

In the whole process of role permission control, the front-end process steps should first display the default page without login (such as 404 page, login page and registration page), then call the back-end interface to get the permission data of the account given by the back-end during login or browser refresh, and then inject the data into the system, After getting the permission data, the whole system starts to generate the display content and page navigation of the page, and finally generates a system that only displays the corresponding permissions of the current user. So as to control the permissions of the whole role. To sum up, the front-end should be more responsible for improving the user's interactive experience in role permissions.

The implementation of front-end role permissions is described from the following three aspects

"Login permission control"

Role permission control

Content permission control

Login permission control

Login permission control, in short, is to realize which pages can be accessed by unlisted users and which pages can be accessed only after users log in.

It is also very simple to realize this function. Here are two common implementation schemes.

1

The first method is to put the page routes without login together. The code is as follows:

let invisible = [
  {
    path: '/login', //Login page
    name: 'Login',
    component: Login,
  },
  {
    path: '/404',
    name: 'index-notFount',
    component: () => import('@/pages/core/NotFount/index'),
  },
];

export default invisible;

Define an invisible array, which contains all page routes that can be viewed without login.

//Introduce a page that does not require login
  import invisible from './invisible';

  let router = new Router({
    routes: [
      ...invisible,
    ],
  });

  const invisibleMap = [];
  invisible.forEach(item => {
    if (item.name) {
      invisibleMap.push(item.name);
    }
  });

  router.beforeEach(async (to, from, next) => {
    if (!invisibleMap.includes(to.name)) {
        //Business logic judgment login, etc
    }
    else {
      next();
    }
  })

Introduce invisible, map the route name to the invisibleMap array, and intercept the judgment in the route guard. Thus, the pages that do not need to log in can be viewed directly (placed in the invisible array), and the pages that need to log in will make business judgments such as login.

2

In addition to the above methods, you can also add meta to the routing object to realize the permission control of the login page. The relevant codes are as follows:

export const routes = [
      {
         path: '/login', //Login page
         name: 'Login',
         component: Login,
      },
      {
         path:"/list", //List page
         name:"List",
         meta:{
            need_login:true //Login required
         }
      }
    ]

The code is as shown above. There is no need to log in on the login page, so there is no need to set meta need_login attribute, but the list page needs to log in, so set need_login attribute.

Like the first method, the real interception is in the routing guard. The code is as follows:

router.beforeEach((to, from, next) => {
  if (to.meta.need_login) {
    //Business logic judgment login, etc
      
  } else {
    next();
  }
});

Get need here_ The login field determines whether it is the routing page to log in. If so, the next login logic judgment will be carried out. If not, it can be released.

Role permission control

Before discussing role permission control, we should first make it clear that in a system that introduces the role concept, any account in the system should have at least one or more role identities, so that the account has the relevant permission functions of the current role (or roles). In short, we do not directly grant permissions to users, but through roles. Role permission control is mainly used to give different permissions to different roles, so as to give different account permissions. Next, let's understand the concept of role.

In a system, there are three common roles: ordinary member, administrator and super administrator. Ordinary members can browse modules a, b and c of the system, but they cannot view and edit modules d and E (assuming that only modules d and E can be edited). The administrator has all the permissions of ordinary members. In addition, it can view d, e modules and edit d modules. The super administrator has all the permissions of this system, so there is one more editing e module than the administrator.

Of course, in terms of the above, there are some simple roles. This article does not do more in-depth discussion here.

So can the role permissions be designed with the front end as the leading role? (that is, the back end only identifies the account as a certain role, and the control of role permissions is dominated by the front end)

We use a simple example to answer the above questions.

Let's make a simple design according to the more common roles above.

export const permission = {
   member:["Home"], //Ordinary member
   admin:["Home" ,"Notify"],  //Administrator
   super_admin:["Home" ,"Notify","Manage"]  //Super administrator
 }

In the above role permissions, ordinary members have home page permissions, administrators have home page and notification permissions, and super administrators also have additional management permissions.

If the front end is dominant, the back end should return the roles of the current account in the login interface. After getting the role of the account, go to the above configuration file to get the page permissions that the role can access, and then load these page permissions into the system to achieve the purpose of permission control (it should be noted that the value in the array should match the routing name of the corresponding page).

In the above design, the back-end is only responsible for identifying the corresponding role for the account, and in the write in process, it will return to the role corresponding to the front-end account when logging in. At this stage, some students may have questions. Isn't that good? The front end can also control role permissions. Don't worry, let's think about a problem now. "If an online system project needs to urgently add a role, such as x, then the front end needs to modify the configuration file urgently (the configuration file is shown in the figure above) At this time, it is not enough to move the user's role to y before moving the user's role to y in the front-end library. Such changes are very error prone and complex. "

To sum up, the best way to configure role permissions is to leave it to the back-end. The logic of which roles there are and which roles the account corresponds to should be the responsibility of the back-end. The back-end directly returns the permissions of the account through login. There is no need to pay too much attention to the front-end. The main responsibility of the role should be to return according to the permissions of the back-end, Display the corresponding permission page and menu. In this way, even if the above modifications are encountered, they can be solved easily and flexibly.

The following describes the scheme of role permissions.

For example, the account permission structure returned by the backend is as follows

{
   "home": {
     "id":"100",
     "name":"home",
     "desc":"home page",
     "value":true,
     "children": [],
   }
 }

In this permission structure, id is the unique id of the page or module. Here, name should preferably correspond to the name value of the front-end routing page object. desc is the name displayed on the menu, and value represents whether the module or page is displayed. The children array is the secondary page array of the page, It has an important impact on routing permission control and menu rendering.

In this structure, the front end determines whether the page has permission to display by judging value. children is the current page or the secondary and tertiary pages under the module. The structure should be the same as home. If the value of the first level page is false, the second and third levels below should not have the right to display.

At this time, the front end needs to recursively traverse the structure returned by the back end. When it is judged that value is false, filter out the corresponding routing page.

Author: finobird
Link: https://www.zhihu.com/question/59644490/answer/2215890783
Source: Zhihu
The copyright belongs to the author. For commercial reprint, please contact the author for authorization, and for non-commercial reprint, please indicate the source.

//Method for generating filter route and menu
function filterRouter(arr, obj, type) {
  if (Array.isArray(obj)) {
    //Array processing
    obj.forEach(item => {
      handleRouterItem(arr, item, type);
    });
  } else {
    //Object processing
    for (let item in obj) {
      handleRouterItem(arr, obj[item], type);
    }
  }
}

//Process each element node
function handleRouterItem(arr, item, type) {
  //Make sure this page or module is not displayed
  if (item.value === false) {
    if (type === 'menu') {
      assistance(arr, routerMap[item.name]);
    } else {
      assistanceRouter(arr, routerMap[item.name]);
    }
  } else if (item.childrens && item.childrens.length > 0) {
    filterRouter(arr, item.childrens, type);
  }
}

function assistanceRouter(arr, name, obj) {
  for (let i = 0; i < arr.length; i++) {
    if (arr[i].name === name) {
      //Set the meta field on the page without permission or delete it directly
      // arr.splice(i, 1);
      Vue.prototype.$set(arr[i].meta, 'hasRoleAuth'false);
      return true;
    } else {
      if (arr[i].children && arr[i].children.length > 0) {
        if (assistanceRouter(arr[i].children, name, arr[i])) {
          return;
        }
      }
    }
  }
}

function assistance(arr, name, obj) {
  for (let i = 0; i < arr.length; i++) {
    if (arr[i].name === name) {
      arr.splice(i, 1);
      return true;
    } else {
      if (arr[i].children && arr[i].children.length > 0) {
        if (assistance(arr[i].children, name, arr[i])) {
          return;
        }
      }
    }
  }
}

export const rolePermission = () => {
  //router is the routing structure of all pages, and roleroter is the role permission object returned by the back end
  filterRouter(router, roleRouter);
  router.addRoutes(router);
}

In the above code, router is the front-end routing object array, and roleroter is the corresponding data structure of the role permissions of the account returned by the back-end.

In the filterRouter function, traverse each item in the roleroter data structure, and give the processing logic of each item to handleRouterItem.

In the handleRouterItem function, judge whether the value field of each item is false. If it is false, it indicates that the module or page has no permission to display. Then it should be handed over to the assistancyrouter and assistancyto filter out the module or page.

In the assistancyrouter and assistance functions, their main function is to find the routing object with the same name value and parameter name in the array routing object. In the assistancyrouter function, the hasRoleAuth field is marked in the meta object to represent that they have no access to the routing permission. They can also be filtered like the assistance function. In assistance, unauthorized page filtering is used for menu generation.

The above method is a way to filter the existing routing structure by recursively traversing the permission field at the back end, so as to generate the routing structure and menu corresponding to the permission.

In this way, the user can only access and view the corresponding page according to the permission rules in his corresponding permission list.

The code of dynamically adding route rolePermission is best encapsulated separately, because users need to call it when logging in and refreshing the page.

Exit and switch users

In the system of introducing role permissions, it is also very important to exit and switch users. Because the permissions of different accounts are often different. Therefore, pay special attention not to bring the permission information of the previous account when exiting and switching accounts, otherwise serious vulnerabilities will be caused.

So what solutions can we take for the exit and logoff of role permissions.

There are two solutions.

The first scheme is that users refresh the browser after exiting or switching accounts, but this scheme will bring users a less friendly experience.

The second scheme is to initialize the relevant routing instance after the user exits. The code is as follows:

import Router from 'vue-router';
import router from '@/router';
import store from '@/store/index.js';
import invisible from '@/router/invisible';

export const resetRouter = () => {
  let newRouter = new Router({
    routes: [...invisible],
  });
  router.matcher = newRouter.matcher;
  store.commit('CLEAR_ROLE_AUTH');
};

Initialize the dynamic routing of the current account and clear the permission information of the current role in vuex.

Content permission control

In the role permissions of the previous part, it allows different accounts to access different pages, but sometimes it is necessary to control an element in the page more finely. For example, adding, deleting and changing one by one corresponds to a button. At this time, it is necessary to control the content permissions of the page.

In this paper, we simply use addition, deletion and modification as content permission control content.

After communication, the back-end return structure should be as follows:

{
   "home": {
     "id":"100",
     "name":"home",
     "desc":"home page",
     "value":true,
     "children": [],
     "options": {
      "create"true,
        "delete"true,
        "update"true,
     }
   }
 }

In the current structure, there are three content permission controls on the home home page, namely create, delete and update (if you need to add a new one, you can add it in options after communicating with the back end).

After obtaining such a data structure, we need to design a scheme to associate the permission structure with the page content. Here, we use instructions. We create a global custom instruction permission, and the pseudo code is as follows:

import router from '@/router';
import store from '@/store';

app.directive('permission', {
  mounted(el, binding, vnode) {
    const permission = binding.value; //Get instruction value
    const current_page = router.currentRoute.value.name; //Get current route name
    const options = getOptions(current_page) //The getOptions method is to get the role permission object corresponding to the route name
    if (!options[permission]) {
      el.parentElement.removeChild(el); //You do not have permission to this content
    }
  },
});

In the above code, first get the instruction value, then get the current route name, get the relevant objects in the role permission data structure corresponding to the route name through the getOptions method, and then judge whether there is the content permission in the options. If not, remove the dom.

In html, the instructions are used as follows:

<template>
    <div>
      <button v-permission="'create'">establish</button>  
   <button v-permission="'update'">modify</button>
      <button v-permission="'delete'">delete</button>
    </div>
</template>

In short, it is to bind the data structures related to role permissions with dom instructions.

For special business scenarios, such as confusion in style and incongruous UI design caused by hiding. At this time, you should judge whether to hide or pop up the prompt without permission according to the needs of the project, which will not be described too much in this paper.

"Permission control at the front end should be more to optimize the user experience. In addition, it also strengthens a layer of protection for applications. However, it should be noted that the relevant verification at the front end can be cracked by technical means. However, permission issues are related to the safety of all data in the software system."

Therefore, in order to ensure the smooth operation of the system, the front and rear ends should do their own authority protection.

Added by jmajeremy on Mon, 03 Jan 2022 11:52:22 +0200