基于BigInteger的菜单访问权限构建

权限的概念

权限管理是一个几乎所有后台系统的都会涉及的一个重要组成部分,主要目的是对整个后台管理系统进行权限的控制。常见的基于角色的访问控制,其授权模型为“用户-角色-权限”,简明的说,一个用户拥有多个角色,一个角色拥有多个权限。

什么是用户

进入系统肯定都会有一个账号,而这个账号就是一把钥匙。我们通过控制账号所具备的权限。

什么是角色

角色是一个集合的概念,是众多最小权限颗粒的组成。我们通过把权限给这个角色,再把角色给账号,从而实现账号的权限,因此它承担了一个桥梁的作用。引入角色这个概念,可以帮助我们灵活的扩展,使一个账号可以具备多种角色。

什么是权限

权限大致可以分为三种:页面权限,操作权限,数据权限。

  • 页面权限控制你可以看到哪个页面,看不到哪个页面,很多系统都只做到了控制页面这一层级。

  • 操作权限则控制你可以在页面上操作哪些按妞。比如:查询,删除,编辑,新增四个按钮哪些你可以点击哪些不可以点击

  • 数据权限则是控制你可以看到哪些数据,其实也是操作权限中的一种。

三者关系如图:
权限配图1

常见的权限框架

  1. Shiro实现权限认证

这里简单介绍一下,因为不是本文重点。shiro主要功能有
三个核心组件:Subject, SecurityManager 和 Realms.

  • Subject:即“当前操作用户”。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
  • SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
  • Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
  1. Spring Security

这是 Spring Security 提供的一个安全权限控制框架,可以根据使用者的需要定制相关的角色身份和身份所具有的权限,完成黑名单操作、拦截无权限的操作。配合 Spring Boot 可以快速开发出一套完善的权限系统。
1593264622123-9e26b58e-4d49-4815-aaaa-b12dc597d3a6
核心概念:
SecurityContext:上下文对象,Authentication对象会放在里面。SecurityContextHolder:用于拿到上下文对象的静态工具类。Authentication:认证接口,定义了认证对象的数据形式。AuthenticationManager:用于校验Authentication,返回一个认证完成后的Authentication对象。
一般我们使用这个方案是在比如需要第三方平台登录的逻辑中,如图逻辑:
未命名文件

基于BigInteger权限控制

上边介绍的权限框架有的时候使用起来感觉比较重,有的时候可能需要进行比较简单的权限控制,比如控制某一个用户是否有新增和删除权限或者某个菜单的权限,这个时候就比较适用采用BigInteger来判断权限了。接下来我们用一个实际的例子来看一下怎么使用:

  1. 参考表结构
