🥑SpringBoot整合Mybatis、MybatisPlus对数据库字段进行加密/脱敏
一、数据加密和脱敏
数据加密和数据脱敏都是保护数据安全的方法,但是它们的目的和方法不同。
数据加密是一种将数据转换为加密形式的技术,通过使用密钥对数据进行加密,以使其对未经授权的人员不可读取。加密后的数据只能通过使用相应的密钥进行解密才能恢复其原始形式。加密技术通常用于保护机密性和防止未经授权的访问,例如在进行互联网交易或传输敏感数据时。
数据脱敏是一种对敏感数据进行变形或替换的技术,以使其在保留数据价值的同时去除敏感信息,从而保护数据隐私。脱敏技术通常用于匿名化或去标识化数据,以便在数据共享或数据分析中保护个人隐私。脱敏可以采用多种方法,如替换、屏蔽、截断、打乱等,以减少数据暴露的风险。
因此,加密通常用于保护数据的机密性,而脱敏则用于保护数据的隐私性。
源码地址:
二、MybatisPlus方式
2.1、导入依赖,加密算法使用hutool包提供的AES
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <dependencies> <!--Hutool工具包--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.3</version> </dependency> <!--Mybatis-Plus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.2</version> </dependency> <!--SpringBoot-Web开发--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--MySQL连接驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--Lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional> true </optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> |
2.2、添加数据库加解密handler,指明加解密字段类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | package com.zhixi.handlers; import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.symmetric.AES; import org.apache.ibatis.type.BaseTypeHandler; import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.MappedJdbcTypes; import org.springframework.util.StringUtils; import java.nio.charset.StandardCharsets; import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * @Description * @Author Huicong She * @Date 2022/8/5 11:36 * */ /** * @ClassName EncryptHandler * @Author zhangzhixi * @Description 数据库字段加解密handler,JdbcType.VARCHAR:表示该处理器处理的数据类型 * @Date 2023-02-26 20:01 * @Version 1.0 */ @MappedJdbcTypes(JdbcType.VARCHAR) public class EncryptHandler extends BaseTypeHandler<String> { /** * 线上运行后勿修改,会影响已加密数据解密 */ private static final byte [] KEYS = "shc987654321camp" .getBytes(StandardCharsets.UTF_8); /** * 设置参数 */ @Override public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { if (StringUtils.isEmpty(parameter)) { ps.setString(i, null ); return ; } AES aes = SecureUtil.aes(KEYS); String encrypt = aes.encryptHex(parameter); ps.setString(i, encrypt); } /** * 获取值 */ @Override public String getNullableResult(ResultSet rs, String columnName) throws SQLException { return decrypt(rs.getString(columnName)); } /** * 获取值 */ @Override public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return decrypt(rs.getString(columnIndex)); } /** * 获取值 */ @Override public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return decrypt(cs.getString(columnIndex)); } public String decrypt(String value) { if ( null == value) { return null ; } return SecureUtil.aes(KEYS).decryptStr(value); } } |
2.3、指明自定义类型包处理器路径
1 2 | #自定义类型处理器包路径 mybatis-plus.type-handlers-package=com.zhixi.handlers |
2.4、在需要加密存储的数据库表字段上添加typeHandler,并指定使用的处理器:autoResultMap = true
package com.zhixi.pojo; import com.baomidou.mybatisplus.annotation.*; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import com.zhixi.handlers.EncryptHandler; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import java.io.Serializable; import java.time.LocalDateTime; /** * @TableName user */ @TableName(value = "user",autoResultMap = true) @Data public class User implements Serializable { /** * 主键ID */ @TableId(value = "id", type = IdType.AUTO) private Long id; /** * 用户名 */ @TableField(value = "username") private String username; /** * 密码 */ @TableField(value = "password") private String password; /** * 姓名 */ @TableField(value = "name") private String name; /** * 年龄 */ @TableField(value = "age") private Integer age; /** * 邮箱 */ private String email; /** * 住址 */ @TableField(value = "address", typeHandler = EncryptHandler.class) private String address; /** * 版本号,用于乐观锁 */ @TableField(value = "version") private Integer version; /** * 创建时间 */ @TableField(value = "create_time", fill = FieldFill.INSERT) @JsonDeserialize(using = LocalDateTimeDeserializer.class) @JsonSerialize(using = LocalDateTimeSerializer.class) // 对入参进行格式化 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") // 对出参进行格式化 @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createTime; /** * 更新时间 */ @TableField(value = "update_time", fill = FieldFill.UPDATE) @JsonDeserialize(using = LocalDateTimeDeserializer.class) @JsonSerialize(using = LocalDateTimeSerializer.class) @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime updateTime; /** * 删除状态,0表示未删除,1表示已删除 */ @TableField(value = "deleted") private Integer deleted; /** * 创建时间,使用MyBatis Plus自动填充功能 */ @TableField(value = "create_at", fill = FieldFill.INSERT) private String createAt; /** * 更新时间,使用MyBatis Plus自动填充功能 */ @TableField(value = "update_at", fill = FieldFill.UPDATE) private String updateAt; @TableField(exist = false) private static final long serialVersionUID = 1L; }
2.5、测试
1、添加用户,验证住址字段是否加密
1 2 3 4 5 6 7 8 9 10 11 12 | { "username" : "zhangsan" , "password" : "123456" , "name" : "张三" , "age" : 30, "email" : "1820712542@qq.com" , "address" : "北京市丰台区" , "version" : 1, "createTime" : "2022-02-27 00:00:00" , "updateTime" : "2022-02-28 04:30:00" , "deleted" : 0 } |
数据库保存的数据:
这里说明一下,我为了演示,没有对密码字段进行加密操作,也就是没有意义
实际的业务中肯定也不会通过这种可逆的方式,密码保存在数据库中,不能通过查询/解密获取到实际的值。
2、查询用户,验证地址字段是否解密
三、通用(数据脱敏)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | E:. │ pom.xml - Maven构建工具的项目配置文件 │ 数据脱敏.iml - IntelliJ IDEA项目文件,保存了项目的设置和配置信息 │ └─src ├─main │ ├─java │ │ └─com │ │ └─zhixi │ │ │ SpringbootDataDesensitizationIntroApplication.java - 应用程序的入口类 │ │ │ │ │ ├─annotation │ │ │ JsonDesensitization.java - 自定义注解,用于标记需要进行数据脱敏的字段 │ │ │ │ │ ├─controller │ │ │ IndexController.java - Web应用程序的控制器类,处理HTTP请求和响应 │ │ │ │ │ ├─desensitizer │ │ │ │ AbstractDesensitizer.java - 数据脱敏器的抽象基类 │ │ │ │ Desensitizer.java - 数据脱敏器接口 │ │ │ │ │ │ │ └─impl │ │ │ DefaultDesensitizer.java - 默认的数据脱敏器实现类,用于处理除手机号和电子邮件以外的字段 │ │ │ EmailDesensitizer.java - 电子邮件地址数据脱敏器实现类 │ │ │ MobileDesensitizer.java - 手机号码数据脱敏器实现类 │ │ │ │ │ ├─entity │ │ │ User.java - 实体类,用于保存用户信息 │ │ │ │ │ ├─enums │ │ │ DesensitizationType.java - 枚举类,定义了不同类型的数据脱敏方式 │ │ │ │ │ ├─exception │ │ │ DesensitizationException.java - 异常类,用于在数据脱敏出现错误时抛出异常 │ │ │ │ │ └─support │ │ DesensitizerFactory.java - 工厂类,用于创建不同类型的数据脱敏器 │ │ JsonDesensitizationSerializer.java - 自定义序列化器,用于将数据脱敏后的对象转换为JSON格式 │ │ │ └─resources │ application.properties │ └─ test └─java └─com └─zhixi SpringbootDataDesensitizationIntroApplicationTests.java |
四、Mybatis方式(推荐,通用)
参考博客:https://blog.csdn.net/weixin_43655425/article/details/121394246
实现逻辑:
- 1、定义个注解作用域为类的属性上,用于标识这个属性需要加密或解密。
- 2、实现mybatis拦截器添加加解密逻辑。
- 3、加解密逻辑:判断如果是新增和更新操作拿到该注解属性的原值进行加密,如果是查询操作就拿到数据库值反向解密。
下面进行测试的是:对地址字段进行加密,对邮箱字段进行脱敏
3.1、引入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | <!--为了下载依赖--> <properties> <java.version>1.8</java.version> </properties> <repositories> <repository> <id>jitpack.io</id> <url>https: //jitpack.io</url> </repository> </repositories> <dependencies> <!--对数据库字段进行加密、脱敏--> <dependency> <groupId>com.gitee.china-zhz</groupId> <artifactId>privacy-spring-boot-starter</artifactId> <version>1.0.0.RELEASE</version> </dependency> <!--SpringBoot-web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--数据库连接驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!--Lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--SpringBoot整合Mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> |
3.2、在实体类上面加上注解
@FieldDesensitize(fillValue = "*"):脱敏
@FieldEncrypt:加密
package com.zhixi.pojo; import cn.zhz.privacy.annotation.FieldDesensitize; import cn.zhz.privacy.annotation.FieldEncrypt; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import java.io.Serializable; import java.time.LocalDateTime; /** * @TableName user */ @Data public class User implements Serializable { /** * 主键ID */ private Long id; /** * 用户名 */ private String username; /** * 密码 */ @FieldEncrypt private String password; /** * 姓名 */ private String name; /** * 年龄 */ private Integer age; /** * 邮箱 */ @FieldDesensitize(fillValue = "*") private String email; /** * 地址 */ @FieldEncrypt private String address; /** * 版本号,用于乐观锁 */ private Integer version; /** * 创建时间 */ @JsonDeserialize(using = LocalDateTimeDeserializer.class) @JsonSerialize(using = LocalDateTimeSerializer.class) // 对入参进行格式化 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") // 对出参进行格式化 @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createTime; /** * 更新时间 */ @JsonDeserialize(using = LocalDateTimeDeserializer.class) @JsonSerialize(using = LocalDateTimeSerializer.class) @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime updateTime; /** * 删除状态,0表示未删除,1表示已删除 */ private Integer deleted; /** * 创建时间,使用MyBatis Plus自动填充功能 */ private String createAt; /** * 更新时间,使用MyBatis Plus自动填充功能 */ private String updateAt; private static final long serialVersionUID = 1L; }
3.3、测试
插入数据:看地址字段是否加密
db:
查询数据:看邮箱字段展示是否脱敏、地址字段是否解密
分类:
微服务框架
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
2022-02-27 SpringBoot下定时任务