基于JDBC层数据权限是如何设计的
基于JDBC层数据权限是如何设计的
悠悠然然 发表于2年前
基于JDBC层数据权限是如何设计的
  • 发表于 2年前
  • 阅读 6922
  • 收藏 240
  • 点赞 16
  • 评论 45

腾讯云 新注册用户 域名抢购1元起>>>   

    前面一篇博客聊聊数据权限哪些事儿介绍了数据权限配置文件和数据权限的几个示例,这篇文章来详细介绍下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进行编译的。

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

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

 

共有 人打赏支持
悠悠然然
粉丝 2278
博文 171
码字总数 353767
作品 14
评论 (45)
smallsun512
罗总,文章太长,看的太累
悠悠然然

引用来自“smallsun512”的评论

罗总,文章太长,看的太累
下次整短些。
徐舟
mark.有时间仔细看
coconutchen
mark13
avril23
SQLParser呢?是通用的吗?
loyal
越搞越复杂
铂金蛋蛋
营养文,mark
山哥
这个支持动作真大啊,数据行列权限一直都是一个要摸头的事情。
谷粑糖
搞得这么复杂,学习曲线很高,那个配置文件真难。
德邦007
mark
navyblue
mark
悠悠然然

引用来自“谷粑糖”的评论

搞得这么复杂,学习曲线很高,那个配置文件真难。

呵呵
悠悠然然

引用来自“loyal”的评论

越搞越复杂

来个简单的?
翟志军
为什么你的代码行高这么高?我们自己写的就不会。你故意的?
悠悠然然

引用来自“翟志军”的评论

为什么你的代码行高这么高?我们自己写的就不会。你故意的?

什么叫代码行高?
翟志军

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

引用来自“翟志军”的评论

为什么你的代码行高这么高?我们自己写的就不会。你故意的?

什么叫代码行高?
就是代码块,行与行之间的高度。。这高度太高了,我看着有些累。我还以为是我们的博客的样式设置问题。
降龙罗汉

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

引用来自“翟志军”的评论

为什么你的代码行高这么高?我们自己写的就不会。你故意的?

什么叫代码行高?
一行代码,空一行,一行代码,空一行,看起来代码很松散~故意的么?
悠悠然然

引用来自“卜祥龙”的评论

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

引用来自“翟志军”的评论

为什么你的代码行高这么高?我们自己写的就不会。你故意的?

什么叫代码行高?
一行代码,空一行,一行代码,空一行,看起来代码很松散~故意的么?

肯定不是故意的,速度查程序吧13
护士的小黄瓜
sql解析是用什么解析的?正则吗?简单的正则对复杂的sql匹配不好,复杂的正则又性能低
悠悠然然
jsqlparser
×
悠悠然然
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: