React Permission control scheme practice

Permission control is a common feature in projects, especially back-end management projects. How does react implement permission control

background

  1. The project is built by UMI
  2. Requirements:
    1. Configure route permissions based on role permissions
    2. Control the page display effect according to different permissions
    3. Button show hide

Implement page routing permissions

Implementation effect: The user without permission does not enter the page: the menu on the left does not enter the page, and directly enter the URL prompt no permission

@umijs/plugin-access

Umijs.org/zh-CN/plugi…

Use with @umijs/plugin-access plug-in

src/access.ts

The SRC /access.ts file is a permission definition file that needs to export a method by default that will be executed when the project is initialized. This method returns an object that defines a permission for each value. For details, see the documentation.

Plan 1

My requirement is to judge whether the user has the route permission according to the role of the user. If I follow the demo of the document, I need to first:

  1. Define whether each role has pageA permission, pageB permission…
  2. Again inaccess.tsOutput object in{canReadPageA: true, canReadPageB: false... }
  3. Then define it in the routing fileaccess: 'canReadPageA'.access: 'canReadPageB'

This can be done, but it is too much code, and the access.ts and Route.ts files need to be embedded to define and determine each page that requires permissions

So I changed my mind and went to another plan, which was Plan 2

Scheme 2

In access.ts, when the value of the returned object is a method:

  1. The parameter is route, which is the current route information
  2. Method returns a Boolean value at the end

Using this parameter, we can add the required information to the route

// routes.ts[{name: 'pageA'.path: '/pageA'.component: './pageA'.access: 'auth'.// Permissions define a key for the return value
    roles: ['admin'.'user'].// The pageA page can be accessed when role is admin or user
  },
  {
    name: 'pageB'.path: '/pageB'.component: './pageB'.access: 'auth'.roles: ['admin'].// The pageA page can be accessed only when role is admin},]Copy the code

I’ve configured route with two attributes:

  1. accessA value ofaccess.tsReturns some key of the object, which is fixed toauth
  2. rolesDefine groups of roles that can have permissions on the page

Return an object with key auth in access.ts:

// access.ts
let hasAuth = (route: any, roleId? : string) = > {
  // Critical: Compare Route.roles with currentUser.roleid to determine whether the user has permission
  return route.roles ? route.roles.includes(roleId) : true;
};
export default function access(initialState: { currentUser? : API.CurrentUser |undefined }) {
  const { currentUser } = initialState || {};
  return {
    auth: (route: any) = >hasAuth(route, currentUser? .roleId), }; }Copy the code

Get the information in the route and compare with the current user information, judge, return a Boolean value

The advantage of Option 2, compared to Option 1, is that when a new page is added, only the access and roles attributes of the page need to be defined in routes.ts

Display the permission control page

Implementation effect: the user has a menu entry, enter the page shows no permission

Plan 1

Ideas:

  1. Use umi to provide Access components to achieve
  2. According to thecurrentUserCorresponding field to determine whether there is permission

The code is as follows:

import { Access } from 'umi';

<Access accessible={currentUser.foo} fallback={<div>No permissions</div>}>
  Foo content.
</Access>;
Copy the code

Disadvantages: Need to embed code in the corresponding page

Scheme 2

Ideas:

This is implemented with the higher-order component Wrappers

  1. inroutes.tsconfigurationwrappersattribute
// routes.ts[{name: 'pageA'.path: '/pageA'.component: './pageA'.wrappers: ['@/wrappers/authA'] {},name: 'pageB'.path: '/pageB'.component: './pageB'.wrappers: ['@/wrappers/authB']]},Copy the code

In this way, when accessing /pageA, permission verification is first done with @/wrappers/authA

  1. Then, in@/wrappers/authA,
// wrappers/authA
import { useModel } from 'umi';

export default (props) => {
  const { initialState } = useModel('@@initialState');
  const { currentUser } = initialState || {};
  if (currentUser.authA) {
    return <div>{ props.children }</div>;
  } else {
    return <div>Without permission</div>; }}Copy the code

In this case, the component is rendered based on currentUser

The advantages of Scheme 2 are:

There is no need to embed the code in the page component. You simply configure wrappers in routes.tx, and the authentication part is handled by @/wrappers/

However, the downside is that if you have multiple permissions, such as authA, authB, authC… Then you need to create multiple authentication files at @/wrappers/

Implementing button permissions

Implementation effect: no permission button is not displayed or gray

The general approach is to judge in the component

// Do not display the button
{currentUser.auth ?  <button>create</button> : null}
/ / grey
{<button disabled={currentUser.auth}>create</button>}
Copy the code

But if you have a lot of permission buttons, you’re going to have to write this code multiple times, so wrap the buttons here

// AuthBtn
import React, { useState, useEffect, useRef } from 'react';
import { Button } from 'antd';

const AuthBtn: React.FC<{}> = (props) = > {
  let { authId, children } = props;
  // btnIds should have a back end interface that tells the front end user what button permissions are available
  let btnIds = ['read'.'edit'];
  let hasAuth = btnIds.includes(authId);
  // This can be packaged according to actual requirements
  return <Button disabled={! hasAuth}>{children}</Button>;
};
export default AuthBtn;

// index.ts
<AuthBtn authId="read">Read Read-only permission</AuthBtn>
<AuthBtn authId="write">Write Write permission</AuthBtn>

Copy the code

The authId passed in should be agreed with the background first. You can also pass in type, loading, and so on as required

In this case, use Button for normal buttons and use AuthBtn for authentication

conclusion

The above is a recent practice on permission control, to do the authentication of the route, page and button level, each has its own implementation, each has its own advantages and disadvantages, aimed at more elegant programming! If you have a better solution, please leave a comment in the comment section!