About the permission scheme design of ant design pro

Access control refers to the control and management of access operations by visitors to protected resources. This control management ensures that authorized persons can access protected resources, and unauthorized persons cannot access protected resources.

Real-life access control can be achieved by payment or authentication. For example: if you go to the cinema to watch a movie, you need enough to buy a movie ticket, otherwise the ticket inspector will not let you in.

There are many models of access control, such as:

  • Discretionary Access Control
  • Mandatory Access Control Model (MAC: Mandatory Access Control)
  • Role-based Access Control (RBAC: Role-based Access Control)
  • Attribute Access Control Model (ABAC: Attribute-Based Access Control)

DAC

Discretionary Access Control (DAC: Discretionary Access Control), the system will identify the user, and then decide whether the user can access the access object based on the information of the access object's access control list (ACL: Access Control List) or access control matrix (ACL: Access Control Matrix). What operations it performs, such as reading or modifying. The user who has the object authority can assign the object authority to other users, so it is called "discretionary" control.

The discretionary access control model is a relatively loose but effective means of protecting resources from unauthorized access and use. It is said to be loose because it is under autonomous control, and it is based on personal will when protecting resources; it is effective because it can be clearly and explicitly pointed out that the subject is accessing or using an object. What kind of permissions to implement, any access behavior beyond the specified permissions will be determined by the access control list and blocked.

A typical scenario is in the Linux file system: every file in the system (some special files may not, such as block device files, etc.) have an owner. The owner of a file is the user (or event, or another file) of the computer that created the file. Then the discretionary access control rights for this file are determined by its creator to set and assign. The owner of the file has access and can assign access to himself and other users

MAC

Mandatory Access Control (MAC: Mandatory Access Control), which is used to manage the information in the system by classification and classification, so as to ensure that each user can only access those users (or other subjects) and files under the marked access control. (or other objects) are marked with fixed security attributes (such as security level, access rights, etc.), and each time an access occurs, the system checks the security attributes to determine whether a user has permission to access the file.

MAC was first mainly used in military applications, usually in combination with DAC. The filtering results of the two access control mechanisms will be accumulated to achieve better access control effects. That is to say, a subject can actually access an object only after passing the double filtering device of DAC restriction check and MAC restriction check. On the one hand, users can use DAC to prevent other users from attacking objects whose ownership belongs to them; Prevent accidental or deliberate misuse of the DAC by other users.

RBAC

Role-based Access Control (RBAC: Role-based Access Control), various permissions are not directly granted to specific users, but a role set is established between the user set and the permission set. Each role corresponds to a corresponding set of permissions. Once a user is assigned an appropriate role, the user has all the operation permissions of the role. At present, the role-based access control model is one of the most widely used, especially in the SAAS field in the 2B direction. That's what we're going to focus on today.

Although RBAC simplifies the management of permissions, it is still not flexible enough for role management in complex scenarios. For example, the permissions between subjects and objects are complex and changeable, and it may be necessary to maintain a large number of roles and their authorization relationships; new objects also need to deal with all related roles. Attribute-based role access control is designed to solve this problem.

ABAC

Attributes-based Access Control is a very flexible access control model. Attributes include the attributes of the request body, the attributes of the request object, the attributes of the request context, the attributes of the operation, and the like. For example, Lao Zhang, who is the head teacher (attribute of subject), can kick (attribute of operation) Xiaoming who is an ordinary student (attribute of object) when he is in class (attribute of context). It can be seen that as long as the attributes are precisely defined and divided, ABAC can realize very complex permission control.

For example, the class cadre (position) of the sophomore (grade) accounting (major) class (class) can upload (operate) the photos of the class on the school intranet (environment).

However, due to the complexity of ABAC, it is a bit overkill for the current SAAS field. Therefore, there are few platforms using ABAC in the SAAS field. Currently, ABAC is mostly used by some cloud services.

RBAC in the stack

The permission scheme of RBAC is used in our product, so we only analyze RBAC at present.

RBAC is role access control, so the first thing we need to know is the role of the user. In this regard, there are two modules of user management and role management in our project.

User Management

Provide functions such as creating, editing and deleting user accounts in the user management of uic.

In Shuixun products, there are tenants, and there is also a user management under each tenant to manage users in the tenant. Ability to set the roles of the current user, these roles include tenant owner, project owner and project manager, etc.

role management

In the role management, you can see the definition of the role and the access rights it has.

Through the user definition in user management and role management, we can get the current user's complete product access rights. When a user enters a function, we can compare the current access rights and the user's access rights, and then draw a conclusion on whether to admit or not.

For us front-end developers, what we need is actually

  1. Get a role permission for the user
  2. Use the obtained permissions to compare and treat the results differently

Then let's take a look at how the permission scheme of ant design pro is handled.

permission scheme in ant design pro

How is the permission scheme in the more common ant design pro in the industry designed?

Get user role permissions

At the beginning, when entering the page, a login verification will be performed. If you are not logged in, it will jump to the login page and perform the login operation. After the login is successful, the role data of the current user will be stored in the localStorage through the setAuthority method, so that we can get it when we re-enter the page. For those who have passed the login verification, they will directly enter the project and render the basic layout of the page. Subsequent permission checks will use CURRENT, which is critical.

The renderAuthorize method is a currying function that assigns a value to CURRENT when the role data is obtained using getAuthority internally.

let CURRENT: string | string[] = 'NULL';

type CurrentAuthorityType = string | string[] | (() => typeof CURRENT);
/**
 * use  authority or getAuthority
 * @param {string|()=>String} currentAuthority
 */
const renderAuthorize = (Authorized: any) => (currentAuthority: CurrentAuthorityType) => {
  if (currentAuthority) {
    if (typeof currentAuthority === 'function') {
      CURRENT = currentAuthority();
    }
    if (
      Object.prototype.toString.call(currentAuthority) === '[object String]' ||
      Array.isArray(currentAuthority)
    ) {
      CURRENT = currentAuthority as string[];
    }
  } else {
    CURRENT = 'NULL';
  }
  return Authorized;
};

export { CURRENT };
export default (Authorized: any) => renderAuthorize(Authorized);

At this point, the permission acquisition and update of the project is completed. The next step is to verify the permissions.

Verify permissions

For permission verification, the following environment parameters are required:

  1. authority: the current access authority is the access authority
  2. currentAuthority: The role of the current user, which is CURRENT
  3. target: Verify the successfully displayed component
  4. Exception: Validation failed to display the component

For components that need permission verification, use Authorized components to combine. Inside the Authorized component, the checkPermissions method is implemented to verify the current user role and whether it has permission to access. If there is permission, the current component will be displayed directly, if not, the message such as no permission will be displayed.

 

Implementation of Authorized Components

type IAuthorizedType = React.FunctionComponent<AuthorizedProps> & {
  Secured: typeof Secured;
  check: typeof check;
  AuthorizedRoute: typeof AuthorizedRoute;
};

const Authorized: React.FunctionComponent<AuthorizedProps> = ({
  children,
  authority,
  noMatch = (
    <Result
      status="403"
      title="403"
      subTitle="Sorry, you are not authorized to access this page."
    />
  ),
}) => {
  const childrenRender: React.ReactNode = typeof children === 'undefined' ? null : children;
  const dom = check(authority, childrenRender, noMatch);
  return <>{dom}</>;
};

function check<T, K>(authority: IAuthorityType, target: T, Exception: K): T | K | React.ReactNode {
  return checkPermissions<T, K>(authority, CURRENT, target, Exception);
}

/**
 * 通用权限检查方法
 * Common check permissions method
 * @param { 权限判定 | Permission judgment } authority
 * @param { 你的权限 | Your permission description } currentAuthority
 * @param { 通过的组件 | Passing components } target
 * @param { 未通过的组件 | no pass components } Exception
 */
