MyBatis-Plus实现部分字段存取加解密
前言
网上教程大致有两种
1.基于MyBatis-Plus自定义类型处理器(TypeHandler)的方法
2.基于MyBatis的方法(拦截器)
这里使用的第二种,为了保护隐私,这里把package路径删掉了
添加两个自定义注解
import java.lang.annotation.*;
/**
* 字段加解密注解
* 放到实体类上
*/
@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CiphertextData {
}
import java.lang.annotation.*;
/**
* 字段加解密注解
* 放到需要加解密的字段上
*/
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CiphertextField {
}
工具类
MissCipher
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
public class MissCipher {
public MissCipher() {
}
public static byte[] work(byte[] content, Key key, byte[] iv, int mode, String algorithm) {
byte[] result = null;
try {
Cipher cipher;
if (iv == null) {
if ("RSA".equals(algorithm)) {
cipher = Cipher.getInstance(algorithm);
} else {
cipher = Cipher.getInstance(algorithm + "/" + "ECB" + "/" + "PKCS5Padding");
}
cipher.init(mode, key);
} else {
cipher = Cipher.getInstance(algorithm + "/" + "CBC" + "/" + "PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(mode, key, ivParameterSpec);
}
result = cipher.doFinal(content);
} catch (Exception var8) {
var8.printStackTrace();
}
return result;
}
}
Des3Utils
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
public class Des3Utils {
private static final Encoder ENCODER = Base64.getEncoder();
private static final Decoder DECODER = Base64.getDecoder();
public Des3Utils() {
}
public static String encrypt(String content, String key) {
byte[] result = encrypt(content.getBytes(), key.getBytes());
return ENCODER.encodeToString(result);
}
public static byte[] encrypt(byte[] content, byte[] key) {
return work(content, key, 1);
}
public static String encrypt(String content, String key, String iv) {
byte[] result = encrypt(content.getBytes(), key.getBytes(), iv.getBytes());
return ENCODER.encodeToString(result);
}
public static byte[] encrypt(byte[] content, byte[] key, byte[] iv) {
return work(content, key, iv, 1);
}
public static String decrypt(String content, String key) {
byte[] result = decrypt(DECODER.decode(content), key.getBytes());
return new String(result);
}
public static byte[] decrypt(byte[] content, byte[] key) {
return work(content, key, 2);
}
public static String decrypt(String content, String key, String iv) {
byte[] result = decrypt(DECODER.decode(content), key.getBytes(), iv.getBytes());
return new String(result);
}
public static byte[] decrypt(byte[] content, byte[] key, byte[] iv) {
return work(content, key, iv, 2);
}
private static SecretKey generateKey(byte[] key) {
SecretKey secretKey = null;
try {
DESedeKeySpec deSedeKeySpec = new DESedeKeySpec(key);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("DESede");
secretKey = secretKeyFactory.generateSecret(deSedeKeySpec);
} catch (Exception var4) {
var4.printStackTrace();
}
return secretKey;
}
private static byte[] work(byte[] content, byte[] key, int mode) {
SecretKey secretKey = generateKey(key);
return MissCipher.work(content, secretKey, (byte[])null, mode, "DESede");
}
private static byte[] work(byte[] content, byte[] key, byte[] iv, int mode) {
SecretKey secretKey = generateKey(key);
return MissCipher.work(content, secretKey, iv, mode, "DESede");
}
}
加解密拦截器
加密拦截器
import com.chinaums.mqy.base.annotation.CiphertextData;
import com.chinaums.mqy.base.annotation.CiphertextField;
import com.chinaums.mqy.util.Des3Utils;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.shiro.codec.Hex;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Properties;
/**
* 加密拦截器
*/
@Component
@Intercepts({
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class ParameterInterceptor implements Interceptor {
@Resource
ResultSetInterceptor resultSetInterceptor;
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object object = invocation.getArgs()[1];
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
if (SqlCommandType.INSERT.equals(sqlCommandType)) {
// Object object = invocation.getArgs()[1];
Class<?> clazz = object.getClass();
boolean isContain = clazz.isAnnotationPresent(CiphertextData.class);
if (isContain) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
boolean isContainCiphertextField = field.isAnnotationPresent(CiphertextField.class);
if (isContainCiphertextField) {
field.setAccessible(true);
Object o = field.get(object);
if (o != null) {
String content = encrypt(String.valueOf(o));
field.set(object, content);
}
}
}
}
} else if ("UPDATE".equals(sqlCommandType.name())) {
// Object object = invocation.getArgs()[1];
Class temp = object.getClass();
boolean res = temp.isAnnotationPresent(CiphertextData.class);
if (res) {
Field[] fields = temp.getDeclaredFields();
for (Field field : fields) {
boolean isContainCiphertextField = field.isAnnotationPresent(CiphertextField.class);
if (isContainCiphertextField) {
field.setAccessible(true);
Object o = field.get(object);
if (o != null) {
String content = encrypt(String.valueOf(o));
field.set(object, content);
}
}
}
} else {
if (object instanceof MapperMethod.ParamMap) {
Map map = (Map) object;
boolean boll = map.containsKey("param1");
Object obj = null;
if (boll) {
obj = map.get("param1");
} else {
obj = map.get("et");
}
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
boolean isContainCiphertextField = field.isAnnotationPresent(CiphertextField.class);
if (isContainCiphertextField) {
field.setAccessible(true);
Object o = field.get(obj);
if (o != null) {
String content = encrypt(String.valueOf(o));
field.set(obj, content);
}
}
}
}
}
}
//执行SQL方法
Object result = invocation.proceed();
//还原实体类被加密过的字段
if (object instanceof MapperMethod.ParamMap) {
Map map = (Map) object;
if (map.containsKey("param1")) {
resultSetInterceptor.deal(map.get("param1"));
} else {
resultSetInterceptor.deal(map.get("et"));
}
} else {
resultSetInterceptor.deal(invocation.getArgs()[1]);
}
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
/**
* 加密
*
* @param content: 待加密的内容
* @return java.lang.String
**/
private String encrypt(String content) {
byte[] res = Des3Utils.encrypt(content.getBytes(), "EE+oJvs9beODbqQqGZv0GhBP".getBytes(), "00000000".getBytes());
return Hex.encodeToString(res);
}
}
解密拦截器
import com.chinaums.mqy.base.annotation.CiphertextData;
import com.chinaums.mqy.base.annotation.CiphertextField;
import com.chinaums.mqy.util.Des3Utils;
import org.apache.ibatis.executor.resultset.DefaultResultSetHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.shiro.codec.Hex;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
/**
* 解密拦截器
*/
@Component
@Intercepts({
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = Statement.class)
})
public class ResultSetInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 获取结果集的类型
DefaultResultSetHandler defaultResultSetHandler = (DefaultResultSetHandler) invocation.getTarget();
MetaObject metaObject = SystemMetaObject.forObject(defaultResultSetHandler);
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("mappedStatement");
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
Class<?> resultType = resultMaps.get(0).getType();
// 判断是否包含CiphertextData注解
boolean isContain = resultType.isAnnotationPresent(CiphertextData.class);
Object resultObject = invocation.proceed();
if (isContain && !Objects.isNull(resultObject)) {
List list = (List) resultObject;
for (Object item : list) {
this.deal(item);
}
}
return resultObject;
}
private boolean validRequest() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
return false;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
/**
* 处理结果集
*
* @param data: 待处理的数据
**/
protected void deal(Object data) throws IllegalAccessException {
Field[] fields = data.getClass().getDeclaredFields();
for (Field field : fields) {
boolean isContainCiphertextField = field.isAnnotationPresent(CiphertextField.class);
if (isContainCiphertextField) {
field.setAccessible(true);
Object o = field.get(data);
if (o != null) {
String content = decrypt(String.valueOf(o));
field.set(data, content);
}
}
}
}
/**
* 解密
*
* @param content: 密文
* @return java.lang.String
**/
private String decrypt(String content) {
String temp = null;
try {
byte[] address = Des3Utils.decrypt(Hex.decode(content), "EE+oJvs9beODbqQqGZv0GhBP".getBytes(), "00000000".getBytes());
temp = new String(address, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return content;
}
return temp;
}
}
在需要使用的实体类上加注解
大功告成
正常像平常那样使用即可,查出来的结果会自动解密,保存和更新的时候会自动加密,数据库中的是加密的
MyBatis-Plus实现部分字段存取加解密的更多相关文章
- Mybatis使用TypeHandler实现数据的加解密转换
参考: MyBatis之TypeHandler: https://www.cnblogs.com/yulinfeng/p/5991170.html 前段时间收到这么个需求:为安全起见,要求在数据库 ...
- SpringBoot+ShardingSphere彻底解决生产环境数据库字段加解密问题
前言 互联网行业公司,对于数据库的敏感字段是一定要进行加密的,方案有很多,最直接的比如写个加解密的工具类,然后在每个业务逻辑中手动处理,在稍微有点规模的项目中这种方式显然是不现实的,不仅工作量大而 ...
- 从零开始实现一个MyBatis加解密插件
作者:vivo 互联网服务器团队- Li Gang 本篇文章介绍使用MyBatis插件来实现数据库字段加解密的过程. 一.需求背景 公司出于安全合规的考虑,需要对明文存储在数据库中的部分字段进行加密, ...
- 使用Mybatis的TypeHandler加解密数据
使用Mybatis的TypeHandler加解密数据 一.背景 二.解决方案 三.需求 四.实现思路 1.编写一个实体类,凡是此实体类的数据都表示需要加解密的 2.编写一个加解密的`TypeHandl ...
- java实现工程配置文件敏感字段加解密
以下引自他人博客: 1. 需求背景我们在开发应用时,需要连接数据库,一般把数据库信息放在一个属性配置文件中,比如***.properties,具体的内容 #mysql的配置文件jdbc.url=jdb ...
- 惊呆了!不改一行 Java 代码竟然就能轻松解决敏感信息加解密|原创
前言 出于安全考虑,现需要将数据库的中敏感信息加密存储到数据库中,但是正常业务交互还是需要使用明文数据,所以查询返回我们还需要经过相应的解密才能返回给调用方. ps:日常开发中,我们要有一定的安全意识 ...
- c# Aes加解密和对象序列化
aes加解密 public class AesCryptto { private string key = "hjyf57468jhmuist"; private string i ...
- DES 算法的 C++ 与 JAVA 互相加解密
国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...
- Asp.Net Core 2.0 项目实战(7)MD5加密、AES&DES对称加解密
本文目录 1. 摘要 2. MD5加密封装 3. AES的加密.解密 4. DES加密/解密 5. 总结 1. 摘要 C#中常用的一些加密和解密方案,如:md5加密.RSA加密与解密和DES加密等, ...
- Spring对JSON请求加解密
Spring中处理JSON请求通常使用@RequestBody和@ResponseBody注解,针对JSON请求加解密和过滤字符串,Spring提供了RequestBodyAdvice和Respons ...
随机推荐
- 【Unit1】表达式化简(层次化设计)-作业总结
三次作业围绕表达式化简展开,逐次递进.主体思路为:递归下降解析表达式保存至类中,依据相关模式化简,依照规范输出字符串. 1.第一次作业 1.1 题目概述 表达式 = 项 + 项 + ... 项 = 因 ...
- Mysql join算法深入浅出
导语 联表查询在日常的数据库设计中非常的常见,但是联表查询可能会带来性能问题,为了调优.避免设计出有性能问题的SQL,在explain命令中,会显示用的是哪个join算法,学习一下join过程是非常有 ...
- (C++实现)2-NAF
(C++实现)2-NAF 前言 任何一个非负整数,都有一个唯一的 NAF (Non-adjacent form) 表示. 因着课程的缘由,我不得不研究一下 NAF 是怎么实现的,也是现学现用. ...
- celery 启动显示警告信息“...whether broker connection retries are made during startup in Celery 6.0 and above...”
博客地址:https://www.cnblogs.com/zylyehuo/ # celery作为一个单独项目运行,在settings文件中设置 broker_connection_retry_on_ ...
- Portainer安装配置
什么是portainer 官网:https://www.portainer.io/ Portainer(基于 Go) 是一个轻量级的Web管理界面,可让您轻松管理 Docker 主机 或 Swarm ...
- HTML5 给网站添加图标
1.首先将图标上传到对应的目录下 2.在网页的index.html,添加已下代码到<head>标签里 <link rel="icon" href="i_ ...
- Redis 应用场景之短信验证码
应用场景 以 OSChina 账号注册 为例...讲错了请留言批评指正... 逻辑场景 用户操作: 用户输入手机号, 然后点击获取验证码. 前端逻辑: ajax 发起请求, 参数带上手机号. 后端逻辑 ...
- $.ajax jsonp parsererror
场景重现 通过$.ajax()发起的跨越请求代码如下: $.ajax({ dataType: "JSONP", type: "GET", url: " ...
- Python科学计算系列11—几何绘图
1.显函数图像绘制 例:绘制y=sinx的图像 代码如下: from sympy import * x = symbols('x') plot(sin(x), (x, -2 * pi, 2 * pi) ...
- java程序乱码问题
1.字符编码简介 字符编码从字面上理解,就是将字符编码为由多个bits(0或1)组成的字节序列.但字符和字节序列的映射并不是直接的,可简要概括为2个步骤,第1步由字符映射到unicode码,第2步由u ...