遊戲陪玩系統原始碼的許可權設計,如何基於位運算實現?

雲豹科技程式設計師發表於2021-11-03

由於這裡 遊戲陪玩系統原始碼實現的許可權設計是基於 Bit 的所以需要大家對位以及位操作符需要有一定的認識。

前置知識

  • 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 得到的為「查」,又將「增」許可權刪除了

在這裡插入圖片描述
複合型別

我們可以使用複合操作在遊戲陪玩系統原始碼中進行更方便快捷的操作,可用於上面任意操作,這裡只用校驗來舉例

當遊戲陪玩系統原始碼的頁面中有下圖中的操作列,既有刪除按鈕也就修改按鈕,就出現下面幾種情況:

  • 當有「刪」許可權時顯示Delete按鈕
  • 當有「改」許可權時顯示Edit按鈕
  • 當「刪」「改」都沒有時將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 資料

優點

  • 一個引數可以代表多種型別,不需要多個許可權編碼
  • 遊戲陪玩系統原始碼開發可以使用複合型別,比如既有新增又有修改則可以定義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; 驗證當前使用者有 寫 許可權

以上就是“基於位運算實現遊戲陪玩系統原始碼的許可權設計”的全部內容,希望對大家有幫助。

本文轉載自網路,轉載僅為分享乾貨知識,如有侵權歡迎聯絡雲豹科技進行刪除處理
原文連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69996194/viewspace-2840458/,如需轉載,請註明出處,否則將追究法律責任。

相關文章