const checkPermissions = <T, K>(
  authority: IAuthorityType,
  currentAuthority: string | string[],
  target: T,
  Exception: K,
): T | K | React.ReactNode => {
  // 没有判定权限.默认查看所有
  // Retirement authority, return target;
  if (!authority) {
    return target;
  }
  // 数组处理
  if (Array.isArray(authority)) {
    if (Array.isArray(currentAuthority)) {
      if (currentAuthority.some((item) => authority.includes(item))) {
        return target;
      }
    } else if (authority.includes(currentAuthority)) {
      return target;
    }
    return Exception;
  }
  // string 处理
  if (typeof authority === 'string') {
    if (Array.isArray(currentAuthority)) {
      if (currentAuthority.some((item) => authority === item)) {
        return target;
      }
    } else if (authority === currentAuthority) {
      return target;
    }
    return Exception;
  }
  // Promise 处理
  if (authority instanceof Promise) {
    return <PromiseRender<T, K> ok={target} error={Exception} promise={authority} />;
  }
  // Function 处理
  if (typeof authority === 'function') {
    const bool = authority(currentAuthority);
    // 函数执行后返回值是 Promise
    if (bool instanceof Promise) {
      return <PromiseRender<T, K> ok={target} error={Exception} promise={bool} />;
    }
    if (bool) {
      return target;
    }
    return Exception;
  }
  throw new Error('unsupported parameters');
};

Using Authorized Components

It is very convenient to use on the page. For the components that need to be managed and controlled, Authorized components can be used to combine them.

function NoMatch = () => {
	return <div>404</div>
}

<Authorized authority={'admin'} noMatch={NoMatch}>
  {children}
</Authorized>

We can also use routing to match components, here is the use of in v4, no matter where the component is written, only match, where the sub-component will be rendered.

<Authorized
    authority={authority}
    noMatch={<Route {...rest} render={() => <Redirect to={{ pathname: redirectPath }} />} />}
  >
    <Route
      {...rest}
      render={(props: any) => (Component ? <Component {...props} /> : render(props))}
    />
</Authorized>

Our Permission Scheme

We currently have two sets of permissions, one is the old permission scheme used by products such as offline quality, and the other is the new permission scheme used by asset tags.

old permission scheme

In the old scheme, the method of acquiring data through the interface is adopted, but the acquired data is only at the level of the menu. The obtained data will be stored in the cache, which is convenient for our business packages and sub-products to use. Monitor the change of the page address in the service package, determine whether there is permission to enter the current page, and perform corresponding processing according to the result, which is actually a function of routing guard. In the sub-product, it is judged according to the data whether to display the current menu entry. The combination of the two forms our old scheme.

With the growth of the data stack, the old solution gradually exposed many problems.

  • The scope of permission control is too small, we only control the level of the menu, and for special pages and certain scenarios that require control of functions (such as: editing, adding, deleting, etc.), currently only the back-end interface is restricted , there is no restriction on the page. If you need to implement this function, you need to add additional interfaces and processing logic.
  • We divide the processing of permissions into two parts, the business package and the sub-product, but the coupling between the two is very high. Often, if one place is changed, the other needs to be changed as well.
  • In the business package, we control the page access logic of each product. Whenever we need to add a menu, we need to add a corresponding menu processing logic, and if we add a product, we need to add all the menu logic corresponding to this product. The number of sub-products has exceeded 10+, you can imagine how bloated this part of the processing logic is.

The actual problem is more than the three points listed above, but these three points are enough for us to explore new permission schemes.

New permission scheme

In the new solution, the business package only retains the public method of permissions, and decentralizes the logic of page permission judgment. Sub-products maintain their own permission judgment logic, and it is very easy to modify the logic of a permission.

Compared with the judgment by roles in ant design pro, in the new scheme, we hand over the judgment logic of role permissions to the backend, and the backend returns the corresponding code code set after corresponding processing.

We define a code code for each module that needs to set access permissions, and compare whether the same code can be found in the collection returned by the backend.

After doing this, we only need to care whether we can enter.

When the permission point is obtained, it will also cache the route list that has permission to access according to the permission point. When the route changes, you can go to the authorized route list to search, and if not found, it will be redirected, etc. operation, that is, the function of the routing guard.

Summarize

After the above introduction, we have some understanding of the permission scheme, which is mainly divided into two stages:

  1. Permission acquisition stage: In the permission acquisition stage, when the user logs in or enters the project, the corresponding permissions are obtained according to the user information at the first time.
  2. Permission verification stage: Through the user's permission, compare with the access permission of the current module, and then operate according to the result.

After knowing this, you can formulate a corresponding permission plan based on your own scenario.

{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/3869098/blog/5392362