文档章节

基于JDBC层数据权限是如何设计的

悠悠然然
 悠悠然然
发布于 2016/05/15 20:39
字数 6528
阅读 7982
收藏 245
点赞 16
评论 51

    前面一篇博客聊聊数据权限哪些事儿介绍了数据权限配置文件和数据权限的几个示例,这篇文章来详细介绍下tiny版本的数据权限是如何设计的?

    Tiny版本的数据权限是基于jdbc底层实现的,可以把它理解为jdbc驱动的增强版实现,可以完全替代底层数据库的驱动。它是轻量级框架,以jar包形式提供服务,无proxy代理层,无需额外部署,只需依赖底层数据库的驱动,而且DBA也无需改变原有的运维方式。

它有以下特点:

(1)、无侵入性,可以在任何java环境下运行

(2)、支持jdk1.5到1.8版本环境下运行

(3)、跨数据库,实现不跟具体底层数据库绑定,可以支持任何第三方数据库

(4)、适用于任何第三方数据库连接池如:DBCP, C3P0, BoneCP, Druid等

(5)、适用于任何基于java的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。

(6)、对程序员透明,数据权限相关代码不需要进行硬编码

(7)、与不同的用户权限能较好集成。

主要提供以下功能:

 (1)、数据行权限控制–让不同的人看到不同的数据行的权限控制

(2)、数据列权限控制–对使用者限制其访问某些列中的数据的权限控制。

    下面这个图是tinydac的整体架构图。

从架构图可以看出,tinydac内部有以下几部分组成,数据权限配置、JDBC接口规范实现、SQL解析层、数据权限匹配处理层、以及SQL执行层。下面将详细这些层次是如何设计的?

在介绍各层次如何设计之前,我们先来介绍下tinydac的基础概念

中文名称

英文名称

说明

数据权限配置

DataAccessControl

数据权限配置节点对象,里面可以包含多个权限规则配置,以及物理数据源配置信息

物理连接配置

OriginalConnectionGetter

配置物理数据源相关信息,可以支持datasource和jndi方式

表规则

TableRule

表规则配置,下面可以定义增删改查的规则配置

新增规则

InsertRule

新增操作的规则配置,支持值检查规则、字段过滤规则

更新规则

UpdateRule

更新操作的规则配置,支持值检查规则、字段过滤规则、附加条件规则

删除规则

DeleteRule

删除操作的规则配置,支持值检查规则、附加条件规则

查询规则

SelectRule

查询操作的规则配置,支持值检查规则、字段过滤规则、附加条件规则

值检查规则

ValueCheckRule

值检查规则配置信息

字段过滤规则

FilterRule

字段过滤规则配置

附加条件规则

ConditionRule

附加条件规则配置

数据权限上下文

DataAccessControlContext

数据权限上下文接口,可以加载框架属性配置信息、设置全局上下文参数与基于线程上下文的参数

SQL解析接口

SQLParser

SQL解析接口,解析SQL,解析结果信息由SqlParserResult封装

SQL解析结果集

SqlParserResult

SQL解析结果集对象

SQL解析工厂

SQLParserFactory

创建SQL解析实例的工厂

SQL解析缓存

ParserCache

存储SQL解析过程中创建的访问者对象

表格规则匹配器

TableRuleMatcher

根据解析sql的表名获取表名相关的表规则信息

增删改查操作类型匹配器

SqlTypeMatcher

用于匹配增删改查操作类型

SQL操作规则处理接口

SqlRuleProcessor

增删改查SQL操作规则处理的抽象接口

值检查规则处理接口

ValueCheckRuleProcessor

定义值检查规则处理接口方法

字段过滤规则处理接口

OperationFieldFilterRuleProcessor

定义字段过滤规则处理接口方法

字段过滤处理接口

FieldFilterProcessor

定义字段过滤处理接口方法

字段过滤处理接口的工厂

FieldFilterProcessorFactory

创建字段过滤处理接口的实例

附加条件规则处理接口

AppendConditionRuleProcessor

定义附加条件规则处理接口

附加条件别名处理接口

AliasConditionProcessor

附加条件处理时,原生sql存在别名,需要给附加条件加上原生SQL中的表别名信息

附加条件别名处理接口的工厂

ConditionProcessorFactory

创建附加条件别名处理接口的实例

新增操作规则处理接口

InsertRuleProcessor

提供了值检查规则处理、字段过滤规则处理

删除操作规则处理接口

DeleteRuleProcessor

提供了值检查规则处理、附加条件规则处理

更新操作规则处理接口

UpdateRuleProcessor

提供值检查规则处理、字段过滤规则处理、附加条件规则处理

查询操作规则处理接口

SelectRuleProcessor

提供值检查规则处理、字段过滤规则处理、附加条件规则处理

表元数据获取接口

TableMetaDataProvider

获取表相关的数据库元数据信息,包括表信息以及表中定义的列信息

表达式执行的引擎接口

ExpressionEngine

定义表达式执行的接口方法,内部默认实现是基于groovy语言的表达式引擎

创建表达式执行引擎的接口

ExpressionEngineFactory

创建表达式执行引擎实例的工厂

SQL执行上下文参数对象

StatementTableActionContext

SQL执行上下文,存放sql语句相关的参数上下文以及SQL解析相关的信息

数据权限配置管理器

