# mybatis-data-security **Repository Path**: JustryDeng/mybatis-data-security ## Basic Information - **Project Name**: mybatis-data-security - **Description**: 基于mybatis插件实现数据库数据自动加解密 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 10 - **Forks**: 11 - **Created**: 2021-07-18 - **Last Updated**: 2026-09-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: 工具 ## README # mybatis-data-security #### 介绍   mybatis-data-security 提供了一种基于注解实现数据库数据加解密的功能支持。 #### 原理   编写 mybatis interceptor 实现,对业务代码无侵入,在入库前加密,出库前解密。 #### 前置条件 - 项目采用 mybatis 作为持久层框架。 #### 功能特性 - 项目启动时分析并校验加解密信息,若使用不当则快速失败。 - 支持直接对 String 类型的变量进行加解密。 - 支持对 POJO 中 String 类型的字段进行加解密(含**任意(非表实体)类**的字段)。 - 支持自定义加解密实现。 #### 注意事项   加密后的密文 / 解密后的明文默认会回写给原对象;若希望加解密操作原对象的 clone 对象,可实现 `com.ideaaedi.mybatis.data.security.support.PojoCloneable` 接口。 ##### 版本选择 | 版本 | JDK | Spring Boot | javax / jakarta | 持久层 | 说明 | |--------------------| --- | --- | --- | --- | --- | | **2.0.1** | 17 | 3.2.4 | jakarta | mybatis(3.5.19) | 推荐:Spring Boot 3.x + mybatis | | **2.0.1-mp3.5.14** | 17 | 3.2.4 | jakarta | mybatis-plus(3.5.14) | 推荐:Spring Boot 3.x + mybatis-plus | | 1.4.3 | 8 | 2.5.2 | javax | mybatis(3.5.7) | 历史版本:Spring Boot 2.x + mybatis | | 1.4.3-mp3.5.1 | 8 | 2.5.2 | javax | mybatis-plus(3.5.1) | 历史版本:Spring Boot 2.x + mybatis-plus | #### 使用说明 1. 引入依赖: ```xml com.idea-aedi mybatis-data-security {你的版本} ``` 2. 启用功能:使用 `com.ideaaedi.mybatis.data.security.annotation.EnableMybatisDataSecurity`。 ```java @SpringBootApplication @EnableMybatisDataSecurity public class YourApplication { // ... } ``` 3. 定制加解密实现 `com.ideaaedi.mybatis.data.security.support.DbCipherExecutor`: ```java @Component public class MyDbCipherExecutor implements DbCipherExecutor { @Override public String encryptParameter(@NonNull String paramName, @NonNull String paramValue, @NonNull DbCipher annotation) { return AesUtil.encrypt(pv); } @Override public String encryptField(@NonNull String fieldName, @NonNull String fieldValue, DbCipher annotation, @NonNull Object pojo) { return AesUtil.encrypt(fv); } @Override public String decryptField(@NonNull String fieldName, @NonNull String fieldValue, @NonNull DbCipher annotation, @NonNull Object pojo) { return AesUtil.decrypt(fv); } } ``` 4. 使用 `com.ideaaedi.mybatis.data.security.annotation.DbCipher` 标注要加解密的字段/参数: ```java @DbCipher private String name; // POJO 字段加解密 // mapper 方法参数: int insertOne(@Param("name") @DbCipher String name); ``` #### SPI扩展点 框架提供若干**可定制扩展点(SPI)**,使用方只需在 Spring 容器注册相应实现(实现接口 + 标注 `@Component`)即可按需定制;未注册时使用默认实现,不影响原有行为。 ##### 1. GenericElementSupport —— 自定义泛型元素支持 框架默认识别 `Map / Collection / 数组` 三类泛型容器;对业务自定义的泛型包装(如 MyBatis-Plus 的 `IPage`),需注册本接口,框架即可**解析泛型**(按 `elementGenericIndex()` 定位元素 bean)并**定位元素**(运行时取出/写回元素、逐条加解密)。 (`IPage` 仅在 mp 分支可用;基础分支请注册自定义非 MP 容器,此处以 `IPage` 为例演示接口语义) ```java @Component public class MybatisPlusPageGenericElementSupport implements GenericElementSupport { @Override public Class containerClass() { return IPage.class; } @Override public int elementGenericIndex() { return 0; } @Override public List extractElements(IPage c) { return new ArrayList<>(c.getRecords()); } @Override public void replaceElements(IPage c, List e) { c.setRecords(e); } } ``` ##### 2. BeanTypeClassifier —— 业务bean类型判定 框架在 `TypeEnum.parseType(Class)` 中判定某类是否"业务 bean"(即 `CUSTOM_BEAN`,需扫描 `@DbCipher` 字段)。默认按 `java.*` 前缀划分。业务可注册本接口自定义。 ```java @Component public class CustomBeanTypeClassifier implements BeanTypeClassifier { @Override public boolean isCustomBean(Class clazz) { String className = clazz.getName(); return !className.startsWith("java") && !className.startsWith("com.baomidou"); } } ``` > 注意:本 SPI 只控制"系统 bean vs 业务 bean"这一层;基础类型/包装/String/Map/Collection/数组 等结构化判定仍固定,不可覆盖。 ##### 3. UnresolvableTypeSkipDecider —— 不可解析泛型判定器 当框架解析 mapper 方法参数/返回值的泛型元素、无法定位到具体业务实体时,默认快速失败;业务可注册本接口让这类类型"跳过"(如 `Collection` 主键 ID 集合)。 ```java @Component public class CustomSkipDecider implements UnresolvableTypeSkipDecider { @Override public boolean shouldSkipOnUnresolvable(Type unresolvedType, Method method) { Type resolved = resolveUpperBound(unresolvedType); if (resolved instanceof Class clazz) { // parseType 判定:CUSTOM_BEAN=业务实体;SYSTEM_BEAN=java.* 系统/框架类型(如 Object/Serializable/Number) return TypeEnum.parseType(clazz) != TypeEnum.CUSTOM_BEAN; } // 无法确定上界(如类型变量本身、嵌套泛型等)时,不跳过,交由库的快速失败机制处理 return false; } /** * 解析"不可解析泛型"的上界(通配符取上界、类型变量取上界)。 * * @param type 库传入的、无法解析出具体业务实体类的泛型类型 * * @return 上界类型;无法解析时返回 {@link Object} */ private static Type resolveUpperBound(Type type) { if (type instanceof WildcardType wildcard) { Type[] upper = wildcard.getUpperBounds(); return upper.length > 0 ? upper[0] : Object.class; } if (type instanceof TypeVariable typeVariable) { Type[] bounds = typeVariable.getBounds(); return bounds.length > 0 ? bounds[0] : Object.class; } return type; } } ``` #### 分支差异专项说明 - **基础分支(mybatis)**:无 mybatis-plus 特化;`@DbCipher`、SPI、任意(非表实体)类字段加解密均可用;若用到 `GenericElementSupport`,请注册自定义的非 MP 容器。 - **mp 分支(mybatis-plus)**:在基础能力之上,额外支持对 `BaseMapper`(增删改查、wrapper 条件)、`IPage` 分页结果等 mybatis-plus 特性的双向加解密;`GenericElementSupport` 可直接对 `IPage` 注册(见上)。`IPage` 相关示例仅在 mp 分支有效。