一种适用于大量租户大量角色权限系统设计

news2024/11/15 1:35:14

前言

权限管理是每个系统不可缺少的一部分,大部分开发者应该都设计过权限管理系统,很多开发者学习的第一个项目可能就是权限管理系统。但是常见的权限设计在租户量非常大、角色数量非常多时会存在角色权限表数据量指数增长的情况,本文介绍一种可以避免这种情况的权限设计思路。

传统权限设计方案

传统的权限系统设计一般有四张表分别为 菜单表、角色表、角色菜单表、用户角色表,我们先按传统权限系统设计一套数据表结构:

菜单表 SYS_MENU

IDNAME
1账务管理
2用户管理
3订单管理

角色 SYS_ROLE

IDNAME
1管理员
2普通用户

角色菜单 SYS_ROLE_MENU

IDROLE_IDMENU_ID
111
212
122

用户角色 SYS_USER_ ROLE

IDROLE_IDUSER_ID
111
221
122

数据指数增长问题

如果我们系统有1万个租户,每个租户有100个角色,每个角色有100个菜单点,则SYS_ROLE_MENU数据量为1亿条数据,这个数据是非常恐怖的。

新的权限设计

角色表 SYS_ROLE

IDROLE_NAMEMENU_CODE
1管理员1fd4

对角色的菜单点进行编码,我们先构建一个二进制,默认为全0,将对角色拥有的菜单MENU_ID位置为1,如
管理员角色三个菜单权限们,它的的MENU_ID为 [16,10,3],则我们将第16位、第10位、第3位置成1,则二进制编码为(从第0位开始)10000010000001000,我们将此二进制转成36进进制为1fd4,二进制如下图所示

在这里插入图片描述

按上面的表设计后,我再看:
如果我们系统有1万个租户,每个租户有100个角色,每个角色有100个菜单点,则SYS_ROLE数据量为100万,比传统的少了100倍

与前端数据交换

用户登录后,前端会调用后端接口获取用户所能访问的菜单权限,比如用户有[16,10,3]菜单权限位,我数据库里存的是36位的编码10g4,传给前端肯定要转成[16,10,3],这里我们利用BigInteger 很易容就可以转成36进制,因为BigInteger 最高进制只能支持36进制,可以自己写个简单的进制转换,转成64进制,这样随着MENU_ID增大,MENU_CODE长度会小很多。

   public class MenuCodeConvert{

    /**
     * code 为36进制 String
     * 1fd4 返回 [16,10,3]
     *
     * @param code
     * @return
     */
    public static List<Long> codeToIds(String code) {
        List<Long> ids = new ArrayList<>();
        BigInteger bigInteger = new BigInteger(code, 36);
        for (int i = 0; i < bigInteger.bitLength(); i++) {
            if (bigInteger.testBit(i)) {
                ids.add((long) i);
            }
        }
        return ids;
    }

    /**
     * [16,10,3]  编码 1fd4
     *
     * @param ids
     * @return
     */
    public static String idsToCode(List<Long> ids) {
        if (ids == null && ids.size() == 0) {
            return null;
        }
        BigInteger bigInteger = BigInteger.ZERO;
        for (Long id : ids) {
            bigInteger = bigInteger.setBit(id.intValue());
        }
        return bigInteger.toString(36);
    }

    public static void main(String[] args) {
        List<Long> ids = Arrays.asList(16L,10L,3L );
        System.out.println(idsToCode(ids));
        System.out.println(codeToIds("1fd4"));
    }
    //输出 
    1fd4
    [3, 10, 16]

}


为了 前端<–>后端<–>数据库双向传输过程menuCode编码转换变的更自动简单,我们可以简单封装一下,自定义TypeHandler可以解决此问题,可以参数我之前的文章1
首先创建一个MenuCode 对象

public class MenuCode {
    public MenuCode(List<Long> appMenuIds) {
        this.appMenuIds = appMenuIds;
        this.menuCode = MenuCodeConvert.idsToCode(appMenuIds);
    }
    public MenuCode(String menuCode) {
        this.appMenuIds = MenuCodeConvert.codeToIds(menuCode);
        this.menuCode = menuCode;
    }
    private String menuCode;
    private List<Long> appMenuIds;
    
    public String getMenuCode(){
        return menuCode;
    }

    public void setMenuCode(String menuCode) {
        this.menuCode = menuCode;
        this.appMenuIds = MenuCodeConvert.codeToIds(menuCode);
    }
    public void setAppMenuIds(List<Long> appMenuIds) {
        this.appMenuIds = appMenuIds;
        this.menuCode = MenuCodeConvert.idsToCode(appMenuIds);
    }
}

创建角色表

@Data
@TableName("sys_role")
public class SysRole  {
    @TableId
    private Long id;
    private String roleName;
    private MenuCode menuCode;
}

给MenuCode创建TypeHandler

