数据权限方案设计
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
数据结构中保存如下几个字段:
• 角色列表:需要使用此规则的角色,可以多个,使用英文逗号隔开。 • 实体列表:对应的规则应用的实体(这里指的是表结构中的表名,可能你的实体 是驼峰而数据库是蛇形,所以这里要放蛇形那个),可以多个,使用英文逗号隔开。 • 表达式:表达式就是数据权限控制的核心了。简单的说这里的表达式就是一段 SQL 语句,其中设置了一些可替换值,底层会用对应运行时的变量替换对应内容,从而达 到增加条件的效果。 • 规则说明:单纯的一个说明字段。
核心流程 系统启动时,首先从数据库加载出所有的规则。底层利用插件机制来拦截所有的查询语句,进 入查询拦截方法后,首先根据当前用户的权限列表筛选出 PermissionRule 列表,然后循环列 表中的规则,对语句中符合实体列表的表进行条件增加,最终生成处理后的 SQL 语句,退出拦 截器,Mybatis 执行处理后 SQL 并返回结果。
Mybatis 的插件机制目前比较出名的实现应该就是 PageHelper 项目了,在做这个实现的 时候也参考了 PageHelper 项目的实现方式。所以权限控制插件的类命名为 PermissionHelper。 机制是依托于 Mybatis 的 plugins 机制,实际 SQL 处理的时候基于 jsqlparser 这个包。 设计中包含两个类,一个是保存角色与权限的实体类命名为 PermissionRule,一个是根据实 体变更底层 SQL 语句的主体方法类 PermissionHelper。
头部只是标准的 Mybatis 拦截器写法,注解中的 Signature 决定了你的代码对哪些方法拦截, update 实际上针对修改(Update)、删除(Delete)生效,query 是对查询(Select)生效。
重点思路 重点其实就在于 Sql 的解析和条件注入,使用开源项目 JSqlParser。
数据权限方案设计
权限控制主要分为两块,认证(Authentication)与授权(Authorization)。认证之后 确认了身份正确,业务系统就会进行授权,现在业界比较流行的模型就是 RBAC(RoleBased Access Control)。RBAC 包含为下面四个要素:用户、角色、权限、资源。用户是 源头,资源是目标,用户绑定至角色,资源与权限关联,最终将角色与权限关联,就形成了比 较完整灵活的权限控制模型。 资源是最终需要控制的标的物,但是我们在一个业务系统中要将哪些元素作为待控制的资源 呢?我将系统中待控制的资源分为三类:
2、数据权限实现
上一节就提及了实现原理,是基于 Mybatis 的 plugins(查看官方文档)实现。
MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括: Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) ParameterHandler (getParameterObject, setParameters) ResultSetHandler (handleResultSets, handleOutputParameters) StatementHandler (prepare, parameterize, batch, update, query)
4、将 controller 中的 request 解析出用户信息,如下所示 //当前登录用户信息 AcOauth2Util acOauth2Util=new AcOauth2Util(); AcOauth2Util.UserJwt userJwtFromHeader = acOauth2Util.getUserJwtFromHeader(request); 3、使用 JsonUtil 工具类,将当前用户转为 json String permissionRuleListForJson = JsonUtil.objToJson(userJwtFromHeader); 4、将 PermissionHelper 的 startData 方法,参数传 json, PermissionHelper.startData(permissionRuleListForJson);
2、定义数据权限插件
a、仿照 PageHelper 的工作原理,创建 PermissionHelper,定义权限开关方法 startData (),开始权限拦截
b、拦截器拦截 sql,判断是否开始拦截,开始拦截,按照 rule 规则,拦截并拼接 sql
3、定义 PermissionHelper 的使用开关
接下来说说优点:
1. 减少了接口数量及接口复杂度。原本针对不同的角色,可能会区分不同的接 口或者在接口实现时利用流程控制逻辑来区分不同的条件。有了数据权限控制,代码 中只用写基本逻辑,权限过滤由底层机制自动处理。
2. 提高了数据权限控制的灵活性。例如原本只有主管能查本部门下组织架构/ 订单数据,现在新增助理角色,能够查询本部门下组织架构,不能查询订单。这样的 话普通的写法就需要调整逻辑控制,使用数据权限控制的话,直接修改配置就好。
1、数据权限控制原理
数据权限控制最终的效果是会要求在同一个数据请求方法中,根据不同的权限返回不同的 数据集,而且无需并且不能由研发编码控制。这样大家的第一想法应该就是 AOP,拦截所有的 底层方法,加入过滤条件。这样的方式兼容性较强,但是复杂程度也会更高。我们这套系统 中,采用的是利用 Mybatis 的 plugin 机制,在底层 SQL 解析时替换增加过滤条件。 这样一套控制机制存在很明显的优缺点,首先缺点:
• 使用当前登录的用户信息(放在缓存中),替换条件表达式中的值。 • 某些情况需要忽略权限,可以考虑使用 ThreadLocal(单机)/Redis(集群) 来控制
3、使用
1、数据准别:
a、创建数据库,储存权限规则,表为 permission_rule b、用户登录时,到数据库查询用户拥有角色所拥有的 permission_rule,将其设置返回 前端储存 c、前端每次发送请求时,携带 pemission_rule 的信息
因 startData()是静态方法,只能操作静态成员变量,在并发时会有安全问题,步骤如下: a、使用 ThreadLocal 作为全局变量,存储当前线程的变量 b、引入 json,将 Permission_rule 格式化成字符串,再在 PermissionHelper 中解析成 LIst c、在需要数据权限的地方使用 PermissionHelper.startData(header),参数为前端传回的 登录用户信息 d、因 PermissionHelper 在 utils 工程,方便其他工程引用,所以在 header 不能直接传, 采用 json 字符串来传 e、PermissionHelper 实现了 Mybatis 的 Interceptor 接口,并且拦截 perpare 方法,才 能对 sql 进行操作并返回给 mybatis 去执行(拦截 query 方法无效) f、PermissionHelper 对是否需要数据权限进行判断,此变量放在了 TreadLocal 中,有 且仅有当前线程能访问 g、对 header 的 json 字符串进行解析,有权限规则 List h、对权限规则 list 进行遍历,按主表、从表的顺序进行匹配规则、替换变量 i、返回 sql,给 mybatis 执行
• 解析出 MainTable 和 JoinTable。from 之后跟着的称为 MainTable,join 之 后跟着的称为 JoinTable。这两个就是我们 PermissionRule 需要匹配的表名, PermissionRule::fromEntity 字段。
• 解析出 MainTable 的 where 和 JoinTable 的 on 后面的条件。使用 and 连接 原本的条件和待注入的条件,PermissionRule::exps 字段。
1. 适用性有限,基于底层的 Mybatis。 2. 方言有限,针对了某种数据库(我们使用 Mysql),而且由于需要在底层解 析处理条件所以有可能造成不同的数据库不能兼容。当然 Redis 和 NoSQL 也无法限 制。
当然,假如你现在就用 Mybatis,而且数据库使用的是 Mysql,这方面就没有太大影响 了。
2. To B 一般都不是开放的,只要做好认证关口,能够进入系统的只有内部员 工。大部分企业内部的员工互联网知识有限,而且作为内部员工不敢对系统进行破坏 性的尝试。
所以针对现在的情况,考虑成本与产出,大部分设计者也不愿意在权限上进行太多的研发 力Hale Waihona Puke Baidu。 菜单和界面元素一般都是由前端编码配合存储数据实现,URL 访问资源的控制也有一些框架比 如 SpringSecurity,Shiro。 目前还没有数据权限控制的框架或者方法,所以自己整理了一份。
1. URL 访问资源(接口以及网页) 2. 界面元素资源(增删改查导入导出的按钮,重要的业务数据展示与否等) 3. 数据资源
现在业内普遍的实现方案实际上很粗放,就是单纯的“菜单控制”,通过菜单显示与否来达 到控制权限的目的。平台分为 To C 和 To B 两种:
1. To C 一般不会有太多的复杂权限控制,甚至大部分连菜单控制都不用,全 部都可以访问。
{uid} 会自动替换为当前用户的 userId, {me} 主实体名称 {me.a} 主实体别名 格式如: userId = {uid} (userId = {uid} AND authType > 3) ((userId = {uid} AND authType) > 3 OR (dept in (select dept from depts where manager.id = {uid})))
h、执行完需关闭权限控制开关
4、用法
1、引 maven 坐标 <dependency>
<groupId>com.auth</groupId> <artifactId>ac-framework-utils</artifactId> <version>1.0-SNAPSHOT</version> </dependency> 2、编写权限规则 在 permission_rule 表中编写权限规则,各字段解释如下: Name: 规则名称 Ids :适用角色 id 列表,格式如:1,2,(用逗号隔开) Main_tables :适用的主表,格式如: Role,User, (用逗号隔开) Exps :过滤表达式字段,
• 角色列表:需要使用此规则的角色,可以多个,使用英文逗号隔开。 • 实体列表:对应的规则应用的实体(这里指的是表结构中的表名,可能你的实体 是驼峰而数据库是蛇形,所以这里要放蛇形那个),可以多个,使用英文逗号隔开。 • 表达式:表达式就是数据权限控制的核心了。简单的说这里的表达式就是一段 SQL 语句,其中设置了一些可替换值,底层会用对应运行时的变量替换对应内容,从而达 到增加条件的效果。 • 规则说明:单纯的一个说明字段。
核心流程 系统启动时,首先从数据库加载出所有的规则。底层利用插件机制来拦截所有的查询语句,进 入查询拦截方法后,首先根据当前用户的权限列表筛选出 PermissionRule 列表,然后循环列 表中的规则,对语句中符合实体列表的表进行条件增加,最终生成处理后的 SQL 语句,退出拦 截器,Mybatis 执行处理后 SQL 并返回结果。
Mybatis 的插件机制目前比较出名的实现应该就是 PageHelper 项目了,在做这个实现的 时候也参考了 PageHelper 项目的实现方式。所以权限控制插件的类命名为 PermissionHelper。 机制是依托于 Mybatis 的 plugins 机制,实际 SQL 处理的时候基于 jsqlparser 这个包。 设计中包含两个类,一个是保存角色与权限的实体类命名为 PermissionRule,一个是根据实 体变更底层 SQL 语句的主体方法类 PermissionHelper。
头部只是标准的 Mybatis 拦截器写法,注解中的 Signature 决定了你的代码对哪些方法拦截, update 实际上针对修改(Update)、删除(Delete)生效,query 是对查询(Select)生效。
重点思路 重点其实就在于 Sql 的解析和条件注入,使用开源项目 JSqlParser。
数据权限方案设计
权限控制主要分为两块,认证(Authentication)与授权(Authorization)。认证之后 确认了身份正确,业务系统就会进行授权,现在业界比较流行的模型就是 RBAC(RoleBased Access Control)。RBAC 包含为下面四个要素:用户、角色、权限、资源。用户是 源头,资源是目标,用户绑定至角色,资源与权限关联,最终将角色与权限关联,就形成了比 较完整灵活的权限控制模型。 资源是最终需要控制的标的物,但是我们在一个业务系统中要将哪些元素作为待控制的资源 呢?我将系统中待控制的资源分为三类:
2、数据权限实现
上一节就提及了实现原理,是基于 Mybatis 的 plugins(查看官方文档)实现。
MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括: Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) ParameterHandler (getParameterObject, setParameters) ResultSetHandler (handleResultSets, handleOutputParameters) StatementHandler (prepare, parameterize, batch, update, query)
4、将 controller 中的 request 解析出用户信息,如下所示 //当前登录用户信息 AcOauth2Util acOauth2Util=new AcOauth2Util(); AcOauth2Util.UserJwt userJwtFromHeader = acOauth2Util.getUserJwtFromHeader(request); 3、使用 JsonUtil 工具类,将当前用户转为 json String permissionRuleListForJson = JsonUtil.objToJson(userJwtFromHeader); 4、将 PermissionHelper 的 startData 方法,参数传 json, PermissionHelper.startData(permissionRuleListForJson);
2、定义数据权限插件
a、仿照 PageHelper 的工作原理,创建 PermissionHelper,定义权限开关方法 startData (),开始权限拦截
b、拦截器拦截 sql,判断是否开始拦截,开始拦截,按照 rule 规则,拦截并拼接 sql
3、定义 PermissionHelper 的使用开关
接下来说说优点:
1. 减少了接口数量及接口复杂度。原本针对不同的角色,可能会区分不同的接 口或者在接口实现时利用流程控制逻辑来区分不同的条件。有了数据权限控制,代码 中只用写基本逻辑,权限过滤由底层机制自动处理。
2. 提高了数据权限控制的灵活性。例如原本只有主管能查本部门下组织架构/ 订单数据,现在新增助理角色,能够查询本部门下组织架构,不能查询订单。这样的 话普通的写法就需要调整逻辑控制,使用数据权限控制的话,直接修改配置就好。
1、数据权限控制原理
数据权限控制最终的效果是会要求在同一个数据请求方法中,根据不同的权限返回不同的 数据集,而且无需并且不能由研发编码控制。这样大家的第一想法应该就是 AOP,拦截所有的 底层方法,加入过滤条件。这样的方式兼容性较强,但是复杂程度也会更高。我们这套系统 中,采用的是利用 Mybatis 的 plugin 机制,在底层 SQL 解析时替换增加过滤条件。 这样一套控制机制存在很明显的优缺点,首先缺点:
• 使用当前登录的用户信息(放在缓存中),替换条件表达式中的值。 • 某些情况需要忽略权限,可以考虑使用 ThreadLocal(单机)/Redis(集群) 来控制
3、使用
1、数据准别:
a、创建数据库,储存权限规则,表为 permission_rule b、用户登录时,到数据库查询用户拥有角色所拥有的 permission_rule,将其设置返回 前端储存 c、前端每次发送请求时,携带 pemission_rule 的信息
因 startData()是静态方法,只能操作静态成员变量,在并发时会有安全问题,步骤如下: a、使用 ThreadLocal 作为全局变量,存储当前线程的变量 b、引入 json,将 Permission_rule 格式化成字符串,再在 PermissionHelper 中解析成 LIst c、在需要数据权限的地方使用 PermissionHelper.startData(header),参数为前端传回的 登录用户信息 d、因 PermissionHelper 在 utils 工程,方便其他工程引用,所以在 header 不能直接传, 采用 json 字符串来传 e、PermissionHelper 实现了 Mybatis 的 Interceptor 接口,并且拦截 perpare 方法,才 能对 sql 进行操作并返回给 mybatis 去执行(拦截 query 方法无效) f、PermissionHelper 对是否需要数据权限进行判断,此变量放在了 TreadLocal 中,有 且仅有当前线程能访问 g、对 header 的 json 字符串进行解析,有权限规则 List h、对权限规则 list 进行遍历,按主表、从表的顺序进行匹配规则、替换变量 i、返回 sql,给 mybatis 执行
• 解析出 MainTable 和 JoinTable。from 之后跟着的称为 MainTable,join 之 后跟着的称为 JoinTable。这两个就是我们 PermissionRule 需要匹配的表名, PermissionRule::fromEntity 字段。
• 解析出 MainTable 的 where 和 JoinTable 的 on 后面的条件。使用 and 连接 原本的条件和待注入的条件,PermissionRule::exps 字段。
1. 适用性有限,基于底层的 Mybatis。 2. 方言有限,针对了某种数据库(我们使用 Mysql),而且由于需要在底层解 析处理条件所以有可能造成不同的数据库不能兼容。当然 Redis 和 NoSQL 也无法限 制。
当然,假如你现在就用 Mybatis,而且数据库使用的是 Mysql,这方面就没有太大影响 了。
2. To B 一般都不是开放的,只要做好认证关口,能够进入系统的只有内部员 工。大部分企业内部的员工互联网知识有限,而且作为内部员工不敢对系统进行破坏 性的尝试。
所以针对现在的情况,考虑成本与产出,大部分设计者也不愿意在权限上进行太多的研发 力Hale Waihona Puke Baidu。 菜单和界面元素一般都是由前端编码配合存储数据实现,URL 访问资源的控制也有一些框架比 如 SpringSecurity,Shiro。 目前还没有数据权限控制的框架或者方法,所以自己整理了一份。
1. URL 访问资源(接口以及网页) 2. 界面元素资源(增删改查导入导出的按钮,重要的业务数据展示与否等) 3. 数据资源
现在业内普遍的实现方案实际上很粗放,就是单纯的“菜单控制”,通过菜单显示与否来达 到控制权限的目的。平台分为 To C 和 To B 两种:
1. To C 一般不会有太多的复杂权限控制,甚至大部分连菜单控制都不用,全 部都可以访问。
{uid} 会自动替换为当前用户的 userId, {me} 主实体名称 {me.a} 主实体别名 格式如: userId = {uid} (userId = {uid} AND authType > 3) ((userId = {uid} AND authType) > 3 OR (dept in (select dept from depts where manager.id = {uid})))
h、执行完需关闭权限控制开关
4、用法
1、引 maven 坐标 <dependency>
<groupId>com.auth</groupId> <artifactId>ac-framework-utils</artifactId> <version>1.0-SNAPSHOT</version> </dependency> 2、编写权限规则 在 permission_rule 表中编写权限规则,各字段解释如下: Name: 规则名称 Ids :适用角色 id 列表,格式如:1,2,(用逗号隔开) Main_tables :适用的主表,格式如: Role,User, (用逗号隔开) Exps :过滤表达式字段,