DataAccessControlManager

数据权限配置信息的管理对象

PreparedStatement参数处理器

ParameterHandler

定义PreparedStatement参数信息的处理器

PreparedStatement参数上下文

ParameterContext

PreparedStatement参数上下文

PreparedStatement参数设置方法的枚举

ParameterMethod

PreparedStatement参数设置方法的枚举

值转换接口

ValueConverter

用于ResultSet.getObject方法,把设置的字符串默认参数值转换成列对应的实际类型。

存储当前执行SQL的对象

ThreadLocalSqlHolder

在线程上下文中存储当前执行的SQL语句

1.数据权限配置

    数据权限配置文件序列化生成的对象是DataAccessControls,数据权限配置文件内容示例:

<data-access-controls>

    <data-access-control id="selDataAccessControl">

        <connection driver="org.h2.Driver"

                        user-name="sa" password="123456" url="jdbc:h2:./dbfile/dac_simple;mode=MYSQL;DB_CLOSE_ON_EXIT=FALSE"

                        class="org.tinygroup.dac.config.DataSourceMode">

        </connection>

        <access-control-rule>

            <table-rules>

                <table-rule table-name="custom">

                    <select-rule>

                        <condition-rules>

                            <condition-rule>

                                 <expression>

                                    <![CDATA[

                                        #s_age#!=null

                                    ]]>

                                </expression>

                                <append-conditions>

                                    <append-condition>

                                           <condition>

                                                <![CDATA[

                                                     "age>"+#s_age#

                                                ]]>

                                           </condition>

                                    </append-condition>

                                </append-conditions>

                            </condition-rule>

                        </condition-rules>

                        <filter-rules>

                            <filter-rule>

                            <expression>

                                <![CDATA[

                                        #age#<20

                                    ]]>

                            </expression>

                            <filter-columns>

                                <filter-column column-name="age" value="20"></filter-column>

                            </filter-columns>

                        </filter-rule>

                        </filter-rules>

                        <value-check-rules>

                            <value-check-rule column-name="name">

                                 <expression>

                                    <![CDATA[

                                        #name#.contains("sds")

                                    ]]>

                                </expression>

                            </value-check-rule>

                        </value-check-rules>

                    </select-rule>

                </table-rule>

            </table-rules>

        </access-control-rule>

    </data-access-control>

</data-access-controls>

配置文件说明

1.data-access-controls

 对应的配置对象DataAccessControls,作为配置文件的根节点出现,节点的所有内容会组织成DataAccessControls对象,可以包含多个数据权限配置

2.data-access-control

 数据权限配置节点,对应的配置对象DataAccessControl。

 id:数据权限配置的唯一标识,在jdbc url参数中体现,例如:url=jdbc:dataaccesscontrol://selDataAccessControl,selDataAccessControl就是数据权限配置id

 connection:获取物理数据库连接的配置节点,节点内容对应的配置对象OriginalConnectionGetter,TinyDAC支持两种方式获取物理数据库连接。

 (1)、DataSourceMode:数据库连接池方式获取connection,上面的配置示例就是DataSourceMode方式

      user-name:数据库用户名称,password:数据库密码,url:物理数据库的url,driver:数据库驱动

 (2)、JndiMode:通过jndi方式获取connection

      配置示例:

<connection jndi-name="test0" env-file="jndi.properties"

                        class="org.tinygroup.dac.config.JndiMode">

</connection>

 jndi-name:绑定在jndi的名称,env-file:加载到jndi上下文的属性信息

3.access-control-rule

数据权限规则的节点,对应的配置对象AccessControlRule,节点下面可以定义多个基于表的数据权限规则配置

4.table-rule

数据权限表规则节点,对应的配置对象TableRule,节点下可以定义增删改查sql操作的配置

5.insert-rule

插件语句的规则配置节点,对应的配置对象InsertRule,新增操作支持值检查规则、字段过滤规则

6.delete-rule

删除语句的规则配置节点,对应的配置对象DeleteRule,删除操作支持值检查规则、附加条件规则

7.update-rule

更新语句的规则配置节点,对应的配置对象UpdateRule,更新操作支持值检查规则,附加条件规则,字段过滤规则

8.select-rule

查询语句的规则配置节点,对象的配置对象SelectRule,查询操作支持值检查规则,附加条件规则,字段过滤规则

9.value-check-rule

值检查规则配置节点,对象的配置对象ValueCheckRule

  column-name:指定要检查的列

  expression:值检查规则的表达式,需要是一个返回boolean类型的表达式

10.filter-rule

字段过滤规则配置节点,对应的配置对象FilterRule

   expression:字段过滤规则的表达式,也是一个返回boolean类型的表达式,只有返回值是true,才会进行下一步字段过滤操作

   filter-columns:定义所有需要过滤的字段信息

   filter-column:定义过滤字段信息,column-name:指定需要过滤的字段名称,value:设置默认值,在查询语句中有用,查询项被过滤可以返回这个默认值。

11.condition-rule

附加条件规则配置节点,对应的配置对象ConditionRule

   expression:附加条件规则的表达式,也是一个返回boolean类型的表达式,只有返回值是true,才会附加下面的查询条件

   append-conditions:定义所有附加条件的节点

   append-condition:定义附加条件的节点,condition:指定要附加的条件 

