关于javascript:基于位运算的权限设计

3次阅读

共计 5169 个字符,预计需要花费 13 分钟才能阅读完成。

基于位运算的权限设计

因为这里权限是基于 Bit 的所以须要大家对 以及 位操作符 须要有肯定的意识

[TOC]

前置常识

  • MDN 位运算符
  • 繁多权限有且只有一位为 1
  • 从右向左,由低到高

操作符速记:

  • &按位与:对应位都是 1 则为 1
  • |按位或:对应位都是 0 则为 0
  • ^按位异或:对应位都雷同则是 0,不同则为 1

理论案例

咱们以四种权限的 CRUD 来举例,应用 4 位的 bit 来进行。这里有一点须要留神,繁多权限有且只有一位为 1

变量 二进制 形容
C 0b0001
D 0b0010
U 0b0100
R 0b1000

校验某些权限

const curPermission = 0b1001; // 以后用户的权限,「增」「查」const allowCreate = (curPermission & C) === C; // => true
const allowUpdate = (curPermission & U) === U; // => false

从下面代码可知,以后用户的权限为 Ob1001 第一位与第四位是 1,则阐明领有的权限

当用户的权限应用 按位与 只有雷同位都为 1 时才能够失去 1

由图与代码能够看出,咱们能够应用 & 失去的值比照定义好的变量是否相等可知,以后是否有某个权限

增加某个权限

let curPermission = 0b0100; // 以后用户只有「改」权限

// C = 0b0001  D = 0b0010
// 增加「增」「删」的权限
curPermission = curPermission | C | U; // => 0b0111

最终咱们失去的权限蕴含原有的「改」以及新退出的「增」「删」

删除某个权限

删除时咱们应用先按位取反再按位与 &(~P) 的操作

let curPermission = 0b1110; // 以后用户权限,「删」「改」「查」// R = 0b1000
curPermission = curPermission & ~C; // => Ob0110 删除了「查」的权限

最终咱们失去的权限只有「删」「改」,曾经将「查」权限删除

Toggle 操作

应用按位异或 无则增有则减(对应位不同为 1,雷同为 0),从后果来看实际上是一个 Toggle 操作

let curPermission = 0b1000; // 以后用户权限,「查」// 无则增;C = 0b0001
curPermission ^ C; // => Ob1001 失去的为「增」「查」// 有则减
curPermission ^ C; // => Ob1001 失去的为「查」,又将「增」权限删除了

复合类型

咱们能够应用复合操作来进行更方便快捷的操作,可用于下面任意操作,这里只用 校验 来举例

当咱们的页面中有下图中的操作列,既有删除按钮也就批改按钮,就呈现上面几种状况:

  1. 当有「删」权限时显示 Delete 按钮
  2. 当有「改」权限时显示 Edit 按钮
  3. 当「删」「改」都没有时将 operation 列暗藏

const curPermission = 0b1000; // 以后用户权限
const D = 0b0010; // 删
const U = 0b0100; // 改
const DandU = 0b0110; // 删、改 都有
const allowDelete = (p: number) => (p & D) === D;
const allowUpdate = (p: number) => (p & U) === U;
const allowDeleteAndUpdate = (p: number) => (p & DandU) === DandU;

const COLUMNS = [
  {
    title: 'operation',
    dataIndex: 'operation',
    render: () => (
      <>
        {allowUpdate(curPermission) && <button>Edit</button>}
        {allowDelete(curPermission) && <button>Delete</button>}
      </>
    ),
  },
];
const retColumns = COLUMNS.filter((x) => {if (x.dataIndex === 'operation') {return allowDeleteAndUpdate(curPermission);
  }
  return true;
});
// retColumns 是咱们最终应用的 Table columns 数据

同理,代码中的复合类型能够利用至其它的操作,作为作业有心的同学能够 coding

长处

  • 一个参数能够代表多种类型,不须要多个权限编码
  • 能够应用复合类型,比方既有新增又有批改则能够定义cosnt allowCreateAndUpdate = 0b1010,应用时(curAccess & allowCreateAndUpdate) === allowCreateAndUpdate
  • 可拓展性高,比方再增加一个是否可执行权限,则能够应用 5 位 bit 0b10000

毛病

位运算符将它的操作数视为 32 位元的二进制串 — 来自 MDN

这样的话可用的权限数无限,能够应用 构造体 命名空间 来进行管控

构造体也就是对象,咱们将具体的权限放在定好的构造体中

const permissionList = [
  {
    pid: 1, // position id 也就是 地位 ID
    code: 0b0001, // 对应的编码
  }
];

命名空间,其实也能够应用下面构造体形容,这里咱们应用字符串来进行形容,有一套默认规定:pos,code

const permissionList = ['pos1,0b0001', 'pos2,0b0011'];

在具体应用时依据本人的不同规定编写好对应权限的操作方法,提供给具体的业务同学应用

TypeScript 加成

应用 Enum 与位赋值操作符以及 namespace 的静态方法

