使用过spring全家桶的朋友们,提起权限控制,一定会想到spring security,没错,spring security实现访问控制非常方便简洁。但在企业级应用中,仅有功能权限控制是不够的,例如,A与B同一角色,但分属不同部门,其所看到的数据是完全不同的。简而言之,即:

角色(role)或权限(privilege)只是对行为(api、method)等在代码级做的控制

数据访问权限的控制,是面向数据的权限控制。

那么今天来介绍一下,笔者在实际应用中,是如何“优雅”的实现数据权限控制:

  1. 什么场景需要面向数据做权限控制?
  2. 界定权限控制的职责
  3. 如何抽取公共组件?
  4. 基于Spring AOP实现数据访问控制

什么场景需要面向数据做访问控制?

拿刚刚的例子来说,两个同级别同角色的人,由于身处不同的部门,其数据可见性是不同的,A能看到A所在部门的数据,却不能看到B所在部门的数据。如下图中每个节点上的人,都只能看到本节点及以下节点的数据:
图一

从功能上来说,我们希望:

  1. 结果集过滤:查看结果时根据当前用户所在层级加过滤条件
  2. 平行越权控制:即增删改查别人的数据,此时,应拒绝访问,并给出提示

界定清楚权限控制的职责

首先,从单一职责的角度出发,应当将结果集过滤归于业务处理的范畴,对于API而言,由参数决定其资源范围;对于方法而言,由入参决定其返回结果范围;做到input与output对应。于是,我们只剩下第二个平行越权控制需要放在权限控制来考虑,即:如果你访问了不该访问的数据,拒绝访问,并给出错误提示(通常是403 forbidden)。

可即便如此,每个api每个方法都这么校验,尤其不同api校验规则可能还不一样,这将是多么浩大并且不易维护的一个工作啊!

如何抽取公共组件

那么,有没有现成的框架来实现数据权限控制呢?同样是普遍面临的权限控制问题,面向功能的控制,如spring security一样的框架,非常之多,而数据权限控制就少之又少呢?

原因很简单:面向数据的权限控制,与功能权限控制相比,其与业务相关度更大,没有一家企业的层级结构与业务逻辑是一样的。
举几个例子:

  1. A公司的部门职级非常严谨,每个人只能看到比自己低级别的人的数据;
  2. B公司很宽松,只有部分敏感信息不可见,其他信息人人可见;
  3. C公司有特权部门,在该部门的人,不受级别限制,可以操作全公司的数据,其他部门则不然;
  4. D公司数据对所有人可见,但不同级别有不同的修改权限,低级别的人不能删除修改,可以增加,高级别的增删改查均可……

这样例子数不胜数,那我们就没有什么办法抽象出一个数据控制层来统一处理吗?

当然有办法。只是粒度问题。

我们需要抽取一个与业务无关,或者能够将业务代理出去的框架,唯有这样才能称之为框架。

首先,我们来考虑当一个请求进来时,有多少事情要做(以api粒度列出):

data validation flow

在我们的例子中,有两处需要与用户所属层级进行比对:

No. Item Descrition
1 参数校验 例如:获取某部门下数据,/api/XXX?deptId=123
2 结果集校验 api所返回的api,是否包含不可访问的内容?

那么有朋友要问,如果我的权限设计里,用的不是层级来做比对,而是有其他业务规则,怎么办?

好问题,如果我们把与业务逻辑的部分抽离,仅仅按照操作顺序可以列出如下几步:

data check steps

好,现在这几个步骤已经与业务无关了。其中:

  • 身份校验,由负责功能权限验证的框架来实现(如spring security);
  • 获取关键信息,也可以从功能权限控制的代码得到,我们只需要实现一个接口即可。
  • 输入参数校验,输入是最重要的部分,前面我们提到过,输入参数和结果应该是吻合的,因此很大一部分api根据参数足以完成平行越权的校验。对于那些在api内部通过session获取当前用户,完成过滤查询结果集的情形,既然用户信息已经获得了,自然也无需校验了。即便校验,也可以在条件查询的方法内部,继续校验入参。仍然是一样的原理。
  • 输出参数校验,考虑极少数的情形下,按照登录用户附带信息查询到的结果,仍然存在不允许访问的数据,那么我们需要对结果进行处理(过滤或者回滚)。原则上,完成了输入校验后,还存在这种情形,应考虑是否设计存在漏洞,以至于输入输出不符。

到此,我们的新框架职责梳理清楚了。

真的吗?

难道不需要考虑一下,如果项目中存在内部定时任务、系统集成、微服务互访等等不存在登录用户的情形吗?

嗯,我们前面的分析,都建立在,有登录用户,取得了校验所需必要信息的基础上。如果你的项目存在上面这些没有登录用户的校验,是否需要进行数据访问控制校验?

无论你的答案是Yes或No,我们已经意识到,这是一个必须定制的点,由此,我们可以得到这套新框架需要哪些模块了。

modules

基于Spring AOP实现数据访问控制

分析清楚开发任务之后,下来我们来实现,这里安利一下笔者自己实现的一套代码:https://github.com/lotuswlz/data-access-security.git

(未完待续)