# 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**: 10 - **Created**: 2021-07-18 - **Last Updated**: 2024-10-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: 工具 ## README # mybatis-data-security #### 介绍   mybatis-data-security提供了一种基于注解实现数据库数据加解密的功能支持。 #### 原理   编写mybatis interceptor实现,对业务代码无侵入,在入库前加密,出库前解密。 #### 前置条件 - 项目采用mybatis作为持久层框架,支持mybatis-plus #### 功能特性 - 项目启动时分析并校验加解密信息,若使用不当则快速失败 - 支持直接对String类型的变量进行加解密 - 支持对POJO中String类型的字段进行加解密 - 支持自定义加解密实现 #### 注意事项   目前而言,加密后的密文/解密后的明文都会回写给原来的对象;但是有的时候,我们不希望加解密直接操作原来的对象,而是希望加解密时操作原对象的clone对象时,就可以通过实现`com.ideaaedi.mybatis.data.security.support.PojoCloneable`接口来达到这个目的。 注:PojoCloneable的实现类最好不要重写equals与hashcode方法, 否则,可能导致某些场景(如:两个对象的id为null,其余所有的字段值都一样时,同时批量新增这两个对象时)下,部分自增id回填失败,详见源码说明`com.ideaaedi.mybatis.data.security.support.PojoCloneable#handleNullPropertyByClonePojo`

一般的,在下述场景下比较需要这种能力: #### 使用说明 1. 引入相关依赖 mybatis项目 ```xml com.idea-aedi mybatis-data-security 1.4.3 ``` mybatis-plus项目 ```xml com.idea-aedi mybatis-data-security 1.4.3-mp3.5.1 ``` 注:适配时,是基于mybatis-plus3.5.1版本适配的,其它版本的mybatis-plus也可以用 注:适配时,只适配了常用功能的加解密 2. 使用注解`com.ideaaedi.mybatis.data.security.annotation.EnableMybatisDataSecurity`,启用mybatis-data-security功能 ```java @SpringBootApplication @EnableMybatisDataSecurity public class YourApplication { // ... } ``` 3. 实现`com.ideaaedi.mybatis.data.security.support.EncryptExecutor`,定制加解密逻辑 ```java import com.ideaaedi.mybatis.data.security.annotation.Encrypt; import com.ideaaedi.mybatis.data.security.support.EncryptExecutor; import com.ideaaedi.mybatis.data.security.util.AesUtil; import org.springframework.stereotype.Component; /** * 实现自己的加解密器 - 示例 * * @author JustryDeng * @since 2021/2/11 19:08:05 */ @Component public class MyEncryptExecutor implements EncryptExecutor { @Override public String encryptParameter(String paramName, String paramValue, Encrypt annotation) { return AesUtil.encrypt(paramValue); } @Override public String encryptField(String fieldName, String fieldValue, Encrypt annotation, Object pojo) { return AesUtil.encrypt(fieldValue); } @Override public String decryptField(String fieldName, String fieldValue, Encrypt annotation, Object pojo) { return AesUtil.decrypt(fieldValue); } } ``` 4. 使用注解`com.ideaaedi.mybatis.data.security.annotation.Encrypt`控制要加解密的字段
**`提示:`** 请着重观察下述示例中的入参出参。 - 示例一:直接对String类型的变量加密 - case 1: ```java @Insert("INSERT INTO employee (`id`,`name`) VALUES(#{id}, #{name})"); int insertThree(@Param("id")int id, @Param("name") @Encrypt String name); ``` - case 2: ```java @MapKey("id") @Select("SELECT `id`,`name`,`age`,`gender`,`motto`,`birthday`,`hobby` FROM `employee` WHERE name = #{name}"); Map selectMapByName(@Encrypt @Param("name") String name); ``` - 示例二:对POJO中String类型的字段加解密 - pojo示例: ```java @Data @Builder @NoArgsConstructor @AllArgsConstructor public class Employee { /** 用户id */ private Integer id; /** 名字 */ @Encrypt private String name; /** 年龄 */ private Integer age; /** 性别 */ private String gender; /** 座右铭 */ @Encrypt private String motto; /** 生日 */ private String birthday; /** 爱好 */ @Encrypt private String hobby; } ``` - case 1: 单POJO入参 ```java @Insert("INSERT INTO employee (`id`,`name`, `age`, `gender`, `motto`, `birthday`, `hobby`) VALUES(#{u.id},#{u.name},#{u.age},#{u.gender},#{u.motto},#{u.birthday},#{u.hobby})") @Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "u.id") int insertOne(@Param("u") Employee employee); ``` - case 2: 单POJO入参 ```java @Insert("INSERT INTO employee (`id`, `name`, `age`, `gender`, `motto`, `birthday`, `hobby`) VALUES(#{id},#{name},#{age},#{gender},#{motto},#{birthday},#{hobby})") @Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id") int insertTwo(Employee employee); ``` - case 3: POJO放在MAP中入参 ```java @Insert("INSERT INTO employee (`id`, `name`, `age`) VALUES(#{u.id},#{u.name},#{u.age})") int insertEight(Map paramsMap); ``` - case 4: POJO放在MAP中入参 ```java @Insert("INSERT INTO employee (`id`,`name`, `age`) VALUES(#{p.u.id},#{p.u.name},#{p.u.age})") int insertNine(@Param("p") Map paramsMap); ``` - case 5: POJO放在COLLECTION中入参 ```java @InsertProvider(type = AbcMapperProvider.class, method = "insertTenProvider") int insertTen(List employeeList); ``` - case 6: POJO放在COLLECTION中入参 ```java @InsertProvider(type = AbcMapperProvider.class, method = "insertElevenProvider") int insertEleven(@Param("list") List employeeList); ``` - case 7: POJO放在ARRAY中入参 ```java @InsertProvider(type = AbcMapperProvider.class, method = "insertTwelveProvider") Integer insertTwelve(@Param("myArray") Employee[] employeeArray); ``` - case 8: 单POJO出参 ```java @Select("SELECT `id`,`name`,`age`,`gender`,`motto`,`birthday`,`hobby` FROM `employee` WHERE id = #{id}") Employee selectOneById(@Param("id") Integer id); ``` - case 9: POJO放在COLLECTION中出参 ```java @Select("SELECT `id`,`name`,`age`,`gender`,`motto`,`birthday`,`hobby` FROM `employee` ") List selectAll(); ``` - case 10: POJO放在ARRAY中出参 ```java @Select("SELECT `id`,`name`,`age`,`gender`,`motto`,`birthday`,`hobby` FROM `employee` ") Employee[] selectAllAsArray(); ``` - case 11: POJO放在MAP中出参 ```java @Select("SELECT `id`,`name`,`age`,`gender`,`motto`,`birthday`,`hobby` FROM `employee` WHERE name = #{name}") Map selectMapByName(@Encrypt @Param("name") String name); ``` - case 12: ... ```java // ... ```