前言
如何实现一个高效简单的系统权限体系是我们长期以来都在思考的问题,也是最近一年来我思考得最多的问题,我们所期望的权限都是应当能够根据应用的需要不断添加和扩展的权限,并且最好能够以最简单的方式来支持,那就最好不过了。
内容
什么是权限系统
那么我们需要一个什么样的权限系统呢或者说什么是权限,我查看了很多的相关资料想要试图解决这个问题,最后看一个最简单最明确的答案"安全问题就是解决谁对什么能够进行什么控制的问题”。如何来分析这段话呢,我敢保证这是我见过的最明确也是最抽象的需求,通过什么样的方式来分析和解决这个需求问题呢。
而对复杂的问题我们必须拿出强大武器才行啊,对于不明确的概念我们首先需要完成的是将“概念明确化”先将这段话中不明确的代词进行明确化,那么我们就从第一个代词开始“谁”是指什么呢,“员工”,“用户”,“领导”,...,还是什么其它呢,我想对于不同的问题我想需要采用不同的概念,如果这个系统只是用于解决公司内容的相关问题,那么这里使用“员工”最好不过,如果专门用于解决“领导”的个人问题,那么“领导”也不错。这里主要需要考虑我们所面临问题和抽象的层次,同时还需考虑到具体应用的行业标准。对于需要大多数情况采用“用户”或“当前用户”是一个比较好抽象,我们这里采用“当前用户”,这样比“用户”更明确一些,那么这段需求就变成了“安全问题就是解决当前用户对什么能够进行什么控制的问题”。
好解决了第一个抽象之后,对于其它问题我们同样采用相关的方式来进行分析,“当前用户对什么” 之中的“什么”当前相关问题的抽象层次之中基本上都是采用“安全资源对象”来进行抽象的,当然也可以根据实际处理的问题进行抽象,我们这里主要是考虑通用性。而对于“能够进行什么访问”之中的“什么”这里也需要根据相关问题的来进行设定,这里我们采用“访问或拒绝”,那么这一段需求就变成“安全问题就是解决当前用户能够对资源对象进行访问或拒绝控制的问题”,如果实现需要就是对功能进行控制还可以说成是“安全问题就是解决当前用户能够对功能进行访问或拒绝控制的问题"。
一个简单的设计
了解了问题的本质后,我们需要做的就是要完成它,那么怎样来完成这呢。首先我们需要从需求之中将我们领域模型分离出来:
如果是一个简单的权限设计,那么整个权限系统不这样就完成了,非常的简单明了(对于这里各对象所需要领域访问就不在说明)。
基于角色的访问
当简单的设计使用的用户数量一天一天的增加的时候,我们发现给一个一个用户分别进行权限的设置是一件非常困难的工作。好,看来是我们修改设计的时候到了。最先想到的是我们必须对用户进行分组,将对用户的访问授权设置到这些组之中。好,那我们就设置一个用户组,通过用户组来设置吧,但我们又会想到在实际的业务之中一个多岗的情况很多啊,那么怎么办呢,同时如果能够以现实际之中公司的实际情况设置权限最好不过,因为这样对于系统的管理人员来说,只需要了解公司内部的情况就可以了,那么怎么样来协调这两个问题呢。
其实这个问题在安全设计的前辈们早已想到应对之策,就是将这些对象抽象以“角色”来表示这些抽象的概念。那么加入的角色对于我们的领域模型带来了什么样的变化呢?
这样对于我们的管理来说真的大大的简化了啊,但这就完全的满足了我们的条件了吗?
最初的数据权限
当我们正在为自己的设计而高兴的时候新的问题又来了,怎么回事呢。小郑啊,北美销售部只能够管理北美的销售啊,发动机事业部只能够销售发动机产品啊,你那个程序是怎么回事啊,怎么都能够看啊,还有怎么部长都能够审批100W以上的定单啊。
啊,我快要完蛋了。
好,兵来将挡,水来土淹什么问题都能不倒我。
看来我们还必须要设计满足数据权限的要求才行啊。
好吧,那我怎么样子来实现呢...?好吧,我应在安全资源之中加一个数据规格,在访问控制之中加入一个值的设定,怎么样,这下行了吧?
好吧,现在就看我来实现它吧,以我精湛的技术。
啊,什么,一个用户多个角色,不同的角色有不同的数据规则控制。啊,还需要数据控制的合并,没事我们使用提供者模式来解决,实现不同的合并提供者,使用提供者来合并结果。
啊,什么,怎么样转换到数据访问的控制,没事,咱们硬编码。
啊,不可能吧,连个获取操作都需要进行控制啊。
啊,我不行了。倒在了黎民前了晚上。
更好的解决方法
如果没有能够对数据权限进行详细的分析,那么一定会在倒在黎明的前夜,没有完整的解决方法,那是不行的。好,我们先来分析一个数据权限的要求,这里我只能够写出我对于这部分的分析,数据权限的限制总是:{实体属性值 条件 允许值}这三个产品分组成,如:数据规则,销售部长只能够审核销售金额小于100W的定单这里的三者是{销售定单.合计金额 小于 100W}, 销售员小张只能够销售摩托车产品,这其中的三者是{销售项目.产品类型 属于 摩托车)
所以这里对于数据的限制都只包括三个要素:领域实体属性 条件 允许值。这三者的结合就限制的约束。那么这三都之中都有哪些情况呢?
领域实体属性:这个基本上都没有什么变化。
条件:如果是条件就只有那么几种,有大于,小于,等于,实体范围等等。
允许值:允许值这里经过我的分析包括三种情况{设置值,用户属性相关值,业务设定值}
这三种情况分别使用于以下情况:设置值表示是我们是设置权限的时候直接进行设置的如100W,用户属性相关值表示与用户的某一个属性相关,如:部门,职务等。还有一种就是业务相关的情况,这类情况相当于是在业务之中设置数据。这类问题在SAP之中设置比较多,感兴趣的可以去了解一下SAP之中的相关设置。
在这里对于条件的设置基本上都只有固定的几种,可以采用枚举的方式来进行,而对于充许值这一个点,因为不同的业务所需要的不同的数据和相关内容不想同,因此,将这一个允许值的获取设置为一个相关的接口来进行,而在进行配置的时候只设置一个相关值,由接口去解晰和获取。这时候的领域模型图如下:
最后的战役
在解决了领域问题之后,我们还需要考虑提供什么方式来获取这些服务,这里首先需要考虑使用权限的两种情况,一种是当需要显示相关界面时; 二是当需要执行相关操作时。
对于第一种情况:我们在执行GetCountries()和GetProducts()这两个方式的内部就必须执行条件的限制,那么如何来实现这个条件的限制呢。这里必须要通过一种方式让GetCountries了解到当前正自于那一个安全的环境之中,再获取获取相应的数据限制,查看这个限制的实体是否为Country如果是那么是那个条件进行的设置编号,国家名称,上级部门。再将这些条件转换为对应的SQL来进行限制,如果这些方法都是通过硬编号来完成,那么一定是一个复杂的工作,好在现在的ORM工具直接就支持领域实体,属性,条件等内容,直接可以将这些配置的安全值设置到这些访问之上,这样就可以完成整个数据限制,同时还需要考虑到如果是在多个不同的应用之间设置这些服务,还需要考虑到安全的层次关系,在多个层次之中使用和设置多层安全环境。这就必须使用一个对应的环境管理类来管理这些环境。对于这个环境可以采用SecurityContext来表示,而对于这个环境的管理类使用SecrityContextManager的线程单例模式来进行设置。在实现的过程之中,如果需要一个新的安全环境则建立一个新的SecurityContext,在新实例的SecurityContext之中将自己注册到SecurityContextManager的单实例之中。同时在建立SecurityContext时传入相关的安全资源对象,对这些对象进行安全检查,如果没有进行授权则引发System.Security.SecurityException对象。
在开发数据访问层时,通过SecurityContextManager.Instance来获取如:Employee实体的的限制,通过前面所设计的获取允许值提供者接口来获取允许的值,并且将属性,条件,允许值,转换为对应的SQL来进行调用。这样就实现了多层次的数据权限。如果对应的ORM工具能够直接支持条件的设置则可以将这个工作直接编写对应的转换器来进行转换,这样可以大大的提高开发的效率,并且还可以根据需要对领域成员进行新的数据约束,而不必修改应用程序。
同时如果所需要限制的数据资源不是领域实体中的数据,也可以通过这种方式来设置自己的数据范围的检查。
总结
本文主要是介绍一种安全的实现方式,安全不是一个简单到只需要在某一具体层就能够解决的问题,而需要在多个层次进行多层次的设置。形成类似于IIS式的多层次,多角度权限设置,这里只是给出了其中最为核心的内容,没有给出具体的代码,这主要是因为不能够公布公司内部开发的程序,所以可能对于完整的理解有所不足。
对本文有什么好的意见或见意请大家多指教。