通用数据权限的思考与设计

一、数据权限概述spring

1.一、什么是数据权限?数据库

若是想学习Java工程化、高性能及分布式、深刻浅出。微服务、Spring,MyBatis,Netty源码分析的朋友能够加个人Java高级交流:787707172,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给你们。spring-mvc

数据权限是指对系统用户进行数据资源可见性的控制,通俗的解释就是:符合某条件的用户只能看到该条件下对应的数据资源。那么最简单的数据权限大概就是:用户只能看到本身的数据。而在正式的系统环境中,会有不少更为复杂的数据权限需求场景,如:架构

  • 领导须要看到全部下属员工的客户数据,员工只能看本身的客户数据;
  • 经理A能看到全部企业客户,经理B只能看到年销售额小于1000万的企业客户;
  • 角色A能看到全国的产品数据,角色B只能看到上海的产品数据;

上述这些需求,使用硬编码也是能够实现的,可是在业务快速发展的过程当中,相似这种数据权限需求会愈来愈多,若是所有采用硬编码的方式,无疑会给咱们带来巨大的开发和维护压力。mvc

1.二、要素分析分布式

从当前登陆用户的角度来讲,数据权限的定义能够解释为:当前登陆的用户只能看到该用户权限范围内的数据资源。由此能够分析出数据权限控制中几个关键要素:ide

  1. 主体,即当前登陆用户。领导、角色等概念可翻译为当前登陆用户是不是领导,是否拥有某角色。
  2. 数据资源。即受管控系统数据。
  3. 条件规则。即当前登陆用户对于某特定的数据资源适用的条件。

二、数据权限设计spring-boot

理论上来讲,用户在访问受控的系统数据时,获取用户对该数据资源适用的条件规则,并将该条件规则解析为SQL查询语句便可实现对数据的权限控制。可是在实现过程当中,仍是会有不少难点,譬如当前登陆用户适用下列规则:微服务

客户数据:[客户经理] [包含于] [下属人员]产品数据:[销售地区] [等于] [上海]订单数据:([产品销售地区] [等于] [上海])[而且] ([客户市场经理] [包含于] [下属人员])

思考以下问题:源码分析

  1. [客户经理] [包含于] [下属人员]如何解析为SQL语句?多表联合查询时又该如何处理?
  2. [下属人员]由系统根据当前登陆用户计算而来,上海由管理员后台选择。两种方式如何兼容?
  3. 对于复杂多变的组合条件,应该如何设计?
  4. 如何肯定当前查询应该应用哪些条件规则?
  5. 一个用户拥有多个角色,不一样角色对于同一个规则设置不一样的值应该如何处理?

2.一、规则元

名词定义:规则元。在本文是指单个独立的数据规则定义,不一样用户对规则元可设置具体的规则过滤值,该值用做数据查询时的筛选条件。上述规则中[客户经理],[销售地区]都属于规则元。

2.二、规则元配置

1.规则元名称的配置。一个表中哪些字段能够进行规则设置,以及规则元名称如何与表字段关联。(如上述规则中[客户经理],[销售地区]),比较容易想到的方法是经过配置文件维护规则名称与数据库字段之间的关系。

2.规则元Value数据源的配置。如上述规则中的[下属人员],[上海],不难发现规则元Value来源有三种状况:

  • 后台管理人员输入。
  • 系统提供数据源,后台管理人员选择。如:所在地区[上海]
  • 系统提供数据。如:[下属人员]

配置文件能够实现数据规则的配置需求,可是当规则元愈来愈多时,维护配置文件就会变得麻烦起来,咱们是否能够效仿spring-boot取代spring-mvc的作法,使用注解来代替配置呢?每一条数据规则最终都会落到对数据库字段的控制,而如今绝大部分系统都会有一个Model层对应到数据库中的表,因而脑补出一个绝佳的规则元配置方式:

@TableName("test")

public class TestModal extends AbstractModel {

@DataRule(name = "规则元名称")

private String name;

}

