Role permissions are an important part of most management backends. 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 corresponding permissions of the page.

In plain English, which pages are open to all users, which pages need to be logged in to access, and which pages need to be accessed by xx roles (xx refers to the roles of administrators, ordinary members, etc.).

The scheme design of role authority is very important in background management system.

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

For role permissions, the real gatekeeper should be the back end. First of all, because the front-end of the relevant code verification can be passed by data fraud, security is not high. Second, interfaces called by the front end in a system should not be called through and return data. Therefore, the back end of the interface must be strictly controlled according to the permission, beware of directly calling the data returned without permission. In short, even if the front end does not control the page and permission, users can not obtain the relevant data and operations of the page or module without permission, the back end should be able to judge that he exceeded his authority to access and refuse to return data. However, if there is no front-end control, the experience of the whole system will be very bad, such as various error problems when accessing the unauthorized page and so on. “Therefore, the front-end should be more responsible for improving the user interaction experience in the role permissions.”

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

This section describes the implementation of front-end role permissions 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 users who have not logged in, and which pages can be accessed only after users have logged in.

Implementing this feature is also simple, and two common implementations are listed below.

1

The first is to route together pages that do not require login 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;Copy the code

Define an invisible array that contains all the page routes that can be viewed without logging in.

// 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)) {else {next(); }})Copy the code

Invisible was introduced, where route names are mapped to the invisibleMap array, intercepting judgments in the route guard. In this way, the pages that do not need to log in can be viewed directly (in an invisible array), and the pages that need to log in can be judged.

2

In addition to the above methods, you can also add meta to the route object to achieve the login page permission control, the relevant code is as follows:

Export const routes = [{path: '/login', // Login,}, {path:"/list", // list page name:" list", meta:{need_login:true // Login required}}]Copy the code

As shown in the code above, the meta. Need_login property is not required for the login page, while the need_LOGIN property is required for the list page.

As with the first method, the real interception is in the route guard, with the following code:

Router.beforeeach ((to, from, next) => {if (to.meta. Need_login) {// Business logic to determine login etc} else {next(); }});Copy the code

The need_login field is obtained to determine whether it is the routing page to be logged in to. If it is, the login logic is determined for the next step.

Role Permission Control

Before discussing role permissions, it is important to understand that in systems where roles are introduced, any account in that system should have at least one or more role identities, so that the account has permissions and functions related to the current role (or roles). In short, we do not grant permissions to users directly, but through roles. Role permission control is mainly used to grant different permissions to different accounts for different roles. Next, understand the concept of roles.

There are three common roles in a system: common member, administrator, and super administrator. A normal member can browse modules A, B, and C of the system, but it cannot view or edit modules D and E (assuming only modules D and E are editable). The administrator has all the privileges of a regular member, plus it can view modules D and E and edit modules D. The super administrator has all permissions on this system, so there is an edit E module here compared to the administrator.

Of course, as far as the above is concerned, these are simple character divisions. This article does not go into more depth here.

Can role permissions be designed to be front-end dominant? (That is, the back end identifies the account as a certain role only, and the front end controls the role permissions)

Let’s answer this question with a simple example.

Let’s do a simple design based on the more common roles above.

Export const Permission = {member:["Home"], // member:["Home","Notify"], // Super_admin :["Home","Notify","Manage"]Copy the code

Common members have home page rights, administrators have home page and notification rights, and super administrators have management rights.

If the previous end is dominant, the back end should return which roles the current account belongs to in the login interface. After getting the role of the account, I will go to the configuration file above to take out the page permissions that the role can access, and then load this part of the 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 route name of the corresponding page).

In the above design, the back end is only responsible for identifying the corresponding role of the account, writing it into the library, and returning it to the front end when logging in. At this stage, there may be some students have questions, it is not good, the front end can not control the role permissions. Okay, now let’s think about a problem. “If for a system project, has launched a new one is in need of urgent roles such as x, then the front end will need to modify the configuration file (the configuration file as shown above), at this time is not enough, still need to move before the user y x role, so I need to change at this time is not only the front configuration file, the back-end also need move y users x role in library. It’s very error-prone and complicated.”

To sum up, in the role authorization, in fact, the best thing to do is to backend configuration, what are the roles and accounts corresponding to which these logic should be a backend role is responsible for, the back-end returned directly by login the account have permissions, front end this without too much attention to role responsibilities should be according to the backend permission to return, show the corresponding permissions page and the menu. Even if encounter afore-mentioned modification to also can deft nimble solve so.

The following describes the role permission scheme.

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

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

In this permission structure, ID is the unique id of the page or module, name is best corresponding to the name value of the front-end routing page object, desc is the name displayed on the menu, value represents whether the module or page is displayed, children array is the secondary page array of the page, It has important influence on the permission control of route and the rendering generation of menu.

In this structure, the front end determines whether the page has the permission to display by judging the value. Children is the current page or the secondary page or tertiary page under the module, and the structure should be the same as home. If the primary page value is false, the secondary and tertiary pages below should not be displayed.

At this point, what the front end needs to do is recursively traverse the structure returned by the back end, and filter out the corresponding routing page when the value is judged to be false.

Function filterRouter(arr, obj, Type) {if (array.isarray (obj)) {// Array handles obj.forEach(item => {handleRouterItem(arr, item, type); }); } else {for (let item in obj) {handleRouterItem(arr, obj[item], type); Function handleRouterItem(arr, item, 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; If (arr[I].name === name) {// 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 for all pages, RoleRouter is the role permission object returned by the back end filterRouter(Router, roleRouter); router.addRoutes(router); }Copy the code

In the above code, the Router is the front end array of routing objects, and the roleRouter is the data structure corresponding to the role permissions of the account returned by the back end.

In the filterRouter function, we iterate over each item in the roleRouter data structure, passing the processing logic for each item to the handleRouterItem.

The handleRouterItem function checks whether the value field of each item is false. If it is false, the module or page is not allowed to display. You should hand it over to assistanceRouter and Assistance to filter out the module or page.

In the assistanceRouter and assistanceRouter functions, the main function of the assistanceRouter function is to find the route object whose name value is the same as the parameter name. In the assistanceRouter function, the meta object is labeled with hasRoleAuth. The delegate has no access to route permissions and 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 route structure by recursively traversing the permission field at the back end, so as to generate the route structure and menu corresponding to the permission.

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

The part of the code that dynamically adds the route rolePermission is best wrapped separately, because it needs to be called when the user logs in and refreshes the page.

Exit and switch users

Quitting and switching users is also important in systems where role permissions are introduced. Because different accounts have different permissions. Therefore, you should pay special attention to not take the permission information of the previous account when you exit and switch accounts, otherwise serious vulnerabilities will be caused.

So what are the solutions we can take for the exit and logout of role permissions?

There are two solutions.

The first option is for the user to refresh the browser after logging out or switching accounts, but this creates a less user-friendly experience.

The second scheme is to initialize the related route instance after the user exits, with the following code:

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');
};
Copy the code

Initialize the dynamic route of the current account and delete the permission information of the current role in vuEX.

Content permission control

In the role permissions of the last part, it allows different accounts to access different pages, but sometimes it is necessary to control a certain element in the page in a more detailed way, such as adding, deleting and modifying a button one by one. At this time, it is necessary to make content permissions control for the content of the page.

In this paper, it is simple to add, delete and change as content permission control content.

After communication, the return structure of the back end should now look like this:

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

In the current structure, there are three content permission controls on the home page, namely, creation, deletion and update (if new content is needed, it can be added in options after communicating with the backend).

Once we have this data structure, we need to design a scheme that relates this permission structure to the content of the page. In this case, we use directives. We create a global custom instruction permission with the following pseudocode:

import router from '@/router'; import store from '@/store'; app.directive('permission', { mounted(el, binding, vnode) { const permission = binding.value; / / to get instruction value const current_page = router. CurrentRoute. Value. The name; Const options = getOptions(current_page) // Get the current route name const options = getOptions(current_page) options[permission]) { el.parentElement.removeChild(el); }},});Copy the code

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 getOptions method, and then judge whether options has the content permission, if not, remove the DOM.

In HTML, directives are used as follows:

<template> <div> <button v-permission="'create'"> create </button> <button v-permission="'update'"> modify </button> <button </button> </div> </template>Copy the code

In short, the data structures associated with the role permissions are bound to the DOM by instructions.

For special business scenarios, such as hidden style confusion, UI design is not coordinated, etc. At this point, we should determine whether to hide or pop up a message indicating no permission according to the requirements of the project. This article will not do too much narration.

“Permissions control on the front end is more to optimize user experience, but also for the application of a layer of protection, but it needs to be noted that the front-end verification can be cracked through technical means. However, permissions are related to the safety of all data in the software system.”

Therefore, to ensure the smooth running of the system, the front and back ends must protect their permissions.