2.数据权限上下文接口

    DataAccessControlContext是数据权限上下文接口,其接口定义如下:

public interface DataAccessControlContext {

  

   /**

    *

    */

   public Object getValue(String key);

  

   /**

    *

    */

   public void setValue(String key, Object value);

  

   /**

    * 以map的形式返回所有上下文参数

    * @return

    */

   public Map<String, Object> getContextValueMap();

  

   /**

    * 清除所有参数信息,包括线程上下文中的参数信息

    */

   public void clearAllValues();

  

   /**

    * 设置绑定在线程中的上下文信息

    * @param context

    */

   public void setThreadContext(Context context);

  

   /**

    * 获取绑定在线程中的上下文对象

    * @return

    */

   public Context getThreadContext();

  

   /**

    * 往线程上下文中存放参数信息

    * @param key

    * @param value

    */

   public void setThreadContextValue(String key,Object value);





   /**

    * 获取属性值,所有内容读取自框架配置属性文件

    * @param key

    * @return

    */

   public String getProperty(String key);



   /**

    * 设置属性值

    */

   public void setProperty(String key, String value);

  

   /**

    * 移除值属性

    */

   public void removeProperty(String key);



   /**

    * 返回所有属性

    */

   public Map<String, String> getProperties();

  

}

通过上下文接口,可以读取设置框架级别的属性配置信息,读取设置全局的参数信息,以及读取设置基于线程上下文的参数信息

2.1框架级别的属性配置文件:

    框架级别的属性配置分为两种:

1、定义在TinyDAC内部的框架默认配置文件---default.tinydac.properties

2、定义在客户工程中的属性配置文件—tinydac.properties,当然这个属性配置文件名称不一定命名为tinydac.properties,配置文件名称可以由System对象org.tinygroup.dac.properties的属性值指定

如果两个属性文件中存在相同key,以客户定义的属性值为准,这样可以覆盖框架默认的特性。

下面是TinyDAC内部的框架默认配置文件介绍:

key

默认值

说明

org.tinygroup.dac.update.sql.enable

true

是否开启更新操作规则处理,默认为true

org.tinygroup.dac.update.rule.processor

org.tinygroup.dac.processor.impl.DefaultUpdateRuleProcessor

更新操作规则处理器的实现类,默认实现DefaultUpdateRuleProcessor

org.tinygroup.dac.thread.sql.enable

true

是否把当前执行的sql存放到线程上下文中,默认为true

org.tinygroup.dac.sql.parser.factory

org.tinygroup.dac.parser.impl.DefaultSQLParserFactory

指定创建SQLParser实例的工厂,默认的实现是DefaultSQLParserFactory

org.tinygroup.dac.select.sql.enable

true

是否开启查询操作规则处理,默认为true

org.tinygroup.dac.select.rule.processor

org.tinygroup.dac.processor.impl.DefaultSelectRuleProcessor

查询操作规则处理器的实现类,默认实现DefaultSelectRuleProcessor

org.tinygroup.dac.insert.sql.enable

true

是否开启插入操作规则处理,默认为true

org.tinygroup.dac.insert.rule.processor

org.tinygroup.dac.processor.impl.DefaultInsertRuleProcessor

新增操作规则处理器的实现类,默认实现DefaultInsertRuleProcessor

org.tinygroup.dac.field.filter.processor.factory

org.tinygroup.dac.processor.fieldfilter.impl.JsqlParserFieldFilterProcessorFactory

字段过滤处理工厂类的类路径

org.tinygroup.dac.expression.engine.factory

org.tinygroup.dac.engine.impl.GroovyExpressionEngineFactory

表达式引擎工厂实现类的类路径

org.tinygroup.dac.delete.sql.enable

true

是否开启删除操作规则处理,默认为true

org.tinygroup.dac.delete.rule.processor

org.tinygroup.dac.processor.impl.DefaultDeleteRuleProcessor

删除操作规则处理器的实现类,默认实现DefaultDeleteRuleProcessor

org.tinygroup.dac.date.format

yyyy-MM-dd HH:mm:ss

日期格式,用于查询项值从字符类型转换为日期类型。默认为“yyyy-MM-dd HH:mm:ss”

org.tinygroup.dac.condition.processor.factory

org.tinygroup.dac.processor.appendcondition.impl.AliasFunctionConditionProcessorFactory

附加条件别名处理工厂类的类路径

org.tinygroup.dac.cache.timeout

60

缓存的超时时间

org.tinygroup.dac.cache.size

5000

缓存存储内容的最大数

2.2.全局参数相关API

public Object getValue(String key);



public void setValue(String key, Object value);

 

2.3.线程上下文参数相关API

/**

 * 以map的形式返回所有上下文参数

 * @return

 */

public Map<String, Object> getContextValueMap();



/**

 * 清除所有参数信息,包括线程上下文中的参数信息

 */

public void clearAllValues();



/**

 * 设置绑定在线程中的上下文信息

 * @param context

 */

public void setThreadContext(Context context);



/**

 * 获取绑定在线程中的上下文对象

 * @return

 */

public Context getThreadContext();



/**

 * 往线程上下文中存放参数信息

 * @param key

 * @param value

 */

public void setThreadContextValue(String key,Object value);

3. 数据权限配置管理器

    DataAccessControlManager是数据权限配置管理器接口类,主要用于加载、读取、删除数据权限配置。接口定义如下。