@MappedTypes({MenuCode.class})
public class MenuCodeHandler extends BaseTypeHandler<MenuCode> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, MenuCode parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, parameter.getMenuCode());
    }

    @Override
    public MenuCode getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String content = rs.getString(columnName);
        return rs.wasNull() ? null : new MenuCode(content);
    }

    @Override
    public MenuCode getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String content = rs.getString(columnIndex);
        return rs.wasNull() ? null : new MenuCode(content);
    }

    @Override
    public MenuCode getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String content = cs.getString(columnIndex);
        return cs.wasNull() ? null : new MenuCode(content);
    }
}

这样我们在调用 SysRole sysRole=sysRoleService.getById(1L);从数据库查出的36进制编码1fd4会自动转成[16,10,3], 返回给前端的数据就是JSON格式为:

{
    "id": 1,
    "name": "管理员",
    "menuCode": [
        16,
        10,
        3
    ]
}

在保存角色权限时前端传的JSON,我们调用sysRoleService.save(sysRole)也会自动将[16,10,3]转成1fd4保存到数据库,这样完成自动转换,根本不用关心中间的菜单权限编码了。

总结

本文介绍一种适用于大量租户大量角色的权限系统设计,解决了系统由于租户数量及角色数据不断增长导致角色权限表成指数增长的问题,并巧妙利用BigInteger 完成二进制和36进制中间的转换,最后利用Mybatis中的自定义TypeHandler解决前端到后端再到数据库菜单编码自动转换的问题。

缺点及未来展望

如果系统中菜单有1000个ID从1-1000,某一个角色只有菜单ID为1000的权限点,那么他的menuCode为4lxcmkxpcdbbom7n3gica9gqteokl39474etuib075x4lhig8dvocg32jwycjwfjzmzfh2ukqnemkxt6xlyq5ze8x7okzf66sgxrzep0m50yirndmhnu9t1ywaycup2k0j6be15l7amfyk29u14alvodnqk6644vt0oldwmm6p082rjyxatszf91qbmhbi1i4g,menuCode会随着最大菜单ID增大而变得非常长,不过可以通过分组来解决,每个分组的菜单ID都是从1开始自增,并将分组ID写到编码前几位。


  1. Mybatis自定义TypeHandler实现数据库Json数组转List对象 ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/564718.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

c++代码手撕红黑树

企业里永远是技术驱动理论发展 比起理解红黑树的原理&#xff0c;更重要的是理解红黑树的应用场景&#xff0c;因为某些应用场景的需要&#xff0c;红黑树才会应运而生。 红黑树的特点&#xff1a; 插入&#xff0c;删除&#xff0c;查找都是O(logn)的复杂度。 红黑树的应用…

【lager】日志系统2:测试程序调试

