[TOC] >[success] # 权限验证 ~~~ 1.利用'JS--按位运算'的形式做权限控制,说明其他语言原理是一样的,这里是针对js说明实践 ~~~ >[danger] ##### 举例子 ~~~ 1.现在有个需求不同用户有不同的权限来设置增删改查,即A用户可能具备'增删',B用户可以能具备'增查', 将其转换二级制的思想,'1111' 表示拥有所有权限,增'1000',删'0100',改'0010',查'0001',那么增查可以 看作'1001',简单的理解为'1' 有权限,'0' 为没权限,则在不同位置表示他们所表示的对应权限 ~~~ >[info] ## 了解按位运算 ~~~ 1. & 按位与 2. | 按位或 3. ^ 按位异或 4. ~ 按位非 5. <<左移 6. >>有符号右移 7. >>>无符号右移 ~~~ >[danger] ##### << -- 左移 ~~~ 1.左移操作符 (<<) 将第一个操作数向左移动指定位数,左边超出的位数将会被清除,右边将会补零 2.左移的计算公式为 'x<<y' ===> 'x * 2 ** y',举个例子 9 << 3 等价于 9 * 2 ** 3 = 9 * 8 = 72 ~~~ * 案例应用 ~~~ 1.所有权限码的二进制数形式,有且只有一位值为 1,其余全部为 0(2^n)简单的说 (位数上只有一位是1其余位为0) 2.符合上述条件的值都是 '2^n' ,采用左移表示 '1 << 0' ==> 1*2**0 ==> 转换二进制为 0001 '1 << 1' ==> 1*2**1 ==> 转换二进制为 0010 '1 << 2' ==> 1*2**2 ==> 转换二进制为 0100 ~~~ >[danger] ##### | -- 按位或 ~~~ 1.参加运算的两个数只要两个数中的一个为1,结果就为1 2.举个例子二进制'0001'、'0010'通过'按位或'运算 0001 0010 结果为 0011 利用 '2^n' 特点即每一位只存在一个1,再利用'按位或'两个数中的一个为1,结果就为1的特点就可以 做到组合权限的一个数值代表 ~~~ >[danger] ##### & -- 按位与 ~~~ 1.只有两个数的二进制同时为1,结果才为1,否则为0 2.举个例子二进制'0001'、'0010'通过'按位或'运算 0001 0011 结果为 0001 利用 '2^n' 特点即每一位只存在一个1,我们如果组合权限利用'按位计算'例如'0001'、'0010'得到'0011' 再利用'按位与'的特性就可以查出该权限是否在总权限中,也就是现在用户权限为'0011' 我想查询'0001' 因为对位为1的缘故所以0001 是在权限0011 中的 ~~~ >[danger] ##### ~ -- 按位非 ~~~ 1.~ 运算符是对位求反,1变0, 0变1,也就是求二进制的反码。 2.举个例子1的二进制表示为: 00000000 00000000 00000000 00000001,这里涉及到js 32位补零知识点,可以 参考'https://www.kancloud.cn/cyyspring/more/1395853',对其位数取反得到11111111 11111111 11111111 11111101 如果你看了参考文章你就知道首位 1 表示为负数,0表示为正数,所以'~1' 按位非得到的是-2 3.简单记忆:一个数与自身的取反值相加等于-1 4.利用'按位非' 和 '按位与' 可以实现'删除一项或多项权限' ,例如你现在拥有的权限为'0011' 你想删除权限'0001' 此时利用'按位非' 对'0001'进行计算得到'1110',再利用'按位与' 将'1110' 和'0011' 计算得到'0010' 5.带有具体内容设计时候可以 static SELECT = { value: 1 << 0, info: "用户编辑权限" }, 这样在查询全部时候可以依次循环权限配置得到结果 ~~~ >[danger] ##### 设计一个权限管理 ~~~ class Permission { // 是否允许查询,二进制第1位,0表示否,1表示是 static SELECT = 1 << 0; // 0001 // 是否允许新增,二进制第2位,0表示否,1表示是 static INSERT = 1 << 1; // 0010 // 是否允许修改,二进制第3位,0表示否,1表示是 static UPDATE = 1 << 2; // 0100 // 是否允许删除,二进制第4位,0表示否,1表示是 static REMOVE = 1 << 3; // 1000 // 存储目前的权限状态 static TEST = 1<<4; // 10000 constructor() { this.flag = 0; } /** * 重新设置权限 */ setPermission(permission) { this.flag = permission; } /** * 添加一项或多项权限 */ enable(permission) { this.flag |= permission; } /** * 删除一项或多项权限 */ disable(permission) { this.flag &= ~permission; } /** * 是否拥某些权限 */ isAllow(permission) { return (this.flag & permission) == permission; } /** * 是否禁用了某些权限 */ isNotAllow(permission) { return (this.flag & permission) == 0; } /** * 是否仅仅拥有某些权限 */ isOnlyAllow(permission) { return this.flag == permission; } } ~~~ * vue 为例一个小案例 ~~~ <template> <div> <p>权限:</p> <section> <label><input v-model="query" type="checkbox" value="" />查询 </label> <label><input v-model="insert" type="checkbox" value="" />增加 </label> <label><input v-model="remove" type="checkbox" value="" />删除 </label> <label><input v-model="update" type="checkbox" value="" />更新 </label> <label><input v-model="test" type="checkbox" value="" />测试 </label> </section> <section>权限值:{{ permission.flag }}</section> </div> </template> <script> // @ is an alias to /src class Permission { // 是否允许查询,二进制第1位,0表示否,1表示是 static SELECT = 1 << 0; // 0001 // 是否允许新增,二进制第2位,0表示否,1表示是 static INSERT = 1 << 1; // 0010 // 是否允许修改,二进制第3位,0表示否,1表示是 static UPDATE = 1 << 2; // 0100 // 是否允许删除,二进制第4位,0表示否,1表示是 static REMOVE = 1 << 3; // 1000 // 存储目前的权限状态 static TEST = 1 << 4; constructor() { this.flag = 0; } /** * 重新设置权限 */ setPermission(permission) { this.flag = permission; } /** * 添加一项或多项权限 */ enable(permission) { this.flag |= permission; } /** * 删除一项或多项权限 */ disable(permission) { this.flag &= ~permission; } /** * 是否拥某些权限 */ isAllow(permission) { console.log(this.flag); console.log(permission); return (this.flag & permission) == permission; } /** * 是否禁用了某些权限 */ isNotAllow(permission) { return (this.flag & permission) == 0; } /** * 是否仅仅拥有某些权限 */ isOnlyAllow(permission) { console.log(permission, this.flag); return this.flag == permission; } } export default { name: "Lab", data() { return { insert: false, update: false, remove: false, query: false, test: false, permission: new Permission(), }; }, created() { this.$watch("insert", (val) => { if (val) { this.permission.enable(Permission.INSERT); } else { this.permission.disable(Permission.INSERT); } console.log("INSERT>>>", this.permission.isAllow(Permission.INSERT)); }); this.$watch("update", (val) => { if (val) { this.permission.enable(Permission.UPDATE); } else { this.permission.disable(Permission.UPDATE); } console.log("UPDATE>>>", this.permission.isAllow(Permission.UPDATE)); }); this.$watch("remove", (val) => { if (val) { this.permission.enable(Permission.REMOVE); } else { this.permission.disable(Permission.REMOVE); } console.log("REMOVE>>>", this.permission.isAllow(Permission.REMOVE)); }); this.$watch("query", (val) => { if (val) { this.permission.enable(Permission.SELECT); } else { this.permission.disable(Permission.SELECT); } console.log("SELECT>>>", this.permission.isAllow(Permission.SELECT)); }); this.$watch("test", (val) => { if (val) { this.permission.enable(Permission.TEST); } else { this.permission.disable(Permission.TEST); } console.log("SELECT>>>", this.permission.isAllow(Permission.SELECT)); }); }, }; </script> ~~~ >[danger] ##### 判断数的奇偶 ~~~ 1.利用二进制偶数末尾为0 基数为1的特点和 1 进行'按位与判断' ~~~ ~~~ // 偶数 & 1 = 0 // 奇数 & 1 = 1 console.log(2 & 1) // 0 console.log(3 & 1) // 1 ~~~ >[info] ## 参考文章 [读读这个挺好 JavaScript 中的位运算和权限设计](https://juejin.cn/post/6844903988945485837#heading-9) [位运算符在JS中的妙用](https://juejin.cn/post/6844903568906911752#heading-4) [奇怪的知识——位掩码](https://juejin.cn/post/6931250132861648910#heading-12) [「硬核JS」令你迷惑的位运算](https://juejin.cn/post/6900710763657166855#heading-31) [巧用JS位运算](https://juejin.cn/post/6844903570584633351#heading-5)