public interface DataAccessControlManager {

   /**

    * 新增数据权限配置

    * @param dataAccessControls

    */

   public void addDataAccessControls(DataAccessControls dataAccessControls);

  

   /**

    * 移除数据权限配置

    * @param dataAccessControls

    */

   public void removeDataAccessControls(DataAccessControls dataAccessControls);

  

   /**

    * 根据数据权限id获取数据权限配置

    * @param rightId

    * @return

    */

   public DataAccessControl getDataAccessControls(String rightId);

  

   /**

    * 新增数据权限配置,location可以是相对于classpath的路径也可以是文件路径,也可以是url路径

    * @param location

    */

   public void addDataAccessControls(String location);

   /**

    * 新增数据权限配置

    * @param inputStream 数据权限配置的流

    */

   public void addDataAccessControls(InputStream inputStream);

  

}

DataAccessControlManagerImpl是框架提供的数据权限配置管理器的默认实现。

4.SQL解析层

    SQLParser 是SQL解析接口类,SQL解析处理接口用来解析SQL,组装SQL解析结果集,接口定义如下:

/**

 * SQL解析接口

 *

 */

public interface SQLParser {

    /**

     * 解析sql语句

     * @param sql

     * @return

     */

    SqlParserResult parse(String sql);

}

SQL解析接口类关系图如下:

类名

说明

SQLParserFactory

创建SQL解析器实例的工厂接口

DefaultSQLParserFactory

框架提供的创建SQL解析器实例的工厂实现类

SQLParser

SQL解析器接口

DefaultSQLParser

框架提供的SQL解析器接口实现类,内部提供jsqlparser进行SQL解析

SqlParserResult

SQL解析结果集接口

DefaultSelectParserResult

sql可以解析支持的解析结果集

SqlNotSupportParserResult

sql不支持解析的解析结果集

    可以进行SQL解析的sql,会把解析结果组装成DefaultSelectParserResult对象,然后根据这个结果集对象进行数据权限处理,如果传人的sql不能进行解析,会跳过数据权限处理,直接把传人的SQL丢到底层数据库去执行。

5.数据权限规则匹配处理

    数据权限规则处理接口对SQL操作进行数据权限控制,其数据权限规则处理类关系图如下:

类名

说明

SqlTypeMatcher

匹配SQL操作类型的匹配接口

SqlRuleProcessor

SQL规则处理的统一接口

ValueCheckRuleProcessor

值检查规则处理接口

OperationFieldFilterRuleProcessor

字段过滤规则处理接口

AppendConditionRuleProcessor

附加条件规则处理接口

InsertRuleProcessor

新增操作规则处理接口,支持值检查、字段过滤规则处理

DeleteRuleProcessor

删除操作规则处理接口,支持值检查、附加条件规则处理

UpdateRuleProcessor

更新操作规则处理接口,支持值检查、附加条件、字段过滤规则处理

SelectRuleProcessor

查询操作规则处理接口,支持值检查、附加条件、字段过滤规则处理

6.表达式引擎

    ExpressionEngine是表达式引擎处理接口,表达式引擎是用来渲染SQL权限规则中出现的表达式,渲染表达式的返回值,可以是boolean或者是String类型。其接口定义如下:

/**

 * 表达式执行引擎接口

 * @author renhui

 *

 */

public interface ExpressionEngine {

  

   /**

    * 执行表达式,返回boolean

    * @param expression 表达式

    * @param params 参数信息

    * @return 渲染结果为boolean

    */

   boolean evalBoolean(String expression,Map<String, Object> params);

   /**

    * 执行表达式,渲染结果为String

    * @param expression 表达式

    * @param params 参数

    * @return 渲染结果为字符串

    */

   String evalString(String expression,Map<String, Object> params);



}

表达式引擎类关系图:

异常号

异常信息

异常号

异常信息

0TE120120001

 SQL:[{0}],检查的列:[{1}],expression:[{2}],表达式的返回结果是false,列检查不通过

0TE120120002

condition-rule:[{0}]中附加的条件表达式为空

0TE120120003

数据权限配置:[{0}]中不存在connection节点配置信息

0TE120120004

sql:[{0}]中,不存在columnName:[{1}]的操作信息,请检查过滤规则配置

0TE120120005

SQL语句有误,找不到列:[{0}]所在的表名

0TE120120006

Connection was null

0TE120120007

Connection:[{0}]获取的DatabaseMetaData对象为空

0TE120120008

找不到框架默认配置文件:[{0}]

0TE120120009

加载配置文件:[{0}]出错

0TE120120010

类:{0}与{1}类型不匹配

0TE120120011

加载框架属性文件过程中出现错误

0TE120120012

原生SQL:{0},由于数据权限过滤规则处理所有操作字段都被过滤,生成的sql是无效的,此次sql操作将被忽略

0TE120120013

打开文件:{0}出错

0TE120120014

打开网址:{0}错误

0TE120120015

SQL:[{0}]暂且不支持,交由底层数据库进行处理 

0TE120120016

从缓存中获取sql解析结果集出错

0TE120120017

获取jndiName:[{0}]对象出现异常

0TE120120018

expression:[{0}]的返回值类型出错,要求返回值的类型是boolean类型,实际类型:[{1}]

0TE120120019