design 官方设计文档 design cmakelist增加plog构建 cmake_minimum_required(VERSION 3.1.3 FATAL_ERROR) project(Lager)set(CMAKE_CXX_STANDARD 11) set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)find_package(

课时八——进程同步(二)

1、信号量 信号量机制是一种功能较强的机制&#xff0c;可用来解决互斥和同步问题&#xff0c;它只能被两个标准的原语wait(S)&#xff08;P操作&#xff09;和signal(S)&#xff08;V操作 &#xff09;访问。 注意&#xff1a;原语是一种特殊的程序段&#xff0c;其执行只能一…

EV 电动汽车远程监控系统

EV 电动汽车远程监控系统 远程监控系统简介 目录 EV 电动汽车远程监控系统 1、远程监控系统是什么&#xff1f; 2、远程监控系统包含什么&#xff1f; 2.1车载终端 2.2、数据服务器 ​​​​​​​ 2.3、监控页面 3、远程监控系统有什么用&#xff1f; 4、车载终端…

中性市场观下,贝壳驶入长期价值之海

&#xff08;图片来源于网络&#xff0c;侵删&#xff09; 文 | 螳螂观察 作者 | 易不二 2023年一季度&#xff0c;房地产市场实现了久违的回暖。 国家统计局公布的数据显示&#xff0c;2023年1-4月&#xff0c;全国商品房销售额39750亿元&#xff0c;增长8.8%&#xff0c;…

Atlassian数据迁移攻略:迁移前必备须知

到2024年2月&#xff0c;Atlassian将终止对Server产品及插件的所有支持。是时候制定您的迁移计划了——Atlassian为您提供两种迁移选择&#xff0c;一是本地部署的数据中心版本&#xff0c;中国用户25人以上即可使用&#xff0c;二是云版。作为Atlassian全球白金合作伙伴&#…

es Elasticsearch 六 java api spirngboot 集成es

目录 Java restApi Springboot 集成es 新增-同步 新增-异步 增删改查流程 _bulk 批量操作 Java restApi Springboot 集成es 新增-同步 Testpublic void te2() throws IOException {System.out.println(1);IndexRequest ir new IndexRequest("test");ir.id(&qu…

STM8 模拟iic接口调试温湿度传感器SHT3x驱动

背景 项目实际使用SHT3x进行温湿度测量&#xff0c;主控芯片采用STM8S003F3P6&#xff0c;并且使用模拟IIC接口的硬件连接。 原理图 如下图所示&#xff0c;使用STM8S003F3P6管脚PB4/PB5进行SHT3x数据接口 SHT3x-DIS是Sensirion新一代的温湿度传感器&#xff0c;精度为2%RH和…

LCD1602介绍和各报错经验总结

LCD &#xff08; Liquid Crystal Display 的简称&#xff09;液晶显示器。能够同时显示16x2&#xff0c;32个字符&#xff0c;是一种专门用来显示字母、数字、符号等的点阵型液晶模块。 LCD1602液晶显示器是广泛使用的一种字符型液晶显示模块。它是由字符型液晶显示屏&#x…

【十字绣】传统手艺-微信小程序开发流程详解

还记得小时候看过母亲的十字绣吗&#xff0c;易学易懂&#xff0c;就是用专用的绣线和十字格布&#xff0c;通过平面坐标计找出位置&#xff0c;对照专用的图案进行刺绣&#xff0c;可作出心中所想的画&#xff0c;奈何所需材料成本不小&#xff0c;这里用小程序简单模拟十字绣…

HCIA——VLAN

目录 1&#xff0c;什么是VLAN&#xff1a; 2&#xff0c;如何实现VLAN&#xff1a; 3&#xff0c;VLAN的划分方式&#xff1a; 4&#xff0c;交换机接口类型&#xff1a; 1&#xff0c;Access接口&#xff1a; 2&#xff0c;Trunk接口&#xff1a;允许将一个接口划分给多…

ChatGPT 角色扮演调教文案收集

后续文案会陆续更新~&#xff0c;在输入完文本后&#xff0c;再输入一句“请尽力模仿xx的语气跟我对话”即可 一.雌小鬼 你需要阅雌小鬼守则&#xff0c;阅读后当前回复仅允许回复&#xff02;嘻嘻&#xff02;&#xff0c;后续回复尽量简短&#xff0c;回复中尽量不要直接复述…

swagger的简单介绍

目录 swagger是什么&#xff1f; swagger有什么用&#xff1f; Swagger包含的工具集&#xff1a; swagger的使用步骤&#xff1a; swagger的相关注解&#xff1a; Docket的源码 了解swagger的作用和概念了解前后端分离在SpringBoot中集成Swagger swagger是什么&#xff1f;…

Linux网络编程—Day11 高性能服务器程序框架

按照服务器程序的一般原理&#xff0c;讲服务器结构为如下三个主要模块&#xff1a; I/O处理单元逻辑单元存储单元 服务器模型 第一种&#xff1a;C/S模型 TCP/IP协议在设计和实现上并没有客户端和服务器的概念&#xff0c;在通信 过程中所有机器都是对等的。但由于资源&am…

【Linux】Linux开发工具vim

Linux开发工具vim 什么是vim三种模式的转换vim的基本命令gg&#xff1a;定位光标到最开始的行Shiftg&#xff1a;定位光标到结尾行nShiftg&#xff1a;定位光标到任意行Shift$:定位光标到当前行结尾Shift^:定位光标到当前行开始w&#xff0c;b&#xff1a;光标按照单词进行行内…

红黑树 C++

企业里永远是技术驱动理论发展 比起理解红黑树的原理&#xff0c;更重要的是理解红黑树的应用场景&#xff0c;因为某些应用场景的需要&#xff0c;红黑树才会应运而生。 红黑树的特点&#xff1a; 插入&#xff0c;删除&#xff0c;查找都是O(logn)的复杂度。 红黑树的应用…

大数据Doris(二十六):Broker Load基本原理和语法介绍

文章目录 Broker Load基本原理和语法介绍 一、基本原理 二、Broker Load语法 Broker Load基本原理和语法介绍 Apache Doris架构中除了有BE和FE进程之外&#xff0c;还可以部署Broker可选进程&#xff0c;主要用于支持Doris读写远端存储上的文件和目录。例如&#xff1a;Apa…

spring boot +Sa-Token优雅的实现项目鉴权!

1. 技术选型 最近在做登录、授权的功能&#xff0c;一开始考虑到的是spring boot spring security&#xff0c;但spring security太重&#xff0c;而我们是轻量级的项目&#xff0c;所以&#xff0c;spring security不适合我们。 而后考虑spring boot shiro&#xff0c;但s…

【老王读SpringMVC-5】Controller method 是如何执行的?

通过前面对 Controller method 参数绑定的分析&#xff0c;我们知道&#xff0c; 被 RequestMapping 标记 handler method 的执行是通过调用 RequestMappingHandlerAdapter#handle()。 RequestMappingHandlerAdapter#handle() 具体的调用过程如下&#xff1a; 参数解析、han…

【Java基础篇】运算符

作者简介&#xff1a; 辭七七&#xff0c;目前大一&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a;Java.SE&#xff0c;本专栏主要讲解运算符&#xff0c;程序逻辑控制&#xff0c;方法的使用&…