CREATE TABLE `sys_user` (
  `USER_ID` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `USERNAME` varchar(255) DEFAULT NULL,
  `PASSWORD` varchar(255) DEFAULT NULL,
  `NAME` varchar(255) DEFAULT NULL,
  `RIGHTS` varchar(255) DEFAULT NULL,
  `ROLE_ID` text,
  PRIMARY KEY (`USER_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
复制代码
CREATE TABLE `sys_role` (
  `ROLE_ID` varchar(100) NOT NULL,
  `ROLE_NAME` varchar(100) DEFAULT NULL,
  `RIGHTS` text, //【通过BigInteger计算】
  `PARENT_ID` varchar(100) DEFAULT NULL, // 角色组操作,0表示角色组
  `TYPE` varchar(11) DEFAULT '1', // 角色的类型
   PRIMARY KEY (`ROLE_ID`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
复制代码
CREATE TABLE `sys_menu` (
  `MENU_ID` int(11) NOT NULL,
  `MENU_NAME` varchar(255) DEFAULT NULL,
  `MENU_URL` varchar(255) DEFAULT NULL,
  `PARENT_ID` varchar(100) DEFAULT NULL, // 0 为一级菜单
  `MENU_ORDER` varchar(100) DEFAULT NULL,
  `MENU_ICON` varchar(30) DEFAULT NULL,
  `MENU_TYPE` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`MENU_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
复制代码

首先,从系统界面中设置角色权限关系,将选好的菜单树权限勾选,完成后传到后台,后台通过转成字符串数组来进行设值;使用如下操作返回权限集合BigInteger值

public static BigInteger sumRights(String[] rights){
        BigInteger num = new BigInteger("0");
        for(int i=0; i<rights.length; i++){
            num = num.setBit(Integer.parseInt(rights[i]));
        }
        return num;
}
复制代码

通过上面方法,返回一个BigInteger,然后将这个数字保存在所属角色的菜单权限字段中(RIGHTS)
最后,检查角色权限时通过把之前存入RIGHTS字段值和菜单ID做对比来判断是否具有该菜单权限,

public static boolean testRights(BigInteger sum,int targetRights){
        return sum.testBit(targetRights);
}
复制代码

我们把这个判断流程抽出来简单说明一下:整个过程就是把具体的权限设置为一个正整数值,如果一个用户有多个权限的话,比如1,2权限,那么我们设置值的时候就是num.setBit(1),num.setBit(2),然后把返回的num值保存在session中,要验证是否有权限的话,只要从session中取得保存的num,然后执行下num.test(权限值),如果返回true就是有权限的,否则无权限。

import java.math.BigInteger;

public class TestBigInteger {

    public static void main(String[] args) {
        //初始
        BigInteger num = new BigInteger("0");
        num = num.setBit(2);
        num = num.setBit(1);
        System.out.println(num);
        System.out.println(num.testBit(2));
        System.out.println(num.testBit(1));
        System.out.println(num.testBit(3));
    }
}
复制代码

返回的结果是:
6
true
true
false

为什么是6呢? 6= 2^2 + 2^1 其实计算的值是2的权的和这个的好处是,session中存储权限的空间很小,一个整数就代表了所有的权限,验证的时候计算速度也很快。

简单说一下判断原理

  • testBit

意思就是将1左移n位,与this做&运算,其实就是判断当前数(要写成二进制)第n+1位上的数是不是为1,是的话返回true

  • setBit

意思就是将1左移n位,与this对象做|运算,这样的话,就会将this的二进制格式的第n+1位的数变为1.这样很明显就和上一个方法形成一对, n可以作为某个功能编号,而角色可以使用setBit的方法设置编号,然后使用testBit来测试是不是含有n编号的功能。 如果每次有添加多个新的功能,那么就用这些功能编号依次给原来的角色编号执行setBit得到新的角色编号。

工具类

package com.test;
import java.math.BigInteger;
/**
 * @author Administrator
 * 权限计算帮助类
 */
public class RightsHelper {
    /**
     * 利用BigInteger对权限进行2的权的和计算
     * @param rights int型权限编码数组
     * @return 2的权的和
     */
    public static BigInteger sumRights(int[] rights){
        BigInteger num = new BigInteger("0");
        for(int i=0; i<rights.length; i++){
            num = num.setBit(rights[i]);
        }
        return num;
    }
    /**
     * 利用BigInteger对权限进行2的权的和计算
     * @param rights String型权限编码数组
     * @return 2的权的和
     */
    public static BigInteger sumRights(String[] rights){
        BigInteger num = new BigInteger("0");
        for(int i=0; i<rights.length; i++){
            num = num.setBit(Integer.parseInt(rights[i]));
        }
        return num;
    }
    /**
     * 测试是否具有指定编码的权限
     * @param sum
     * @param targetRights
     * @return
     */
    public static boolean testRights(BigInteger sum,int targetRights){
        return sum.testBit(targetRights);
    }
    /**
     * 测试是否具有指定编码的权限
     * @param sum
     * @param targetRights
     * @return
     */
    public static boolean testRights(String sum,int targetRights){
        if("".equals(sum))
            return false;
        return testRights(new BigInteger(sum),targetRights);
    }
    /**
     * 测试是否具有指定编码的权限
     * @param sum
     * @param targetRights
     * @return
     */
    public static boolean testRights(String sum,String targetRights){
        if("".equals(sum))
            return false;
        return testRights(new BigInteger(sum),targetRights);
    }
    /**
     * 测试是否具有指定编码的权限
     * @param sum
     * @param targetRights
     * @return
     */
    public static boolean testRights(BigInteger sum,String targetRights){
        return testRights(sum,Integer.parseInt(targetRights));
    }
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享