WEB应用数据权限控制轻量级解决方案aliang2012-3-2目录1前言 (3)2方案目标 (4)3设计思路 (4)4逻辑流程图 (6)5数据库表设计 (7)6关键技术点 (7)7方案应用 (8)8总结 (9)1前言很早就想把自己关于WEB应用中数据权限控制的思考写出来同大家分享,一直没有抽出时间,今天总算把此码成文字,以便能给对此感兴趣的朋友提供一种思路。
在许多应用开发中我们都会涉及到权限管理,当然,权限管理主要分两部分,即功能权限和数据访问权限。
针对功能权限的控制,想必很多朋友都有很成熟的方案,在此不再废话。
本文档主要探讨在WEB应用开发中的数据权限控制。
现在,许多的软件公司开发WEB应用都是利用开源框架S(Struts)S(Spring)H(Hibernate)或S(Struts)S(Spring)I(ibatis)来进行WEB应用开发的。
关于数据权限的控制一般都是在代码开发时对访问数据库数据的SQL语句添加涉及数据权限控制的WHERE条件来实现。
这样做的弊端就是在开发时就要详细分析哪些数据需要加入权限控制,并且这些关于数据权限控制的代码散布到整个应用中。
一旦需要调整时就要对许多的SQL语句扒拉一边进行修改,很是耗费精力。
在做项目时,我也同样经受过这样的痛苦,于是我就想,能否把WEB应用的数据权限控制独立出来,就象Spring和切面编程一样,做到代码侵入度最低,灵活方便的添加数据权限控制。
于是就有了我下面的思考和验证。
许多成熟的开发平台都有自己的一套关于数据权限控制的方案,这些控制都是依托数据集来完成的,通过把数据访问的规则加到数据集上来实现对数据访问的控制。
但在WEB应用开发中,这些开源框架中都没有数据集的概念或展现,准确的说是很难在开发或运行状态下获取到有哪些数据集及数据项。
其实数据集的本质就是SQL语句返回的结果集(从XML或表格文件等形成的数据集不在此讨论范围),对数据集加入数据访问控制就是对SQL语句加入数据过滤条件。
既然开源框架中没有数据集可用,那我们是否可以把数据访问控制直接应用到数据库表或视图上来达到目的呢?经过思考和验证完全可以做到。
2方案目标做到代码低侵入度,在开发时不需要太多关注数据权限控制,可以在应用开发完成后,通过对表和视图定义权限控制策略,然后绑定到登录用户或功能URI 上来进行数据权限控制。
数据访问控制需要调整时,只需要修改定义的权限策略即可。
3设计思路数据权限控制需要解决的主要问题就是谁能访问哪些数据。
只要搞定谁正在访问哪些表或视图,即可在运行时根据定义的数据访问策略加入数据权限控制。
如何找到访问数据库表和视图的访问者是个问题。
WEB应用开发中一般登录人员的信息都是保存在Session中,但Session中保存的信息只能Struts的Action 或Servlet能够访问,Server层和ORM层都是不能访问Session中存储的登录者信息的,而最终访问数据库的SQL语句也只有在Datasource层才是最完全的,登录者信息不能通过调用对象方法时以参数方式传递,否则代码的改动和侵入度就太高了。
要找到一种方法和一个点,即能获取到访问者信息又能获取到执行的SQL语句即可完成数据权限控制。
1、对需要进行数据权限控制的表和视图定义访问策略。
定义数据访问策略实际上就是定义要添加到表的Where条件。
但同一个数据访问策略条件,不同的登录者访问将具有不同的结果。
这就要求添加到Where 条件中要含有访问者信息。
比如登录者只能看到自己所属机构的数据,那定义的访问策略条件中就要含有登录者所属机构号。
要做到这点就要事先定义系统参数项,例如定义@LOGIN_ORG_ID代表登录者所属机构,在运行时添加数据访问权限(定义的数据访问策略)时,把定义的的数据访问策略中的@LOGIN_ORG_ID 替换为登录者所属机构号即可实现目的。
当然这些定义的访问策略是要保存到数据库内。
表的设计也很简单,只需要两个字段,一个BOJECT_NAME字段(存储表名或者视图名),一个SQL WHERE字段(存储用户访问控制的WHERE条件语句)。
而且要开发一个定义这些策略的WEB功能。
一般常用的系统参数项有://当前请求地址的系统参数ID:"@REQUEST_URI";//当前登录用户的IP的系统参数ID:"@LOGIN_IP";//当前登录用户的系统ID的系统参数ID:"@LOGIN_USER_ID";//当前登录用户的姓名的系统参数ID:"@LOGIN_USER_NAME";//用户口令:"@DLOGIN_PASSWORD";//当前登录用户的登录时间点的系统参数ID:"@LOGIN_DATETIME";//当前登录用户的登录帐号的系统参数ID:"@LOGIN_USER_LOGID";//当前登录用户的所属机构ID的系统参数ID:"@LOGIN_ORG_ID";//当前登录用户的所属机构名称的系统参数ID:"@LOGIN_ORG_NAME";//当前登录用户的所属部门ID的系统参数ID:"@LOGIN_DEPT_ID";//当前登录用户的所属部门名称的系统参数ID:"@LOGIN_DEPT_NAME";//当前登录用户的所属岗位ID的系统参数ID: "@LOGIN_STATION_ID";//当前登录用户的所属岗位名称的系统参数ID:"@LOGIN_STATION_NAME";2、开发一个为用户或角色和URI分配数据访问策略的WEB功能页面。
定义的数据访问策略不仅可以绑定到用户或角色,而且可以绑定到请求URI 上。
为每一个登录用户在内存中维护一套数据访问策略数据,确实会占用一些内存资源,大部分访问数据的URI对每一个登录用户使用相同的数据策略,针对不同登录用户只是策略语句中需要替换的参数值不同而已,这样倒可以把数据访问策略绑定到URI请求上,那么对于系统URI的策略只需要维护一份数据即可。
所以两种绑定方法(绑定到用户或URI)应该结合使用。
3、在登录验证功能中,当登录验证通过时初始化好定义的系统参数项值,同时获取绑定到此用户的数据访问策略,一并保存到Session内。
对于系统定义的参数项值,每一个登录用户的值都是不一样的,这就要为每个登录用户维护一套系统参数项值,其实也就是登录用户的属性信息,可以添加到MAP中并保存到此登录用户的Session中,以MAP表的键值对(map的key 就是定义的参数项)形式存储的目的就是为了替换数据策略条件中的参数项时方便获取,直接调用map的get(key)即可获取键值对应的值。
4、添加一个filter,在每次提交页面请求时把登录用户的信息放到线程内。
每次页面的URI请求,从请求开始到处理到最后的Response返回就是一个线程。
我们把登录用户的信息存贮到线程内(包括本次请求的URI),在datasoure 提交SQL语句前取出线程内的登录用户信息,并根据定义的数据权限策略改写SQL语句添加数据访问条件,然后再提交给数据库执行改写后的SQL语句即可。
5、实现一个分析SQL语句并根据数据访问策略定义添加数据权限的过程。
从SQL语句中分析出所访问的表和视图确实是个比较复杂的过程,虽然繁琐但也不是不可为。
利用正则表达式匹配我们可以完美做到对SELELECT、DELETE、UPDATE、INSERT语句进行分析,甚至包括语句中的子查询或者使用了表别名都没问题,难度不太大的一个主要原因就是此分析仅仅只需要获取访问的表或视图。
当然我提供的分析过程只验证了ORACLE数据库语法。
其他数据库的验证如果有问题可以在进行修正。
不过关键的语法都是通用的,差别较大的只是各数据库的函数而已,应该问题不大。
6、实现DataSource的代理。
需要代理类有:DataSource、CallableStatement、PolicyStatement、Statement,这样做的目的就是全面接管DataSource及DataSource创建的对象,主要目的还是在提交SQL语句前改写SQL语句。
4逻辑流程图此数据权限控制方案只需要在前端添加一个filter过滤器和后端添加一个DataSource代理,以及登录代码中获取绑定的数据策略(在登录代码中获取相关用户属性和绑定的数据策略保存到Session中是为了不用每次页面提交请求时都去访问数据库),此对现有的系统代码侵入度应该是比较低的。
添加或去除此数据控制方案也比较容易。
5数据库表设计功能URI登记表和角色表此处就不再列出。
6关键技术点1、Filter如何把用户信息和绑定的数据策略放入到本线程内Java提供了一个ThreadLocal类,可以在一个类中定义一个静态的ThreadLocal,在系统第一次加载类时创建一个实例:private static ThreadLocal threadLocalSession = new ThreadLocal();此类同时提供set和get方法操作此对象threadLocalSession,每个线程直接把自己需要的信息(登录用户信息和绑定到用户/角色的数据策略)整合成一个MAP对象set到threadLocalSession内,在使用时调用get方法获取即可。
不同的线程不会覆盖别的线程set到threadLocalSession的对象。
本人提供的DataPolicyEngine.java类中包含有相关代码。
2、在源SQL中添加绑定的数据策略在Datasource代理层获取到执行的SQL语句,然后分析出SQL中所访问的数据库表或视图。
再根据绑定到URI或访问用户上的数据策略来改写执行的SQL 语句,同时替换数据策略中使用的系统参数。
本人提供的DataPolicyEngine.java 类中包含有相关代码。
Datasource代理层捕获到执行的SQL语句后调用DataPolicyEngine 类中提供的方法(方法所完成的工作是分析SQL语句同时添加数据访问策略并替换策略中的系统参数)。
3、Datasoruce代理配置所提供的示例代码中Datasoruce代理是运用Spring的对象注入方式,其配置代码在applicationContext-resources.xml内。
7方案应用本次提供的部分代码没有涉及同URI绑定数据策略的代码,其实同URI绑定数据策略与用户绑定策略的方法类似,可以把同URI绑定的数据策略存储在静态类中实现。