MyBatis系列之自定义TypeHandler实现字段加密解密

news2025/1/16 16:45:52

文章目录

  • 前言
  • 一、TypeHandler接口
  • 二、实现
  • 总结


前言

今天简单介绍利用MyBatis的TypeHandler接口实现字段的加解密。

字段的加密和解密,实现方式确实有好几种。比如,在业务层实现、在数据库层面实现等等,但是这些相对来说,耦合性稍微强那么一点点,而且涉及到改动的话,改动的地方比较多;所以最好的就是统一管理这些功能。


一、TypeHandler接口

Mybatis的TypeHandler类型转换器是负责Java类jdbc类型之间的转换。

主要涉及到下面这几个类:

  • TypeHandler 类型转换器的顶层接口。
  • BaseTypeHandler 抽象类继承自TypeHandler,Mybatis中所有的类型转换器实现均继承他。
  • TypeHandlerRegistry 类型转换器注册器,负责存储类型转换器。
  • TypeAliasRegistry 类型别名转换器,用来存储类型与别名的对应关系。

其主要作用就是:

  • 可以指定我们在Java实体类所包含的自定义类型存入数据库后的类型是什么。
  • 从数据库中取出该数据后自动转换为我们自定义的Java类型。

所以来实现简单的字段加密和解密比较方便。

二、实现

  • 1、定义一个实体类包装对象表示我们要加密的数据对象。
/**
 * 编写一个实体类,凡是此实体类的数据都表示需要加解密
 */
@Data
public class Encrypt {
    private String value;
}
  • 2、实现TypeHandler<T>接口,我们这实现BaseTypeHandler<T>
@MappedJdbcTypes(JdbcType.VARCHAR) // 表示该Handler处理的java类型
@MappedTypes(Encrypt.class) // 表示处理器处理的Jdbc类型
public class EncryptTypeHandler extends BaseTypeHandler<Encrypt> {

    private static final byte[] KEYS = "12345678abcdefgh".getBytes(StandardCharsets.UTF_8);

    /**
     * 设置参数
     */
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Encrypt parameter, JdbcType jdbcType) throws SQLException {
        if (parameter == null || parameter.getValue() == null) {
            ps.setString(i, null);
            return;
        }
        AES aes = SecureUtil.aes(KEYS);
        String encrypt = aes.encryptHex(parameter.getValue());
        ps.setString(i, encrypt);
    }

    /**
     * 获取值
     */
    @Override
    public Encrypt getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return decrypt(rs.getString(columnName));
    }

    /**
     * 获取值
     */
    @Override
    public Encrypt getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return decrypt(rs.getString(columnIndex));
    }

    /**
     * 获取值
     */
    @Override
    public Encrypt getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return decrypt(cs.getString(columnIndex));
    }

    public Encrypt decrypt(String value) {
        if (null == value) {
            return null;
        }
        return new Encrypt(SecureUtil.aes(KEYS).decryptStr(value));
    }
}
  • 3、配置Handler,配置的方式有集中;如果是MyBatisPlus,直接在字段注解上面添加即可;
    这里是MyBatis,所以可以写在配置文件中,也可以写在XML映射文件中,我们这里写在配置文件中。
# 指定自定义TypeHandler的包位置
mybatis.type-handlers-package=com.mybatis.typehandler

至此,关于自定义TypeHandler的开发就此完成,直接使用即可。


另外,基于自定义TypeHandler不仅可以实现自定义加解密,还可以实现特定数据类型的转换,这里以数据库类型为varchar映射Java对象类型的List集合类型为例说明。

还是自定义TypeHandler类,以逗号[,]分割为集合如下:

/**
 * 特定类型转换TypeHandler
 */
@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(List.class)
public class ListTypeHandler extends BaseTypeHandler<List<String>> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, List<String> parameter, JdbcType jdbcType) throws SQLException {
        if (null == parameter || parameter.isEmpty()) {
            ps.setString(i, null);
            return;
        }
        ps.setString(i, String.join(",", parameter));
    }

    @Override
    public List<String> getNullableResult(ResultSet rs, String columnName) throws SQLException {
        final String value = rs.getString(columnName);
        if (StringUtils.hasText(value)) {
            return Arrays.asList(value.split(","));
        }
        return null;
    }

    @Override
    public List<String> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        final String value = rs.getString(columnIndex);
        if (StringUtils.hasText(value)) {
            return Arrays.asList(value.split(","));
        }
        return null;
    }

    @Override
    public List<String> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        final String value = cs.getString(columnIndex);
        if (StringUtils.hasText(value)) {
            return Arrays.asList(value.split(","));
        }
        return null;
    }
}
  • 4、使用
    定义数据库对象类,如下:
@Data
public class Customer {

    private Integer id;
    private Encrypt phone; // 表示要加密的字段
    private List<String> address; // 对应数据库字段address
    
}

建表语句:

CREATE TABLE `customer` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `phone` varchar(64) DEFAULT NULL COMMENT '手机号',
  `address` varchar(200) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户表';

