小言_互联网的博客

Mybatis 自定义TypeHandler实现数据脱敏(加解密)

1002人阅读  评论(0)

请相信我,你一定会更优秀!

目录:

1、应用场景

2、实现方案

2-1、MySQL 自带函数 AES_ENCRYPT、AES_DECRYPT 进行加解密转换

2-2、setter、getter 方法中处理

2-3、mybatis自定义类型处理器(TypeHandler)


1、应用场景

系统中的用户信息、订单信息、地址信息等等,都应在公司数据安全红线范围之内,并且,如果公司需要申请一些安全资质,数据保密也是必须拿下的一关,如果发生数据泄露,对公司,对用户都是极大的损失。数据安全架构体系包括很多东西,如DB、日志、文件等数据转换的场景等。本文我们不打散,只说一件事:数据库中的数据脱敏。

例子:MySQL中用户表(user)手机号字段进行数据脱敏。
组件:Springboot + Mybatis + MySQL

2、实现方案

2-1、MySQL 自带函数 AES_ENCRYPT、AES_DECRYPT 进行加解密转换

加密函数:AES_ENCRYPT('待加密串', '自定义秘钥')
解密函数:AES_DECRYPT('待解密串', '自定义秘钥')

SQL示例: 

  • 插入:
insert into user(mobile) values (HEX((AES_ENCRYPT('18800000000', 'b@BRATdBcHOt1&b0'))));
  • 读取:

  
  1. select mobile from user;
  2. ---
  3. C50233E113E08B89F9AB7BD390489B4A
  • 解密读取:

  
  1. select AES_DECRYPT( UNHEX(mobile), 'b@BRATdBcHOt1&b0') from user;
  2. ---
  3. 18800000000

mybatis 使用示例:


  
  1. <insert id = "create" >
  2. insert into user(mobile) values (HEX((AES_ENCRYPT(#{mobile}, 'b@BRATdBcHOt1&b0'))));
  3. </insert>
  4. <select id = "listMobile" >
  5. select AES_DECRYPT(UNHEX(mobile), 'b@BRATdBcHOt1&b0') from user;
  6. </select>

问题:xml中会有很多秘钥串的地方,如果一处有误,将导致写读均出现问题。

如何做到秘钥的统一管理?

" mybatis全局配置 Configuration中支持全局变量 variables "

Mybatis Configuration类代码示例

> mybatis 全局配置 


  
  1. @Autowired
  2. private SqlSessionFactory sqlSessionFactory;
  3. sqlSessionFactory.getConfiguration().getVariables().put( "mobileEncryptKey", "b@BRATdBcHOt1&b0");

> mapper-xml 代码:


  
  1. <insert id = "create" >
  2. insert into user(mobile) values (HEX((AES_ENCRYPT(#{mobile}, ${mobileEncryptKey}))));
  3. </insert>
  4. <select id = "listMobile" >
  5. select AES_DECRYPT(UNHEX(mobile), ${mobileEncryptKey}) from user;
  6. </select>

方案一缺点:直接在字段上使用函数急剧拉低MySQL性能,强烈不推荐。

2-2、setter、getter 方法中处理

UserDAO接口中方法参数 UserDO的 setter、getter中做加解密逻辑处理,不影响mapper中sql的编写。

方案二缺点:

  • 在 setter、getter方法中侵入业务逻辑,不易维护,增加排查问题难度。
  • 别的业务需要脱敏时,依然需要对应的业务DO中进行同样处理。侵入性强,扩展性极低。

2-3、mybatis自定义类型处理器(TypeHandler)

" 先来看下mybatis中有哪些我们常用的熟悉的类型处理器,如图:"

> 扩展 TyepHandler 实现

第一步:自定义 JavaType,给个别名


  
  1. package com.starter.mybatis;
  2. import org.apache.ibatis.type.Alias;
  3. @Alias( "mobileSecret")
  4. public class MobileSecret {
  5. }

 第二步:自定义 JavaType的 Handler


  
  1. package com.starter.mybatis;
  2. import java.nio.charset.StandardCharsets;
  3. import java.sql.CallableStatement;
  4. import java.sql.PreparedStatement;
  5. import java.sql.ResultSet;
  6. import java.sql.SQLException;
  7. import org.apache.ibatis.type.BaseTypeHandler;
  8. import org.apache.ibatis.type.JdbcType;
  9. import org.apache.ibatis.type.MappedTypes;
  10. import org.springframework.util.Base64Utils;
  11. @MappedTypes(MobileSecret.class)
  12. public class MyEncrTypeHandler extends BaseTypeHandler<String> {
  13. @Override
  14. public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
  15. throws SQLException {
  16. try {
  17. String parameterEncode = Base64Utils.encodeToString(parameter.getBytes());
  18. ps.setString(i, parameterEncode);
  19. } catch (Exception e) {
  20. ps.setString(i, parameter);
  21. }
  22. }
  23. @Override
  24. public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
  25. String columnValue = rs.getString(columnName);
  26. try {
  27. byte[] decodeFromString = Base64Utils.decodeFromString(columnValue);
  28. columnValue = new String(decodeFromString, StandardCharsets.UTF_8);
  29. } catch (Exception e) {
  30. }
  31. return columnValue;
  32. }
  33. @Override
  34. public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
  35. return null;
  36. }
  37. @Override
  38. public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
  39. return null;
  40. }
  41. }

第三步:xml 中代码


  
  1. <resultMap id="BaseResultMap" type="com.pojo.UserDO">
  2. <result column="id" property="id" />
  3. <!--
  4. 等价于:
  5. <result column="mobile" property="mobile" typeHandler="com.starter.mybatis.MyEncrTypeHandler" />
  6. 下边这行 javaType="mobileSecret",写起来更方便一些,因为我们上边给 MobileSecret.class配置了别名,这样会去找 MobileSecret.class对应的handler是 MyEncrTypeHandler
  7. -->
  8. <result column="mobile" property="mobile" javaType="mobileSecret" />
  9. </resultMap>
  10. <!-- 新增 -->
  11. <insert id="add" useGeneratedKeys="true" keyProperty="id">
  12. insert into `user`(`mobile`) values (#{mobile, javaType=mobileSecret})
  13. </insert>
  14. <!-- 查询 -->
  15. <select id="list" resultMap="BaseResultMap">
  16. select * from user
  17. </select>

第四步、pom.xml 配置


  
  1. <dependency>
  2. <groupId>org.mybatis.spring.boot </groupId>
  3. <artifactId>mybatis-spring-boot-starter </artifactId>
  4. <version>1.1.1 </version>
  5. </dependency>

第五步、application.xml 配置


  
  1. # 配置别名、自定义TypeHandler处理器包位置
  2. mybatis.type-aliases-package=com.starter.mybatis
  3. mybatis.type-handlers-package=com.starter.mybatis

第五步、完成

> 为什么这样配置就ok了?如果在 bean加载完后再动态给 SqlSessionFactory.getConfigutation中添加别名或自定义类处理器行吗?两张图让你搞明白。

MybatisAutoConfiguration中通过SqlSessionFactoryBean创建SqlSessionFactory

 

SqlSessionFactoryBuilder.build()中解析顺序

解析 mapper-xml是在最后一步,如果在 bean加载完后再动态给 SqlSessionFactory.getConfigutation中添加别名或自定义类处理器,明显已经来不及,在解析 xml时候就会报错:找不到别名或类型处理器。

推荐使用第三种。

  努力改变自己和身边人的生活。

特别希望本文可以对您有所帮助,转载请注明出处。感谢大家留言讨论交流。 


转载:https://blog.csdn.net/weixin_35586546/article/details/105475140
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场