expression:[{0}]的返回值类型出错,要求返回值的类型是String类型,实际类型:[{1}]

0TE120120020

SQL片段:[{0}]不支持处理

0TE120120021

附加条件:[{0}],进行别名处理过程出错

0TE120120022

附加条件:[{0}],获取的AliasSettingExpressionVisitor对象为空

8.JDBC层扩展

TinyDAC是在JDBC层实现的数据权限框架,它可以看做是对数据库驱动的扩展,按照JDBC规范进行实现的。

8.1.Driver

    DataAccessControlDriver直接实现了java.sql.Driver接口,通过重写connect(String url, Properties info) 方法来获取数据权限数据库连接。url参数规范:“jdbc:dataaccesscontrol://”+数据权限配置id,connect方法返回DataAccessControlConnection类型的数据库连接。

8.2.Connection

    DataAccessControlConnection内部管理真正的物理数据库连接,其物理数据库连接的配置就是数据权限配置的connection节点,创建物理数据库连接方式有2种,一种是通过datasource方式创建连接,另一种是通过jndi方式创建连接。DataAccessControlConnection大部分实现方法是直接委托内部管理的connection来实现的,主要是重写了创建Statement和PreparedStatement接口实例的方法。

下面是创建Statement和PreparedStatement接口实例的接口方法列表说明

创建Statement的接口方法

接口方法

说明

createStatement()

使用返回的 Statement 对象创建的结果集在默认情况下类型为 TYPE_FORWARD_ONLY,并带有 CONCUR_READ_ONLY 并发级别

createStatement(int resultSetType, int resultSetConcurrency)

创建一个 Statement 对象,该对象将生成具有给定类型和并发性的 ResultSet 对象。此方法与上述 createStatement 方法相同,但它允许重写默认结果集类型和并发性。 

createStatement(int resultSetType,int resultSetConcurrency, int resultSetHoldability)

创建一个 Statement 对象,该对象将生成具有给定类型、并发性和可保存性的 ResultSet 对象。此方法与上述 createStatement方法相同,但它允许重写默认结果集类型、并发性和可保存性。 

创建PreparedStatement接口方法

接口方法

说明

接口方法

说明

prepareStatement(String sql)

创建一个 PreparedStatement 对象来将参数化的 SQL 语句发送到数据库,返回的 PreparedStatement 对象创建的结果集在默认情况下类型为TYPE_FORWARD_ONLY,并带有 CONCUR_READ_ONLY 并发级别

prepareStatement(String sql, int resultSetType,
int resultSetConcurrency)

创建一个 PreparedStatement 对象,该对象将生成具有给定类型和并发性的 ResultSet 对象。此方法与上述 prepareStatement 方法相同,但它允许重写默认结果集类型和并发性。

prepareStatement(String sql, int resultSetType,
int resultSetConcurrency, int resultSetHoldability)

创建一个 PreparedStatement 对象,该对象将生成具有给定类型和并发性的 ResultSet 对象。此方法与上述 prepareStatement 方法相同,但它允许重写默认结果集类型和并发性。

prepareStatement(String sql, int autoGeneratedKeys)

创建一个默认 PreparedStatement 对象,该对象能检索自动生成的键。给定常量告知驱动程序是否应该使自动生成的键可用于检索。如果该 SQL 语句不是一条 INSERT 语句,则忽略此参数。 

prepareStatement(String sql, int[] columnIndexes)

创建一个能够返回由给定数组指定的自动生成键的默认 PreparedStatement 对象。此数组包含目标表中的列的索引,而该目标表包含应该使其可用的自动生成的键。如果该 SQL 语句不是一条 INSERT 语句,则忽略此数组。

 prepareStatement(String sql, String[] columnNames)

创建一个能够返回由给定数组指定的自动生成键的默认 PreparedStatement 对象。此数组包含目标表中的列的索引,而该目标表包含应该使其可用的自动生成的键。如果该 SQL 语句不是一条 INSERT 语句,则忽略此数组。

8.3.Statement

    java.sql.Statement接口的实现DataAccessControlStatement,它是数据权限框架的核心类,数据权限控制功能通过此类完成的。先来看看DataAccessControlStatement的类关系图:

    从类图可以看出,DataAccessControlStatement通过SQLParser接口进行SQL解析,返回解析结果集对象,SQL解析结果集会告知此次SQL操作语句是那种类型(增删改查SQL类型),然后交给对应的SQL操作数据权限规则处理器进行数据权限控制。例如此次SQL语句是新增语句,那么DataAccessControlStatement内部会交给InsertRuleProcessor进行新增SQL数据权限规则处理。

8.4. PreparedStatement

    DataAccessControlPreparedStatement的类关系图

    DataAccessControlPreparedStatement继承了DataAccessControlStatement,并实现了PreparedStatement接口,用于执行预编译的SQL。DataAccessControlPreparedStatement采用ParameterContext来存储预编译的参数,先看看ParameterContext的类代码

public class ParameterContext {

    private ParameterMethod parameterMethod;

    /**

     * args[0]: parameterIndex args[1]: 参数值 args[2]: length

     * 适用于:setAsciiStream、setBinaryStream、setCharacterStream、setUnicodeStream

     * 。。。

     *

     */

    private Object[] args;

    public ParameterContext() {

    }

