let r = 0b100let w = 0b010let x = 0b001let user = 0b010;console.log((user & r) === r) // false 没有 r 权限console.log((user & w) === w) // true 有 w 权限console.log((user & x) === x) // false 没有 x 权限那么具体怎么操作呢?其实有两种方案,最简单的就是异或 ^,按照上文的介绍“当两个操作数相应的比特位有且只有一个 1 时,结果为 1,否则为 0”,所以异或其实是 toggle 操作,无则增,有则减:
let r = 0b100let w = 0b010let x = 0b001let user = 0b110 // 有 r w 两个权限// 执行异或操作,删除 r 权限user = user ^ rconsole.log((user & r) === r) // false 没有 r 权限console.log((user & w) === w) // true 有 w 权限console.log((user & x) === x) // false 没有 x 权限console.log(user.toString(2)) // 现在 user 是 0b010// 再执行一次异或操作user = user ^ rconsole.log((user & r) === r) // true 有 r 权限console.log((user & w) === w) // true 有 w 权限console.log((user & x) === x) // false 没有 x 权限console.log(user.toString(2)) // 现在 user 又变回 0b110那么如果单纯的想删除权限(而不是无则增,有则减)怎么办呢?答案是执行 &(~code),先取反,再执行与操作:
let r = 0b100let w = 0b010let x = 0b001let user = 0b110 // 有 r w 两个权限// 删除 r 权限user = user & (~r)console.log((user & r) === r) // false 没有 r 权限console.log((user & w) === w) // true 有 w 权限console.log((user & x) === x) // false 没有 x 权限console.log(user.toString(2)) // 现在 user 是 0b010// 再执行一次user = user & (~r)console.log((user & r) === r) // false 没有 r 权限console.log((user & w) === w) // true 有 w 权限console.log((user & x) === x) // false 没有 x 权限console.log(user.toString(2)) // 现在 user 还是 0b010,并不会新增4. 局限性和解决办法前面我们回顾了 JavaScript 中的 Number 和位运算,并且了解了基于位运算的权限系统原理和 Linux 文件系统权限的实例 。
上述的所有都有前提条件:1、每种权限码都是唯一的;2、每个权限码的二进制数形式,有且只有一位值为 1(2^n) 。也就是说,权限码只能是 1, 2, 4, 8,...,1024,...而上文提到,一个数字的范围只能在 -(2^53 -1) 和 2^53 -1 之间,JavaScript 的按位操作符又是将其操作数当作 32 位比特序列的 。那么同一个应用下可用的权限数就非常有限了 。这也是该方案的局限性 。
为了突破这个限制,这里提出一个叫“权限空间”的概念,既然权限数有限,那么不妨就多开辟几个空间来存放 。
基于权限空间,我们定义两个格式:
- 权限 code,字符串,形如 index,pos 。其中 pos 表示 32 位二进制数中 1 的位置(其余全是 0); index 表示权限空间,用于突破 JavaScript 数字位数的限制,是从 0 开始的正整数,每个权限code都要归属于一个权限空间 。index 和 pos 使用英文逗号隔开 。
- 用户权限,字符串,形如 1,16,16 。英文逗号分隔每一个权限空间的权限值 。例如 1,16,16 的意思就是,权限空间 0 的权限值是 1,权限空间 1 的权限值是 16,权限空间 2 的权限是 16 。
// 用户的权限 codelet userCode = ""// 假设系统里有这些权限// 纯模拟,正常情况下是按顺序的,如 0,0 0,1 0,2 ...,尽可能占满一个权限空间,再使用下一个const permissions = { SYS_SETTING: { value: "0,0", // index = 0, pos = 0 info: "系统权限" }, DATA_ADMIN: { value: "0,8", info: "数据库权限" }, USER_ADD: { value: "0,22", info: "用户新增权限" }, USER_EDIT: { value: "0,30", info: "用户编辑权限" }, USER_VIEW: { value: "1,2", // index = 1, pos = 2 info: "用户查看权限" }, USER_DELETE: { value: "1,17", info: "用户删除权限" }, POST_ADD: { value: "1,28", info: "文章新增权限" }, POST_EDIT: { value: "2,4", info: "文章编辑权限" }, POST_VIEW: { value: "2,19", info: "文章查看权限" }, POST_DELETE: { value: "2,26", info: "文章删除权限" }}// 添加权限const addPermission = (userCode, permission) => { const userPermission = userCode ? userCode.split(",") : [] const [index, pos] = permission.value.split(",") userPermission[index] = (userPermission[index] || 0) | Math.pow(2, pos) return userPermission.join(",")}// 删除权限const delPermission = (userCode, permission) => { const userPermission = userCode ? userCode.split(",") : [] const [index, pos] = permission.value.split(",") userPermission[index] = (userPermission[index] || 0) & (~Math.pow(2, pos)) return userPermission.join(",")}// 判断是否有权限const hasPermission = (userCode, permission) => { const userPermission = userCode ? userCode.split(",") : [] const [index, pos] = permission.value.split(",") const permissionValue = https://www.isolves.com/it/cxkf/yy/js/2019-11-07/Math.pow(2, pos) return (userPermission[index] & permissionValue) === permissionValue}// 列出用户拥有的全部权限const listPermission = userCode => { const results = [] if (!userCode) { return results } Object.values(permissions).forEach(permission => { if (hasPermission(userCode, permission)) { results.push(permission.info) } }) return results}const log = () => { console.log(`userCode: ${JSON.stringify(userCode, null, " ")}`) console.log(`权限列表: ${listPermission(userCode).join("; ")}`) console.log("")}userCode = addPermission(userCode, permissions.SYS_SETTING)log()// userCode: "1"// 权限列表: 系统权限userCode = addPermission(userCode, permissions.POST_EDIT)log()// userCode: "1,,16"// 权限列表: 系统权限; 文章编辑权限userCode = addPermission(userCode, permissions.USER_EDIT)log()// userCode: "1073741825,,16"// 权限列表: 系统权限; 用户编辑权限; 文章编辑权限userCode = addPermission(userCode, permissions.USER_DELETE)log()// userCode: "1073741825,131072,16"// 权限列表: 系统权限; 用户编辑权限; 用户删除权限; 文章编辑权限userCode = delPermission(userCode, permissions.USER_EDIT)log()// userCode: "1,131072,16"// 权限列表: 系统权限; 用户删除权限; 文章编辑权限userCode = delPermission(userCode, permissions.USER_EDIT)log()// userCode: "1,131072,16"// 权限列表: 系统权限; 用户删除权限; 文章编辑权限userCode = delPermission(userCode, permissions.USER_DELETE)userCode = delPermission(userCode, permissions.SYS_SETTING)userCode = delPermission(userCode, permissions.POST_EDIT)log()// userCode: "0,0,0"// 权限列表: userCode = addPermission(userCode, permissions.SYS_SETTING)log()// userCode: "1,0,0"// 权限列表: 系统权限
推荐阅读
- 国都证券|面试中的奇葩事
- 2022第三代社保卡有几个密码,第三代社保卡密码是几位数
- 茶水在食疗养生中的妙用
- 三星堆遗址以高度发达的青铜文明著名于世 我国西南地区的青铜时代遗址三星堆位于哪个省
- 淘宝店铺定位的要素 淘宝店铺的市场定位
- 单位体检一半人都有肺结节吗,单位体检过程中发现肺部结节会现场告诉吗
- 水浒传中的王婆茶坊
- 什么星座中的七颗亮星组成一个勺子的形状 北斗七星是勺子形状的吗
- 如何挑选优质益母草茶
- 雪天开车手动挡下坡用几档 下雪天开车用什么档位