/** 定义 */
enum AuthCode {
  Read = 0b001, // 也能够写成 1
  Write = 0b010, // 也能够写成 r << 1 或 2
  /** 执行 execute */
  Exec = 0b100, // 也能够写成 r << 2 或 4

  // 以下为复合类型
  /** 0b011 */
  ReadAndWrite = 0b011,
  /** Union of all host auth */
  HostAuthMask = 0b111,
}
namespace Auth {
  /**
   * 验证以后权限是否存在
   * @param validCode - 要验证的权限编码(即用户返回的编码)* @param code - 定义好的权限编码
   */
  export const validator = (validCode: AuthCode, code: AuthCode): boolean => {return (validCode & code) === code;
  };
  // curry 解决 validator()
  /**
   * 给用户退出权限
   * @param userCode - 以后用户领有的权限
   * @param waitingCode - 待退出给用户的权限
   * @returns 返回退出权限后的所有权限
   */
  export const add = (
    userCode: AuthCode,
    waitingCode: AuthCode | AuthCode[]): AuthCode => {
    let code: number;
    if (Array.isArray(waitingCode)) {code = waitingCode.reduce((acc, cur) => {return acc | cur;}, 0);
    } else {code = waitingCode;}
    return userCode | code;
  };

  /**
   * 删除用户的权限
   * @param userCode - 以后用户领有的权限
   * @param rmCode - 要删除的权限
   */
  export const remove = (
    userCode: AuthCode,
    rmCode: AuthCode | AuthCode[]): AuthCode => {
    let code: number;
    if (Array.isArray(rmCode)) {code = rmCode.reduce((acc, cur) => {return acc | cur;}, 0);
    } else {code = rmCode;}
    return userCode & ~code;
  };

  /**
   * 用户权限 Toggle
   * @description 无则增,有则减
   * @param userCode - 以后用户领有的权限
   * @param tglCode - 要 toggle 的权限
   */
  export const toggle = (userCode: AuthCode, tglCode: AuthCode) => {return userCode ^ tglCode;};
}

// test validator
// const userCode = 0b011; // 获取到用户在当前页的权限码(读、写)// console.log(Auth.validator(userCode, AuthCode.Read)); // => true; 以后用户领有 读 权限
// console.log(Auth.validator(userCode, AuthCode.ReadAndWrite)); // => true; 以后用户领有 读写 权限
// console.log(Auth.validator(userCode, AuthCode.Exec)); // => false; 以后用户没有 执行 权限
// console.log(Auth.validator(userCode, AuthCode.Read | AuthCode.Exec)); // => false; 以后用户没有 读,执行 权限;两个权限都有才为真

// test add
// let userCode = 0b000; // 获取到用户在当前页的权限码(无权限)// console.log((userCode = Auth.add(userCode, AuthCode.Read))); // => 1 === 0b001; 给以后用户退出 读 权限
// console.log(Auth.validator(userCode, AuthCode.Read)); // => true; 验证以后用户曾经领有 读 权限
// console.log(Auth.validator(userCode, AuthCode.Write)); // => false; 验证以后用户没有 写 权限
// console.log((userCode = Auth.add(userCode, [AuthCode.Write, AuthCode.Exec]))); // => 7 === 0b111; 给以后用户退出 写、执行 权限
// console.log(Auth.validator(userCode, AuthCode.HostAuthMask)); // => true; 验证以后用户领有所有权限

// test remove
// let userCode = 0b111; // 获取到用户在当前页的权限码(所有权限)// console.log(Auth.validator(userCode, AuthCode.HostAuthMask)); // => true; 验证以后用户有所用权限
// console.log((userCode = Auth.remove(userCode, AuthCode.Read))); // => 6 === 0b110; 移除用户 读 权限
// console.log(Auth.validator(userCode, AuthCode.Read)); // => false; 验证以后用户曾经删除 读 权限
// console.log(//   (userCode = Auth.remove(userCode, [AuthCode.Write, AuthCode.Exec]))
// ); // => 0 === 0b000; 移除用户 读、执行 权限
// console.log(Auth.validator(userCode, AuthCode.Write)); // => false; 验证以后用户曾经删除 写 权限
// console.log(Auth.validator(userCode, AuthCode.Exec)); // => false; 验证以后用户曾经删除 执行 权限

// test toggle
// let userCode = 0b101; // 获取到用户在当前页的权限码(执行、读)// console.log((userCode = Auth.toggle(userCode, AuthCode.ReadAndWrite))); // => 6 === 0b110; 以后用户删除了 读,增加了 写(写 无则增,读 有则减)// console.log(Auth.validator(userCode, AuthCode.Read)); // => false; 验证以后用户无 读 权限
// console.log(Auth.validator(userCode, AuthCode.Write)); // => true; 验证以后用户有 写 权限

See Also

  • https://segmentfault.com/a/11…
  • https://juejin.cn/post/684490…
正文完
 0