    public ParameterContext(ParameterMethod parameterMethod, Object[] args) {

        this.parameterMethod = parameterMethod;

        this.args = args;

    }

    public ParameterMethod getParameterMethod() {

        return parameterMethod;

    }

    public void setParameterMethod(ParameterMethod parameterMethod) {

        this.parameterMethod = parameterMethod;

    }

    public Object[] getArgs() {

        return args;

    }

    public void setArgs(Object[] args) {

        this.args = args;

    }

     

    public ParameterContext copyParameterContext(){

        ParameterContext newParameterContext=new ParameterContext();

        newParameterContext.setParameterMethod(parameterMethod);

        Object[] newArgs=new Object[args.length];

        System.arraycopy(args, 0, newArgs, 0, args.length);

        newParameterContext.setArgs(newArgs);

        return newParameterContext;

    }

    public String toString() {

        StringBuilder buffer = new StringBuilder();

        buffer.append(parameterMethod).append("(");

        for (int i = 0; i < args.length; ++i) {

            buffer.append(args[i]);

            if (i != args.length - 1) {

                buffer.append(", ");

            }

        }

        buffer.append(")");

        return buffer.toString();

    }

     

    @Override

    public int hashCode() {

        return HashCodeUtil.reflectionHashCode(this);

    }

    @Override

    public boolean equals(Object obj) {

        return EqualsUtil.reflectionEquals(this, obj);

    }

    /**

     * 重新设置参数值

     * @param value

     */

    public void setVaule(Object value) {

        args[1] = value;

    }

    /**

     * 重新设置参数位置

     * @param index

     */

    public void setParameterIndex(int index){

        args[0]=index;

    }

}

    args由设置预编译参数接口方法传递的参数值组成的对象数组。ParameterMethod 是一个枚举类,是根据PreparedStatement设置预编译参数接口方法来定义的,可代码如下:

public enum ParameterMethod {

    setArray, setAsciiStream,setAsciiStream1,setAsciiStream2, setBigDecimal, setBinaryStream,setBinaryStream1,setBinaryStream2,setBlob,setBlob1,setBlob2,

setBoolean,setByte,setBytes, setCharacterStream,setCharacterStream1,setCharacterStream2, setClob,setClob1,setClob2,

    setDate1, setDate2, setDouble, setFloat, setInt,

    setLong, setNull1, setNull2, setObject1, setObject2,

    setObject3, setRef, setShort, setString, setTime1,

    setTime2, setTimestamp1, setTimestamp2, setURL, setUnicodeStream, setNCharacterStream, setNCharacterStream1,

    setNClob,setNClob1,setNClob2, setSQLXML, setRowId, setNString,setSQLType,setSQLType1

}

例如设置int类型的参数值

public void setInt(int parameterIndex, int x) throws SQLException {

        parameterContextList.add(new ParameterContext(ParameterMethod.setInt,

                new Object[] { parameterIndex, x }));

}

通过parameterContextList来收集客户端设置的预编译参数信息,那么它如何设置到底层数据库创建的PreparedStament对象中呢?

DataAccessControlPreparedStatement内部定义了

protected static Map<ParameterMethod, ParameterHandler> parameterHandlers = ParameterUtil.getParameterHandlerMap();

注册了所有设置预编译参数方法的参数处理器。ParameterHandler就是给底层数据库PreparedStatement实例设置预编译参数的,其接口定义如下:

public interface ParameterHandler {

    

    /**

     *

     *

     * @param stmt

     * @param args

     * @throws SQLException

     */

    void setParameter(PreparedStatement stmt, Object[] args) throws SQLException;

}

框架提供了很多接口实现,还是以设置int类型参数为例:SetIntHandler

public class SetIntHandler implements ParameterHandler {

    public void setParameter(PreparedStatement stmt, Object[] args)

            throws SQLException {

        stmt.setInt((Integer) args[0], (Integer) args[1]);

    }

}

这样就可以把DataAccessControlPreparedStatement对象收集的预编译参数信息设置到底层数据库创建的PreparedStatement实例中。

8.5.ResultSet

    DataAccessControlResultSet是对java.sql.ResultSet接口的实现,其内部大部分实现都是委托底层数据库创建的ResultSet的方法。查询显示项的过滤功能就是通过DataAccessControlResultSet来完成的,在DataAccessControlResultSet内部重写了获取查询项信息的方法。可以给过滤的查询项设置默认值,我们知道通过ResultSet接口可以获取查询项的值,如果通过getObject方法来获取查询项的值,就要涉及类型转换的问题,如果某个字段被过滤,设置在配置文件的值都是字符串类型,我们就需要设计一个可以把字符串转换成实际类型的类型转换接口。ValueConverter接口就是干这个用的,接口代码如下:

public interface ValueConverter<T> {

    T convert(String defaultValue);

     

}

框架内部定义了多个值转换接口的实现,例如:IntValueConverter、BooleanValueConverter、DateValueConverter等。

9.多种JDBC规范支持

    我们知道不同jdk版本,其JDBC定义的接口规范是不一样的,例如jdk5定义的是JDBC3的规范,jdk6定义的是JDBC4的规范,jdk7定义的是JDBC41的规范,jdk8定义的是JDBC42的规范。 TinyDAC是可以支持jdk5~jdk8所有JDBC规范。

