# field-encryptor **Repository Path**: tired-of-the-water/field-encryptor ## Basic Information - **Project Name**: field-encryptor - **Description**: 基于mybatis+jsqlparser 动态改写sql,减少重复操作,专注业务开发 0侵入自动加解密 业务差异化脱敏 sql语法自动转换(mysql自动转达梦数据库) 0侵入业务数据隔离 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 8 - **Forks**: 0 - **Created**: 2024-04-01 - **Last Updated**: 2025-08-26 ## Categories & Tags **Categories**: database-dev **Tags**: None ## README # field-encryptor使用文档 ## 技术支持 - 990319383@qq.com - bug反馈可通过邮件或者QQ的方式进行联系,QQ的话请备注来意 ## 简介 > 用于等保,秘评,信创,数据安全等场景。实现业务快速改造,减少重复性工作,使客户专注业务开发 > > 基于mybatis拦截器 + jsqlparser ,动态改写sql。 > > 此文档针对3.5.0以后的版本 > > 请使用最新版本,没有特殊情况,bug都会在最新版本修改。当前最新git版本为3.5.0,还在测试阶段,中央仓库最新版本为3.5.3-alpha (测试中) ## 功能清单 > - 数据库字段自动加解密(自动改写sql实现密文存储,明文查询) > - 使用数据库自带的加解密算法 > - 使用java可以实现的加解密算法 > - 数据库字段查询脱敏 > - sql语法自动切换 > - mysql的语法自动转换为达梦数据库语法 > - 业务数据自动隔离(执行的sql自动拼接上权限隔离的条件) > - 数据变更时维护默认值(数据新增,修改时自动填充值) ## 功能介绍 ### 1.数据库字段自动加解密 #### 应用场景 > 用于敏感数据数据库存储加密的项目改造 > > 能解决等保,秘评,数据安全等常见问题,实现项目快速改造 #### 方案对比 | | 一般方案 | 本框架 | | -------- | ------------------------------------------------------------ | -------------------------------------- | | 使用方式 | 基于拦截器,在mapper的入参和响应进行标注,实现自动加解密 | 仅需在实体类上标注哪些字段需要密文存储 | | 效率 | 改造繁琐,对业务代码有侵入 | 快捷改造,业务代码0侵入 | | 示栗 | 假设字典表的名字需要密文存储,有100个sql用到了,需要标注100次 | 仅需在字典表实体类字段上标注1次即可 | #### 模式介绍 > 框架总共有两种加密模式 - db - 特点 - **依赖数据库的加解密算法**,通过自动改造原sql,在对应字段自动调用库函数,实现字段的自动加解密 - 优点 - 加解密场景适应性强,支持列运算的字符加解密 - **支持密文模糊查询** - 缺点 - 原生数据库的加解密函数较少,但是可以通过自己扩展udf解决这个问题 - 额外增加数据库的计算开销(数据库会多几个函数运算,这个看项目场景,一般场景可忽略) - pojo - 特点 - **依赖java库的加解密算法**,通过动态修改mapper的入参响应,实现字段的自动加解密 - 优点 - 加解密算法可选择性强,java能实现的算法都支持 - 缺点 - 部分场景不兼容(详见 --> 不兼容的场景) - 一般情况下不支持模糊查询(除非选用特定的加密算法) #### 效果示栗 > 假设tb_user的phone字段是需要加密存储的,经过配置之后,所有涉及到tb_user表的数据插入会自动加密,数据读取会自动解密 ```mysql # 其中 phone 字段是密文存储的 select phone,user_name FROM tb_user WHERE phone = ? ``` - db模式 拦截器会自动把sql替换成下面的 ```sql -- select查询的会进行解密转换 where条件的会进行加密转换 SELECT CAST(AES_DECRYPT(FROM_BASE64(phone), '7uq?q8g3@q') AS CHAR) AS phone, user_name FROM tb_user WHERE phone = TO_BASE64(AES_ENCRYPT(?, '7uq?q8g3@q')) ``` - pojo模式 原sql不做变更,在拦截器这层,对入参进行加密,拿到sql结果集后,对响应进行解密 #### 不兼容场景 ##### db,pojo模式均不兼容的场景 - 项目基于jsqlparser4.9解析,其版本不支持的sql语法,此框架无法兼容 - INSERT INTO 表名 VALUES (所有字段); 表名后面没有跟具体字段的,无法兼容 ##### pojo模式不兼容的场景 - mybatis-plus service层自带的saveBatch()方法不支持自动加密 - 列运算的结果集和sql的入参响应需要做对应的 - 例如: select concat(phone,"---") as ph from tb_user; 无法将ph变量做自动的解密映射 - 同一个#{}入参,sql中对应不同的字段,想要拥有不同的值 ### 2.数据库字段查询脱敏 #### 应用场景 > 用于敏感数据脱敏展示 #### 方案对比 | | 序列化脱敏 | 本框架 | | -------- | ------------------------------------------------------------ | ----------------------------------------------------- | | 使用方式 | 对接口返回类字段进行标注 | 对sql的返回类字段进行标注 | | 适用场景 | 序列化时,仅能获取到本字段信息,获取整个响应对象困难,无法实现业务差异化脱敏的场景 | 脱敏时可以获取到整个对象,实现业务差异化脱敏 | | 示栗 | 有个单据数据需要展示物料名字,但是A业务类型很特殊,需要对物料名字进行脱敏展示,其它业务类型不需要脱敏,此方案实现此场景较为困难 | 能获取到整个响应对象,判断是否是A业务,进行差异化脱敏 | #### 效果示栗 > 场景:我现在返回了一批订单的物料详情数据,但是其中A类型订单的物料属于敏感物料,不能让app端的客户直接看到原物料名字A,需要统一脱敏展示成物料名字* ,但是其它类型的单子要求展示原名字 ``` 经过合理配置标注之后的订单列表如下,实现同一接口的list进行差异化脱敏 订单_001 订单类型A 脱敏物料名字* 订单_002 订单类型A 脱敏物料名字* 订单_003 订单类型B 原本的物料名字 ``` #### 实现原理 > 使用mybatis拦截器,将sql执行结果的结果集进行脱敏处理 ### 3.sql语法自动切换 #### 应用场景 > 信创大环境下,有些项目原本使用的mysql数据库,现在需要整个切换到达梦数据库中 > > 目前仅支持mysql自动转达梦,后续其它数据库的自动转换后续版本会规划 #### 方案对比 | | 硬改项目sql | 本框架 | | -------- | ------------------------------------------------ | ------------------------------------------------------------ | | 效率对比 | 满项目找,改动大 | 仅需引入依赖,改好配置即可 | | 覆盖率 | 一个不支持的语法,需要满项目找,不一定能全部改完 | 只要覆盖了一种语法的转换,整个项目这种语法的都会进行自动转换 | #### 效果示栗 > 开启了语法转换后,目前已经兼容的语法,全局mysql的语法会自动转换成达梦的语法,无需针对sql进行调整 ### 4.业务数据自动隔离 #### 应用场景 > 用于一般的项目开发中,需要不同的登录用户看到不同的数据范围 #### 方案对比 | | 一般方案 | 本框架 | | -------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | 使用对比 | 封装一个注解,标注在方法上或者具体的mapper上,上面指定数据隔离字段,进行隔离。或者借助mybatis-plus的DataPermissionHandler,针对不同的sql标注,进行隔离 | 仅需在实体类上标注即可 | | 项目规范 | 封装的注解,团队中可能有的人会有,有的人不用自己sql拼,不统一,后续权限改动的话,改造困难 | 仅建表的人写即可,业务开发人员大家都不自己写,也不用自己标注解,项目统一 | | 扩展性 | 一般的封装对不同场景支持较弱,有的场景需要 = 有的需要like,有的需要in,而且有的需要同一个sql不同场景下使用不同字段进行隔离 | 均支持 | #### 效果示栗 > 场景:假设表 tb_user 的数据隔离PC端是使用org_id,根据当前登录用的org_id进行隔离,app端是根据id进行数据隔离 注意:配置好后,所有涉及到tb_user查询都会默认加上数据隔离的条件(有个例想不用的见快速接入的进阶使用) ``` 假设原程序中的执行sql如下: select * from tb_user where phone = 'xxx' or name = 'yyy' 经过合理配置后(见快速使用),所有涉及到tb_user的查询,都会自动拼接上上述规则,上面的sql会变成 PC端: select * from tb_user where (phone = 'xxx' or name = 'yyy') and org_id = 登录用户组织id APP端: select * from tb_user where (phone = 'xxx' or name = 'yyy') and id = 登录用户id ``` ### 5.数据变更时维护默认值 #### 应用场景 > 一些固有字段,每次新增,或者修改时都会设置固定的值 > > 配合功能4的数据隔离,可以轻松实现项目的数据隔离管理 > > 栗如: > > ​ 创建时间,修改时间,创建人,修改人 > > ​ 数据隔离字段(组织id) 租户id #### 方案对比 | | 一般方案 | 本框架 | | -------- | ----------------------------------------------------------- | ---------------------- | | 使用方式 | 利用mybatis-plus的MetaObjectHandler,在实体类字段上标注即可 | 在实体类字段上标注即可 | | 适配场景 | 必须要求插入和修改的入参类拥有需要维护的字段才行 | 无限制 | #### 效果示栗 > 场景:有一张订单表现在突然有需求,需要在PC端按照部门进行数据隔离,所以我需要记录下每笔订单生成时的所属部门是谁 ``` 原有业务sql: insert into 订单表(字段1,字段2...) values(值1,值2...) 里面并没有新增的部门字段 增加配置之后,所有涉及到订单表的新增语句都会自动加上部门字段 实际执行sql: insert into 订单表(部门字段,字段1,字段2...) values(登录用户部门,值1,值2...) ``` ## 快速接入 ### 引入依赖 - 一般情况,仅需引入下面依赖即可 ```xml io.gitee.tired-of-the-water encryptor-core 对应版本 ``` - 当实体类模块没有mybatis依赖时,可以在实体类模块单独引入注解模块,用于使用框架注解 ```xml io.gitee.tired-of-the-water encryptor-annos 对应版本 ``` ### 1.数据库字段自动加解密 #### 快速使用 > 进行了“基本配置” 和“字段标注” 即可使用 ##### 基本配置 ``` #配置@TableName标注的实体类路径 field.scanEntityPackage[0]=com.sangsang.*.entity #确定想要使用的模式是哪种,目前支持 db pojo 两种模式配置 field.encryptor.patternType=db #自定义秘钥(这里配置秘钥后,默认的加密算法会使用这个秘钥) field.encryptor.secretKey=TIREDTHEWATER ``` ##### 字段标注 > 在标注了@TableName的实体类中,找到自己需要密文存储的字段使用@FieldEncryptor标注 ```java @Data @TableName(value = "tb_user") public class UserEntity extends BaseEntity { @TableField(value = "phone") @FieldEncryptor //标识这个字段是加密的字段 private String phone; } ``` #### 进阶使用 > 根据项目实际情况,可以使用下面的进阶的一些配置 ##### 自定义加密算法 - db模式 > 注意1: 所有引入的jsqlparser的包是com.shade.net.sf.jsqlparser 开头的,使用本框架打的包,不要使用自己引的net.sf.jsqlparser包下的 > > 注意2:如果当前项目想要配置多种算法的话,需要在默认的算法的类上面标注@DefaultStrategy ```java package xxx.strategy; import com.sangsang.config.properties.FieldProperties; import com.sangsang.domain.annos.DefaultStrategy; import com.sangsang.domain.strategy.encryptor.FieldEncryptorStrategy; import com.shade.net.sf.jsqlparser.expression.Expression; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * @author liutangqi * @date 2025/7/4 14:02 */ @Component @DefaultStrategy public class DBFieldEncryptorStrategrDefault implements FieldEncryptorStrategy { @Autowired private FieldProperties fieldProperties; // 关于如何使用jsqlparser 拼凑出数据库的加解密函数 //参考com.sangsang.encryptor.db.DefaultDBFieldEncryptorPattern @Override public Expression encryption(Expression oldExpression) { //todo 加密的逻辑 秘钥最好使用fieldProperties配置的秘钥 return null; } @Override public Expression decryption(Expression oldExpression) { //todo 解密的逻辑 秘钥最好使用fieldProperties配置的秘钥 return null; } } ``` - pojo模式 ```java package xxx.strategy; import com.sangsang.config.properties.FieldProperties; import com.sangsang.domain.strategy.encryptor.FieldEncryptorStrategy; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * @author liutangqi * @date 2025/7/8 11:17 */ @Component public class PojoFieldEncryptorStrategy implements FieldEncryptorStrategy { @Autowired private FieldProperties fieldProperties; @Override public String encryption(String s) { //todo 加密的逻辑 秘钥最好使用fieldProperties配置的秘钥 return null; } @Override public String decryption(String s) { //todo 解密的逻辑 秘钥最好使用fieldProperties配置的秘钥 return null; } } ``` ##### 项目同时存在多种加密算法 参考上面的“自定义加密算法”,项目中实现多种算法,并放在spring容器中 - 当项目spring容器中只有一个算法,则这个算法就是默认算法 - 当spring容器中存在多个算法,需要使用@DefaultStrategy标注在算法类上作为默认算法 - 当字段使用默认算法时@FieldEncryptor 后面不用指定,使用其它算法时,需要这个注解后面指定算法类 ##### 分库分表项目集成 - @FieldEncryptor 字段标注是记录了哪些表的哪些字段需要进行密文存储,当不进行任何配置时,需要将每张分表都创建好实体类,并在上面的每个字段都标注好 - 简化配置 - 栗子:"stat_vehicle_alarm"这张表是分表前的表名。 - 实际数据库中不存在这张表 - 数据库中有的表名是"stat_vehicle_alarm_0101","stat_vehicle_alarm_0102"等根据天进行了分表的 - 实现ShardingTableStrategy接口,在里面配置根据原表名获取到分表的表名的规则 - 在原表的实体类上标注@ShardingTableEncryptor(value = ShardingTableByDay.class) ```java //原表实体类 @ShardingTableEncryptor(value = ShardingTableByDay.class) @TableName("stat_vehicle_alarm") public class StatVehicleAlarmEntity { //xxx字段 } //分表表名规则 public class ShardingTableByDay implements ShardingTableStrategy { private static final LocalDate BASE_DATE = LocalDate.parse("2000-01-01"); private static final DateTimeFormatter DATE_FMT = DatePattern.createFormatter("MMdd"); private static final List SUFFIX; static { SUFFIX = new ArrayList<>(); for (int i = 0; i < 366; i++) { SUFFIX.add(LocalDateTimeUtil.format(BASE_DATE.plusDays(i), DATE_FMT)); } } @Override public List getShardingTableName(String prefix) { return SUFFIX.stream().map(suffix -> prefix + suffix).collect(Collectors.toList()); } } ``` ##### 配置sql解析LRU缓存容量(默认值是100) ​ 所有sql都会过一遍jsqlparser解析,大sql这部分操作比较耗时(大约15到40ms),所以这部分有层LRU缓存 ``` #sql解析LRU缓存容量(默认值100) field.lruCapacity=100 ``` ##### pojo模式强制指定响应解密 > 当pojo模式遇到不兼容的场景,但是还是想要在查询结果进行解密处理时 ``` # 在sql的响应类的字段上面标注这个注解 # 注意:这里是响应类,不是实体类!!! @PoJoResultEncryptor ``` ### 2.数据库自动查询脱敏 #### 快速使用 > 完成 基本配置 自定义脱敏策略 和字段标注即可开始使用 ##### 基本配置 ``` #开启脱敏支持 field.desensitize.enable=true ``` ##### 自定义脱敏策略 ```java import com.sangsang.domain.strategy.desensitize.DesensitizeStrategy; /** * @author liutangqi * @date 2025/7/8 13:45 */ public class TestDesensitizeStrategy implements DesensitizeStrategy { //注意: s可能为null,自己根据业务进行不同处理 @Override public String desensitize(String s, Object o) { //s 是待脱敏的字符串 //o 是一整个返回对象,可以根据o来做一些业务差异化脱敏 return "脱敏后:" + s; } } ``` ##### 字段标注 > 注意:这个是标注在sql的返回对象上,不是实体类,区别于其它功能 - 响应是实体类:在具体字段上面标注@FieldDesensitize 并指定算法 ``` public class UserVo{ private Long id; /** * 用户名 */ @FieldDesensitize(TestDesensitizeStrategy.class) private String userName; } ``` - 响应是String:在mapper上标注@MapperDesensitize 并指定算法 ``` @MapperDesensitize(@FieldDesensitize(TestDesensitizeStrategy.class)) List getListResult(String name); ``` - 响应是Map:在mapper上标注@MapperDesensitize 并指定算法,字段名 - 注意:fieldName指定的是sql中结果集的变量名(as 后面是什么就是什么,没有as 的话就是表达式或字段名) ```java @MapperDesensitize({@FieldDesensitize(value = TestDesensitizeStrategy.class, fieldName = "user_name"), @FieldDesensitize(value = TestDesensitizeStrategy.class, fieldName = "xxx")}) Map getResultMap(String name); ``` ### 3.sql语法转换 #### 快速使用 > 仅需基本配置完成即可完成语法的自动切换 > > 完整切换到其它数据库的话,还需要改项目的数据库驱动和连接地址 ##### 基本配置 ``` #配置@TableName标注的实体类路径 field.scanEntityPackage[0]=com.sangsang.*.entity #语法转换模式指定,目前仅支持mysql转达梦 field.transformation.patternType=mysql2dm ``` #### 进阶使用 ##### 配置sql解析LRU缓存容量(默认值是100) ​ 所有sql都会过一遍jsqlparser解析,大sql这部分操作比较耗时(大约15到40ms),所以这部分有层LRU缓存 ``` #sql解析LRU缓存容量(默认值100) field.lruCapacity=100 ``` ### 4.业务数据自动隔离 #### 快速使用 > 完成 基本配置 自定义数据隔离策略 标注 即可快速使用 ##### 基本配置 ``` #配置@TableName标注的实体类路径 field.scanEntityPackage[0]=com.sangsang.*.entity #开启数据隔离 field.isolation.enable=true ``` ##### 自定义数据隔离策略 > 注意1:和加解密类似,整个spring容器中如果只有一个DataIsolationStrategy子类,则这个就是默认策略 > > 注意2:项目spring容器中存在多个DataIsolationStrategy子类,@DefaultStrategy标注那个是默认策略 ```java package xxx.strategy; import com.sangsang.domain.enums.IsolationRelationEnum; import com.sangsang.domain.strategy.isolation.DataIsolationStrategy; import org.springframework.stereotype.Component; /** * @author liutangqi * @date 2025/7/4 17:12 */ @Component public class TIsolationBeanStrategy implements DataIsolationStrategy { //返回需要进行数据隔离的表字段名字 //这里入参的tableName可以获取到当前是哪张表,一般是表名小写 //一般项目会将登录用户存threadlocal中,这里可以取出来,根据不同的登录用户选择不同的字段隔离 @Override public String getIsolationField(String tableName) { return "org_id"; } //目前支持 "=" "in" "like 'xxx%'" 三种模式 @Override public IsolationRelationEnum getIsolationRelation(String tableName) { return IsolationRelationEnum.EQUALS; } //从当前登录用户的theadlocal中获取到用于数据隔离的字段即可,比如当前登录用户组织id之类的 @Override public Long getIsolationData(String tableName) { return 777777777L; } } ``` ##### 标注 > 标注在实体类上,一个实体类表示一种抽象数据,一种数据的权限所属一般情况是固定的 > > 一个@DataIsolation 可以同时指定多个策略,并且可以指定多个策略之间是and 还是 or ```java /** * @author liutangqi * @date 2025/7/2 14:45 */ @TableName("tb_order") @Data //使用上面的默认策略,根据org_id进行数据隔离,只能看到同组织的单据 @DataIsolation public class OrderEntity extends BaseEntity { @TableField("org_id") private Long orgId; } ``` #### 进阶使用 ##### 禁用数据隔离 > 当某些场景下,不想系统默认加上数据隔离,就想看到全部数据时 > > 使用@ForbidIsolation进行标注 - 单个sql不想要数据隔离,标注在mapper上 ``` @ForbidIsolation List getPage(); ``` - 整个service的方法都不想要数据隔离,标注在service上 ``` @ForbidIsolation public void test(){ //各种sql 各种业务都不想要数据隔离 } ``` ##### 配置sql解析LRU缓存容量(默认值是100) ​ 所有sql都会过一遍jsqlparser解析,大sql这部分操作比较耗时(大约15到40ms),所以这部分有层LRU缓存 ``` #sql解析LRU缓存容量(默认值100) field.lruCapacity=100 ``` ##### 配置多个数据隔离策略之间的关系是属于and还是or - 一个sql中多个表均有数据隔离条件,在全局配置中配置 ``` #一个sql中多个表均有数据隔离条件,这里配置不同表的隔离之间的关系,默认是 and field.isolation.conditionalRelation=or ``` - 一个sql中同一张表有多个数据隔离条件,在这个表的@DataIsolation中使用conditionalRelation进行配置 ``` #一个sql中同一张表有多个数据隔离条件,这里配置不同策略之间的关系,默认是 and @TableName("sys_user") @DataIsolation(conditionalRelation = IsolationConditionalRelationEnum.OR, value = {策略1.class, 策略2.class}) public class SysUserEntity { ... 略 } ``` ##### 复杂场景的业务隔离 - 模拟系统简介 - PC端系统中存在 一级组织 --> 二级组织---> 三级组织 - 每一级只能看到自己和下级的单据 - 此时通过org_seq进行隔离 - H5端或者App端 有司机 - 每个司机登录只能看到自己的单据 - 系统简单设计 - 权限系统 - org_seq 这个字段存储 上级组织路径-自己组织id - 一级组织: 一级组织id - 二级组织: 一级组织id-二级组织id - 三级组织: 一级组织id-二级组织id-三级组织id - 登录系统 - 用户登录,将当前用户的类型(是司机还是一,二,三级组织)存到threadlocal - 是一二三级组织的话,存一份org_seq 司机的话存一份司机id - 自定义策略实现 ``` @Component public class TIsolationBeanStrategy implements DataIsolationStrategy { @Override public String getIsolationField(String tableName) { //从threadLocal中获取用户类型,是司机的话就返回 司机id字段(driver_id)否则返回 org_seq return null; } //目前支持 "=" "in" "like 'xxx%'" 三种模式 @Override public IsolationRelationEnum getIsolationRelation(String tableName) { //根据当前登录用户类型,司机的话就是 "=" 组织的话就 "like 'xxx%'" return null; } @Override public String getIsolationData(String tableName) { //根据当前登录用户类型,返回登录用户的driverId或者orgSeq即可 return null; } } ``` ### 5.数据变更时维护默认值 #### 快速使用 ##### 基本配置 ``` #配置@TableName标注的实体类路径 field.scanEntityPackage[0]=com.sangsang.*.entity #开启数据变更时设置默认值 field.fieldDefault.enable=true ``` ##### 自定义默认值策略 ``` /** * 创建时间默认值 * * @author liutangqi * @date 2025/7/24 14:20 * @Param **/ public class CreateTimeStrategy implements FieldDefaultStrategy { @Override public boolean whetherToHandle(SqlCommandEnum sqlCommandEnum) { //新增时设置默认值 return SqlCommandEnum.INSERT.equals(sqlCommandEnum); } @Override public LocalDateTime getDefaultValue() { return LocalDateTime.now(); } } ``` ##### 标注 ``` /** * *这个是所有实体类的基类,也可以直接标注在实体类(@TableName)上面 */ public class BasePo { @TableId(type = IdType.AUTO) private Long id; /** * 创建者 */ @FieldDefault(CreateTimeStrategy.class)//新增时维护默认值 private String createBy; ...略 } ``` #### 进阶使用 ##### 强制覆盖原sql的值 > 当我们需要设置的值在原业务sql中存在,我们不想要业务sql的值,想要强制用我们策略的值覆盖sql的值 ``` # 进行字段标注的时候,使用mandatoryOverride为true,设置强制覆盖原值 @FieldDefault(value = CreateTimeStrategy.class,mandatoryOverride = true) ``` ##### 配合数据隔离实现隔离字段自动写入,查询自动增加隔离条件 > 继续4.业务数据隔离---进阶使用---复杂场景的业务隔离 我们接入了数据隔离功能后,数据查询会自动增加上数据隔离的条件,但是我们数据写入仍然需要手动去维护 此时我们可以用上本功能,在对应字段上标注,自动维护org_seq的值,实现真正的全自动0侵入数据隔离 ## 常见问题Q&A ### Q1项目没有使用mybatis-plus,没有实体类这个概念,怎么快速接入 > 可以单独建立一个文件夹,利用第三方工具(比如代码生成器)将整个表的字段信息导入到指定文件夹 > > 也可以使用本项目提供的生成实体类工具(目前仅用于mysql和oracle,其它数据库尚未测试过) ``` -- 提供的专门的工具类,可以生成实体类 com.sangsang.util.EntityGenerateUtil#generateEntity(GenerateDto dto) -- 栗子 EntityGenerateUtil.generateEntity(GenerateDto.builder() .packageName("com.sangsang.es.entity")//生成实体类包名 .outputDir("D:\resource\draft\test")//生成实体类路径 .url("jdbc:mysql://127.0.0.1:3306/your_database")//数据库地址 .username(username)//数据库账号 .password(password)//数据库密码 .catalog("sjj-dts")//目录,可不传 .schemaPattern(null)//模式:Oracle一般传用户名/模式名(大写);mysql一般传null .tableNamePattern("%")//表名过滤规则,% 匹配任意多个字符 _ 匹配单个字符 null表示不限制 .build()); ``` ### Q2:项目没有使用mybatis-plus,新写的实体类,我不想把字段写全,有什么影响 > 如果使用到的功能涉及到表字段的,必须要求涉及到的字段存在,其它字段不存在会影响部分功能 - 1.数据库字段自动加解密 - db模式 - 假设A表,实体类字段仅写了一部分,且A表中涉及到需要密文存储的字段 - 此时遇到 select * 的场景,会导致字段缺失 - pojo模式 - 暂未发现影响 - 2.数据库自动查询脱敏 > 此功能和实体类标注无关 - 3.sql语法转换 - 如果涉及到语法转换的表字段没有标注全的话,涉及到对字段的语法转换将无法正常转换 - 栗子: - mysql 查询的字段是 `字段` 到达梦需要变成 "字段" - 如果实体类这个字段缺失,会导致缺失的字段没有正常转换,导致sql执行报错 - 4.业务数据自动隔离 - 隔离字段必须存在,其它字段不存在,暂未发现影响 - 5.数据变更时维护默认值 - 需要维护的字段必须存在,其它字段不存在,暂未发现影响 ### Q3:我项目已经存在了,我想利用db模式对项目进行加密,历史数据清洗有没有什么建议 > 项目提供了生成备份,回滚的脚本生成逻辑可供参考 > > 具体根据自己项目数据量权衡具体实现方式 ``` -- 再自己配置好的项目中调用这个方案 -- 注意:确保自己项目密文存储字段标注完成,并确保spring环境启动好了,再调用下面方法 com.sangsang.bak.BakSqlCreater#bakSql() -- 栗子 BakSqlCreater.bakSql("jdbc:mysql://127.0.0.1:3306/your_database",//数据库地址 username,//数据库用户名 password, //数据库密码 suffix,//备份表后缀 expansionMultiple,//原表名扩容倍数,建议值 5 "D:\resource\draft\test")//脚本输出路径 ``` ## 附录 ### 测试用例中的表结构 ```mysql CREATE TABLE `tb_user` ( `id` bigint NOT NULL AUTO_INCREMENT, `user_name` varchar(100) DEFAULT NULL COMMENT '用户名', `login_name` varchar(100) DEFAULT NULL COMMENT '登录名', `login_pwd` varchar(100) DEFAULT NULL COMMENT '登录密码', `phone` varchar(50) DEFAULT NULL COMMENT '电话号码', `role_id` bigint DEFAULT NULL COMMENT '角色id', `create_time` datetime DEFAULT NULL, `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) )COMMENT='测试表-用户'; CREATE TABLE `tb_role` ( `id` bigint NOT NULL AUTO_INCREMENT, `role_name` varchar(100) DEFAULT NULL COMMENT '角色名字', `role_desc` varchar(100) DEFAULT NULL COMMENT '角色描述', `create_time` datetime DEFAULT NULL, `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) )COMMENT='测试表-角色'; CREATE TABLE `tb_menu` ( `id` bigint NOT NULL AUTO_INCREMENT, `menu_name` varchar(100) DEFAULT NULL COMMENT '菜单名字', `path` varchar(100) DEFAULT NULL COMMENT '路径', `parent_id` bigint DEFAULT NULL COMMENT '父级id,第一级是0', `create_time` datetime DEFAULT NULL, `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) )COMMENT='测试表-菜单'; ``` ### 其它扩展 建议使用db模式,支持大部分场景,如果数据库不支持此加解密算法,可以自己扩展mysql的 udf 下面链接是使用rust扩展sm4 加解密的一个栗子 [encryptor-udf: rust扩展mysql的udf ,本项目简单扩展了sm4的加解密支持](https://gitee.com/tired-of-the-water/encryptor-udf) ### 版本迭代记录 | 版本号 | 主要内容 | 日期 | | :----: | :----------------------------------------------------------: | :----: | | 1.0 | 支持db模式的自动加解密 | 2024/5 | | 2.0.0 | 增加pojo模式,pojo同时支持多种算法共存 | 2024/9 | | 2.1.0 | 将jsqlparser打入项目的jar中,重命名避免版本冲突 | 2025/2 | | 3.0.0 | 优化项目结构,减少visitor的重复代码,
db模式兼容insert(select)密文存储不同的场景 | 2025/3 | | 3.1.0 | jsqlparser升级到4.9版本
增加脱敏功能支持 | 2025/4 | | 3.2.0 | 增加 transformation语法自动转换功能
(目前支持mysql转达梦) | 2025/6 | | 3.3.0 | 增加数据隔离支持(maven仓库未发布) | 2025/7 | | 3.5.0 | 整体配置方式重构,使用方法变化 | 2025/7 |