Mapper接口:

public interface CustomerMapper {

    int addCustomer(@Param("phone") Encrypt phone, @Param("address") String address);

    Customer findCustomer(@Param("phone") Encrypt phone);

}

Mapper接口XML:

 	<insert id="addCustomer">
        insert into customer(phone,address) values (#{phone},#{address})
    </insert>

    <select id="findCustomer" resultMap="BaseResultMapper">
        select * from customer where phone = #{phone}
    </select>

Controller接口:

@RestController
public class CustomerController {

    @Autowired
    private CustomerMapper customerMapper;

    @GetMapping("/addCustomer")
    public String addCustomer(@RequestParam("phone") String phone, @RequestParam("address") String address) {
        int result = customerMapper.addCustomer(new Encrypt(phone), address);
        return "添加结果: " + result;
    }

    @GetMapping("/findCustomer")
    public Customer findCustomer(@RequestParam("phone") String phone) {
        return customerMapper.findCustomer(new Encrypt(phone));
    }
}

数据库数据:
在这里插入图片描述
电话phone字段是加密之后的,address字段是 以逗号分隔的多个地址。

API调用结果如下:
在这里插入图片描述
可以看到电话号码,已经解密;地址成为字符串数组。


总结

MyBatis给我们提供了很多扩展类,MP和MB是一样的。所以关于数据结果类型的处理,可以使用TypeHandler接口。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2256579.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C总结(C语言知识点,深化重难点)

C语言 1.使用C语言的7个步骤2.ASCII码3.提高程序可读性的机巧4.如何使用多种整形5.打印多种整形6.课移植类型&#xff1a;stdint.h和inttypes.h7.浮点数常量8.浮点值的上溢和下溢9.使用数据类型11.常量和C预处理器12.转换说明的意义12.1转换不匹配13.副作用和序列点14.数组简介…

JavaScript编写css自定义属性

一、自定义属性 是在 CSS 中定义的变量&#xff0c;以 --开头。它们可以存储颜色、尺寸、字体等任何 CSS 值&#xff0c;并且可以在整个文档中重复使用。 :root {--primary-color: #3498db;--font-size: 16px; }body {color: var(--primary-color);font-size: var(--font-siz…

【机器学习】任务十一:Keras 模块的使用

1.Keras简介 1.1 什么是Keras&#xff1f; Keras 是一个开源的深度学习框架&#xff0c;用 Python 编写&#xff0c;构建于 TensorFlow 之上。它以简单、快速和易于使用为主要设计目标&#xff0c;适合初学者和研究者。 Keras 提供了高层次的 API&#xff0c;帮助用户快速构…

02_Node.js模块化

02_Node.js模块化 知识点自测 以下代码运行的结果是多少&#xff1f; const arr [10, 20, 30] const result arr.map(val > val 1).reduce((sum, val) > sum val, 0) console.log(result) A&#xff1a;60 B&#xff1a;63 <details><summary>答案</…

故障识别 | GADF-CNN-SSA-XGBoost数据分类预测/故障识别(Matlab)

故障识别 | GADF-CNN-SSA-XGBoost数据分类预测/故障识别&#xff08;Matlab&#xff09; 目录 故障识别 | GADF-CNN-SSA-XGBoost数据分类预测/故障识别&#xff08;Matlab&#xff09;分类效果基本描述程序设计参考资料 分类效果 基本描述 格拉姆角场差&#xff08;GADF&#…

OPenCV 图片局部放大

m_image cv::imread("C:/Code/JPG/1.jpg");if (m_image.empty()) return;cv::imshow("原始图像", m_image); // TODO: 在此添加控件通知处理程序代码int width m_image.cols;int height m_image.rows;// 确定要放大的区域&#xff08;这里是图像中心部分…

【C++】变长参数

文章目录 1. 定义&#xff1a;2. C代码示例:2.1 实现方式一&#xff1a;2.2 实现方式二&#xff1a;2.3 实现方式三&#xff1a; 3. 总结3.1 使用...语法&#xff1a;3.2 使用 std::initializer_list3.3 使用变长模板参数&#xff08;Variadic Templates&#xff09; 1. 定义&a…

内网穿透 natapp安装与使用

前言 NATAPP是一款基于ngrok的内网穿透工具。以下是对NATAPP的详细概述&#xff1a; 基本概念 定义&#xff1a;内网穿透&#xff08;NAT穿透&#xff09;是一种技术&#xff0c;它允许具有特定源IP地址和端口号的数据包能够绕过NAT设备&#xff0c;从而被正确地路由到内网主机…

TPAMI 2023:When Object Detection Meets Knowledge Distillation: A Survey

摘要 目标检测&#xff08;Object Detection&#xff0c;OD&#xff09;是计算机视觉中的一项关键任务&#xff0c;多年来涌现出了众多算法和模型。尽管当前 OD 模型的性能有所提升&#xff0c;但它们也变得更加复杂&#xff0c;由于参数规模庞大&#xff0c;在工业应用中并不…

怎么样能使Ubuntu的文件浏览器显示当前目录的路径,而不是只显示一个文件名?

默认情况下Ubuntu的文件浏览器是只显示当前目录的目录名的&#xff0c;这很不便我们查看路径或直接利用路径进行定位&#xff0c;那么怎么样能使Ubuntu的文件浏览器显示当前目录的路径呢&#xff1f; 两种方法&#xff1a; 第1种-临时方法 按下快捷键 Ctrl L&#xff0c;导航…

Uniapp Android SpringBoot3 对接支付宝支付(最新教程附源码)

Uniapp Android SpringBoot3 对接支付宝支付&#xff08;最新教程附源码&#xff09; 1、效果展示2、后端实现2.1 引入支付宝SDK依赖 pom.xml2.2 配置 application.yml2.3 支付宝相关代码2.3.1 AlipayConfig.java2.3.2 ZfbPayConfig.java2.3.3 支付接口2.3.4 支付回调处理接口&…

阿里云ECS服务器域名解析

阿里云ECS服务器域名解析&#xff0c;以前添加两条A记录类型&#xff0c;主机记录分别为www和&#xff0c;这2条记录都解析到服务器IP地址。 1.进入阿里云域名控制台&#xff0c;找到域名 ->“解析设置”->“添加记录” 2.添加一条记录类型为A,主机记录为www&#xff0c…

【MySQL】mysql服务器架构

目录 1、背景2、mysql服务器架构解释3、总结 1、背景 简单理解一下mysql的服务器架构。 2、mysql服务器架构解释 mysql的架构图如下&#xff1a; 主要分为三部分&#xff1a;客户端、服务端、存储引擎。接下来我们来解释一下各个部分&#xff1a; 客户端 用来连接mysql服务…

BERT模型的输出格式探究以及提取出BERT 模型的CLS表示,last_hidden_state[:, 0, :]用于提取每个句子的CLS向量表示

说在前面 最近使用自己的数据集对bert-base-uncased进行了二次预训练&#xff0c;只使用了MLM任务&#xff0c;发现在加载训练好的模型进行输出CLS表示用于下游任务时&#xff0c;同一个句子的输出CLS表示都不一样&#xff0c;并且控制台输出以下警告信息。说是没有这些权重。…

基于LSTM的A股股票价格预测系统(torch) :从数据获取到模型训练的完整实现

1. 项目简介 本文介绍了一个使用LSTM&#xff08;长短期记忆网络&#xff09;进行股票价格预测的完整系统。该系统使用Python实现&#xff0c;集成了数据获取、预处理、模型训练和预测等功能。 这个代码使用的是 LSTM (Long Short-Term Memory) 模型&#xff0c;这是一种特殊的…

【ArcGIS微课1000例】0134:ArcGIS Earth实现二维建筑物的三维完美显示

文章目录 一、加载数据二、三维显示三、三维符号化一、加载数据 加载配套实验数据(0134.rar中的建筑物,2d或3d都可以),方法如下:点击添加按钮。 点击【Add Files】,在弹出的Open对话框中,选择建筑物,点击确定,完成添加。 默认二维显示: 二、三维显示 右键建筑物图层…

汽车EEA架构:发展历程

1.发展历程的基本逻辑 汽车电子电气的发展历程中&#xff0c;其使用的基本逻辑是IPO(Input-Processing-Output)模型&#xff0c;如下图1所示&#xff1a; 图 1 那什么是IPO模型了&#xff1f;我们从控制器的原理入手解释IPO模型,控制器的主要用途如下&#xff1a; 根据给定的逻…

python拆分Excel文件

按Sheet拆分Excel 或 按照某一列的不同值拆分Excel。文档样式如下&#xff1a; 结果&#xff1a;红色是按照Sheet名拆出的&#xff0c;蓝色和橙色是某个Sheet按照某列的不同值拆分的。 代码&#xff1a; # -*- coding: utf-8 -*- """ 拆分excel文件——按照…

存内架构IR-DROP问题详解-电容电导补偿

一、总述 电容、电导补偿作为大规模数字电路的关键设计理念&#xff0c;是 CIM 架构优化的核心技术。在 CIM 中&#xff0c;平衡电容或电导并实现计算的精准映射&#xff0c;对能效提升和计算精度保障具有关键作用。本文基于近期文献探讨电容、电导补偿在 CIM 中的具体补偿策…

汽车网络安全 -- IDPS如何帮助OEM保证车辆全生命周期的信息安全

目录 1.强标的另一层解读 2.什么是IDPS 2.1 IDPS技术要点 2.2 车辆IDPS系统示例 3.车辆纵深防御架构 4.小结 1.强标的另一层解读 在最近发布的国家汽车安全强标《GB 44495》,在7.2节明确提出了12条关于通信安全的要求,分别涉及到车辆与车辆制造商云平台通信、车辆与车辆…