框架内部提供maven-ant插件配置多个jdk对源代码进行编译,所有非JDBC4开头的类是用jdk5进行编译的,以JDBC4开头的类用jdk6进行编译的,以JDBC42开头的类用jdk8进行编译的。

    好了,关于数据权限框架设计思路大致介绍到此,如果有开发人员对本框架感兴趣,想做一些扩展开发,可以联系本人。

如果您对我的博客感兴趣,请点击左上角的关注,以便及时收到我的相关通知。

 

© 著作权归作者所有

共有 人打赏支持
悠悠然然

悠悠然然

粉丝 2365
博文 173
码字总数 360373
作品 14
杭州
架构师
加载中

评论(51)

低调的前行

引用来自“悠悠然然”的评论

引用来自“hexinzhe”的评论

哇噻,感觉已经很全面了,跳了三个地方才把这三篇文章看全。已经打算试试了结果没开源。。。
已经两年了看来没戏了
马上就要开源了,tiny 4.0会一大波开源出来的。

谢谢悠然大大,期待……
悠悠然然
悠悠然然

引用来自“hexinzhe”的评论

哇噻,感觉已经很全面了,跳了三个地方才把这三篇文章看全。已经打算试试了结果没开源。。。
已经两年了看来没戏了

引用来自“低调的前行”的评论

而且作者评论里的网站都无法访问了。
原有的链接删除了
悠悠然然
悠悠然然

引用来自“hexinzhe”的评论

哇噻,感觉已经很全面了,跳了三个地方才把这三篇文章看全。已经打算试试了结果没开源。。。
已经两年了看来没戏了
马上就要开源了,tiny 4.0会一大波开源出来的。
悠悠然然
悠悠然然

引用来自“hexinzhe”的评论

哇噻,感觉已经很全面了,跳了三个地方才把这三篇文章看全。已经打算试试了结果没开源。。。
已经两年了看来没戏了

引用来自“低调的前行”的评论

而且作者评论里的网站都无法访问了。
http://www.tinygroup.org/docs/697473216636870322
低调的前行

引用来自“hexinzhe”的评论

哇噻,感觉已经很全面了,跳了三个地方才把这三篇文章看全。已经打算试试了结果没开源。。。
已经两年了看来没戏了
而且作者评论里的网站都无法访问了。
hexinzhe
hexinzhe
哇噻,感觉已经很全面了,跳了三个地方才把这三篇文章看全。已经打算试试了结果没开源。。。
已经两年了看来没戏了
悠悠然然
悠悠然然

引用来自“68号小喇叭”的评论

请问什么时候能开源,最近有这个需求,不想去硬改sql或代码,由于使用的mybatis,想通过拦截器重新组装sql实现,但是需要处理的场景(如连接查询、嵌套查询等)还是比较多的,如果能有开源的框架,就太棒了,而且看给出来的设计方案,支持的需求场景非常全面,不知道对于性能是否有影响,还有配置量是不是很大,多谢

回复@68号小喇叭 : 嗯嗯,确实是有个好框架可以比较好的解决问题最好,不过还没有开源呢。
68号小喇叭
68号小喇叭
请问什么时候能开源,最近有这个需求,不想去硬改sql或代码,由于使用的mybatis,想通过拦截器重新组装sql实现,但是需要处理的场景(如连接查询、嵌套查询等)还是比较多的,如果能有开源的框架,就太棒了,而且看给出来的设计方案,支持的需求场景非常全面,不知道对于性能是否有影响,还有配置量是不是很大,多谢
悠悠然然
悠悠然然

引用来自“风之子ZONDA”的评论

老大,能不能尽快开源啊,正好最近有这样的需求,很想参考下老大的思路
开源是个系统工程,不是简单开就开了的,有一系列准备工作要做。
悠悠然然
悠悠然然

引用来自“kleen”的评论

没有后续了吗? 请问产品出来了吗
http://tinygroup.org/docs/4661819250760192397
产品早就出来了,已经第二版了。
RMI:Java中的分布式计算框架

RMI全称是Remote Method Invocation-远程方法调用,Java RMI在JDK1.1中实现的,其威力就体现在它强大的开发分布式网络应用的能力上,是纯Java的网络分布式应用系统的核心解决方案之一。其实...

qq_39521554 ⋅ 05/15 ⋅ 0

面试必看!2018年4月份阿里最新的java程序员面试题目

目录 技术一面(23问) 技术二面(3大块) 性能优化(21点) 项目实战(34块) JAVA方向技术考察点(15点) JAVA开发技术面试中可能问到的问题(17问) 阿里技术面试1 1.Java IO流的层次结构...

美的让人心动 ⋅ 04/16 ⋅ 0

集成 Proxy 与 DB Mesh,Sharding-JDBC 3 将"Sharding"做到极致

嘉宾:张亮 作者:雨多田光 提起数据库中间件,我们可以很自然地联想到 OneProxy、TDSQL、Sharding-JDBC 与 MyCat 等知名项目。在众多的数据库中间件实现技术中,通常存在两种架构模式,一种...

编辑部的故事 ⋅ 05/23 ⋅ 18

主流Java数据库连接池比较及前瞻

本文转载自微信公众号「工匠小猪猪的技术世界」 主流数据库连接池 常用的主流开源数据库连接池有C3P0、DBCP、Tomcat Jdbc Pool、BoneCP、Druid等 C3p0: 开源的JDBC连接池,实现了数据源和JND...

渣渣(Charles) ⋅ 04/30 ⋅ 0

闲谈“如何优化SSH框架的项目”

使用struts框架的好处之一就是所有action类继承一个基类,将访问控制在基类中处理.2.所有的action类都继承自baseaction,一个资源对应一个action类. 1.实现一个继承自struts的action的baseact...

thinkyoung ⋅ 2014/12/30 ⋅ 0

Android应用开发以及设计思想深度剖析(3)

特别声明:本系列文章LiAnLab.org著作权所有,转载请注明出处。作者系LiAnLab.org资深Android技术顾问吴赫老师。 我们接下来从安全性,性能,功能,可移植性的角度分别分析Android系统为应用...

21cnbao ⋅ 2012/09/14 ⋅ 0

分享几个JAVA程序员们最容易犯的错误,你中了几枪?

都说Java语言是一门简单的编程语言,基于C++演化而来,剔除了很多C++中的复杂特性,但这并不能保证Java程序员不会犯错。那么对于广大的Java程序员来说,它们最常犯的几个错误都是什么样的呢?...

启示录是真的 ⋅ 05/25 ⋅ 0

JVM 上各种 Web 框架的抽象层 - Asity

Asity Asity 是 Java 虚拟机上各种 Web 框架的抽象层,可以在 JVM 上构建 Web 框架不可知的应用程序。 Asity 是 Web 框架的一个轻量级抽象层,它被设计用来构建应用程序和框架,这些应用程序...

匿名 ⋅ 05/28 ⋅ 0

android -------- java虚拟机和Dalvik虚拟机

java虚拟机 虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。Java虚...

切切歆语 ⋅ 04/29 ⋅ 0

互联网分层架构系列之四:前后端为什么要分离

通用业务服务化之后,系统的典型后端结构如上: web-server通过RPC接口,从通用业务服务获取数据 biz-service通过RPC接口,从多个基础数据service获取数据 基础数据service通过DAO,从独立d...

wqhlmark64 ⋅ 04/12 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

HiSDP —— 高效的C++软件开发平台

目前阿里集团每天有近1000PB的数据是通过LogAgent采集的,为了让LogAgent做到资源占用节省和高效采集,背后是基于HiSDP去构建的。 缘由 当决定采用C++编程语言去开发一个软件时,紧接着所面临...

阿里云云栖社区 ⋅ 24分钟前 ⋅ 0

zookeeper-3.4.12 下载与安装教程

一、zookeeper下载地址 http://mirrors.hust.edu.cn/apache/zookeeper/ 二、启动教程 把压缩包放在指定目录下 第三: 进入 conf文件夹底下 zoo_sample.cfg 文件名改成 zoo.cfg 第四步: 进入b...

泉天下 ⋅ 26分钟前 ⋅ 0

Oracle 中文日期转换

SELECT TO_date('2011年11月11日', 'yy"年"mm"月"dd"日"') FROM DUAL; 1. Oracle无法识别中文格式,所以添加双引号。 2. 后面的格式是指字符串在转换前的格式,而不是指转换后的格式。...

江戸川 ⋅ 27分钟前 ⋅ 0

MySell:API Spring Boot

起步 类目 商品 订单

BeanHo ⋅ 30分钟前 ⋅ 0

Spring方法拦截器MethodInterceptor

参考资料 1、Spring方法拦截器MethodInterceptor 2、Sharding JDBC源码分析-JdbcMethodInvocation类的作用

哎小艾 ⋅ 33分钟前 ⋅ 0

正则表达式

元字符 元字符,又叫字符集,就是用一些特殊符号表示特定种类的字符或位置。 匹配字符 . 匹配除换行符以外的任意字符 \w 匹配字母或数字或下划线或汉字 \s 匹配任意的空白符 \d 匹配数字 匹配...

wangchen1999 ⋅ 33分钟前 ⋅ 0

数据库数据导入Elasticsearch案例分享

基于bboss持久层和bboss elasticsearch客户端实现数据库数据导入es案例分享(支持各种数据库和各种es版本) 1.案例对应的源码 https://gitee.com/bboss/bboss-elastic/blob/master/bboss-el...

bboss ⋅ 34分钟前 ⋅ 0

动手---sbt(2)

参考 https://blog.csdn.net/leishangwen/article/details/46225587 建立一个chisel_max目录,文件内容如后面所述,现在开始执行命令: joe@joe-Aspire-Z3730:/media/sdb4/download/scala$ c......

whoisliang ⋅ 40分钟前 ⋅ 0

纯js实现最简单的文件上传(后台使用MultipartFile)

<!DOCTYPE html><html><head> <meta charset="UTF-8"> <title>XMLHttpRequest上传文件</title> <script type="text/javascript"> //图片上传 var xhr......

孟飞阳 ⋅ 45分钟前 ⋅ 0

iOS宇宙大战游戏、调试工具、各种动画、AR相册、相机图片编辑等源码

iOS精选源码 日期时间选择器,swift Space Battle 宇宙大战 SpriteKit游戏源码 LLDebugTool - 便捷的IOS调试工具(新增截屏功能) 相机扫描or长按识别二维码、FMDB、键盘动态高度、定位等 动画...

sunnyaigd ⋅ 46分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部