@DataRule注解源码以下:

@Target({ElementType.FIELD})

@Retention(RetentionPolicy.RUNTIME)

public @interface DataRule {

/**

* 规则元名称

*/

String name() default "";

/**

* 规则元值来源类型

*/

RuleSourceStrategy strategy() default RuleSourceStrategy.TEXT;

/**

* 当数据来源是用户选择时{@code RuleSourceStrategy.CHOICE}数据地址

*/

String url() default "";

/**

* 当数据来源是系统提供时{@code RuleSourceStrategy.SYSTEM}提供器类名

*/

Class<? extends IDataRuleProvider> provider() default NullDataRuleProvider.class;

}

系统启动时,将规则元配置信息(名称、对应数据表、对应字段、值来源类型,值来源url,值来源提供者类名等)同步至数据库。数据表简单设计以下图:

通用数据权限的思考与设计

2.三、数据规则的配置

有了规则元信息,管理人员便可在系统中针对不一样用户(角色)设置规则元Value,该值做为数据查询时的筛选条件。规则元Value数据源包含三种状况,其中第①、②种状况下,须要管理员填写或选择该规则的值,存储于数据库;第③种状况下,Value值根据当前登陆用户计算得出,也便是@DataRule注解中provider计算得来的值。由数据库存储的规则与系统计算获得的规则合并后便是登陆用户的全部数据规则。

一个简单的配置界面以下:

通用数据权限的思考与设计

2.4 数据规则的解析

由上文可知,适用于当前登陆用户的数据规则主要来源有两种:

  1. 存储在数据库中的规则配置;如:所在地区[上海]
  2. 须要系统计算的规则配置;如:[下属人员]

两种状况下获取的数据规则合并以后便可获取适用于当前登陆用户的数据规则集合,流程图以下:

通用数据权限的思考与设计

两种状况下获取的数据规则如何兼容?规则合并后成为一个复杂的查询条件应该如何设计?

定义通用的规则结构以下:

{

rule:[{

field: "name",

operate: "equal",

value: "xxx"

}],

operate:"and",

group:[{

rule:[],

operate:"greater",

group:[]

}]

}

数据库存储规则结构的JSON串,合并时将JSON串反序列化以后使用and与系统计算得出规则对象链接便可,合并后的规则结构解析成简单SQL语句已经不是很难了。

可是对于多表联合查询时应该如何处理呢?

解析成SQL语句时可使用表名+字段名的方式,但是遇到查询中使用别名的时候,这种方式也不能正常工做,这里暂时的处理方式是支持解析时传递别名。

一个用户拥有多个角色,不一样角色对于同一个规则设置不一样的值应该如何处理?

譬如,用户A拥有角色role一、role2,其中:

role1适用规则:[销售地区] [等于] [上海]role2适用规则:[销售地区] [等于] [北京]

那么用户A合并后的数据规则应该是:

用户A适用规则:([销售地区] [等于] [上海]) or ([销售地区] [等于] [北京])

即:一个用户对于同一个规则元的多个规则设置,应使用or链接后再与其余规则元进行and链接。

2.五、肯定当前查询适用的数据规则

通过上述的规则配置与解析以后,咱们很容易拿到当前用户适用的数据规则集合。可是在一次查询时咱们应该使用集合中哪些规则进行过滤呢?一次查询是否开启数据规则过滤,使用哪些表的规则过滤应该是开发者来决定,相似:

xxxQuery(...).withDataRule("`table1`,`table2`");

即表示当前用户本次查询使用table一、table2中配置的数据规则。数据表中的每条规则应该支持在管理后台设置是否启用,这样理论上可实现每一个用户对每一条数据规则的配置。

欢迎工做一到八年的Java工程师朋友们加入Java高级交流:787707172

本群提供免费的学习指导 架构资料 以及免费的解答

不懂得问题均可以在本群提出来 以后还会有直播平台和讲师直接交流噢

相关文章
相关标签/搜索