使用mybatis/mybatisPlus拦截器对数据库表字段加密

前言:

数据库字段在存储的时候进行加密或者解密的时候,在实体类字段上面添加上加密解密注解即可,需要用到几个工具类

使用展示

    @EncryptTransaction
    @DecryptTransaction
    private String contactPersonPhone;

注解类1

package com.xinke.ebid.common.annotations;

import java.lang.annotation.*;

/**
 * 解密注解
 *  @author CGY
 */
@Documented
@Inherited
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface DecryptTransaction {

}

注解类2

package com.xinke.ebid.common.annotations;

import java.lang.annotation.*;

/**
 * 加密注解
 *  @author CGY
 */
@Documented
@Inherited
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptTransaction {

}

工具类

package com.xinke.ebid.common.shiro;

import com.xinke.ebid.common.annotations.EncryptTransaction;

import com.xinke.ebid.common.utils.AESUtil2;
import com.xinke.ebid.common.utils.AesUtil;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.util.Objects;
import java.util.Properties;

/**
 * 用于处理请求数据字段加密
 *  @author CGY
 */
@Component
@Intercepts({ @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class), })
public class ParameterInterceptor implements Interceptor {

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		// @Signature 指定了 type= parameterHandler 后,这里的 invocation.getTarget()
		// 便是parameterHandler
		// 若指定ResultSetHandler ,这里则能强转为ResultSetHandler
		ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
		// 获取参数对像,即 mapper 中 paramsType 的实例
		Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");
		parameterField.setAccessible(true);
		// 取出实例
		Object parameterObject = parameterField.get(parameterHandler);
		if (parameterObject != null) {
			Class<?> parameterObjectClass = parameterObject.getClass();
			// 取出当前当前类所有字段,传入加密方法
			Field[] declaredFields = parameterObjectClass.getDeclaredFields();
			encrypt(declaredFields, parameterObject);
		}
		return invocation.proceed();
	}

	public <T> T encrypt(Field[] declaredFields, T paramsObject) throws IllegalAccessException {
		for (Field field : declaredFields) {
			// 取出所有被EncryptTransaction注解的字段
			EncryptTransaction encryptTransaction = field.getAnnotation(EncryptTransaction.class);
			if (!Objects.isNull(encryptTransaction)) {
				field.setAccessible(true);
				Object object = field.get(paramsObject);
				// 暂时只实现String类型的加密
				if (object instanceof String) {
					String value = (String) object;
					// 加密
					try {
						field.set(paramsObject, AESUtil2.encrypt(value));
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}
		return paramsObject;
	}

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {

	}

}
package com.xinke.ebid.common.shiro;


import com.xinke.ebid.common.annotations.DecryptTransaction;
import com.xinke.ebid.common.utils.AESUtil2;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Properties;

/**
 * 查询数据结果字段解密
 *  @author CGY
 */
@Component
@Intercepts({ @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = { Statement.class }) })
public class ResultSetInterceptor implements Interceptor {

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		// 取出查询的结果
		Object resultObject = invocation.proceed();
		if (Objects.isNull(resultObject)) {
			return null;
		}
		// 基于selectList
		if (resultObject instanceof ArrayList) {
			ArrayList resultList = (ArrayList) resultObject;
			if (!CollectionUtils.isEmpty(resultList)) {
				for (Object result : resultList) {
					// 逐一解密
					this.decrypt(result);
				}
			}
			// 基于selectOne
		} else {
			this.decrypt(resultObject);
		}
		return resultObject;
	}

	public <T> T decrypt(T result) throws IllegalAccessException {
		// 取出resultType的类
		Class<?> resultClass = result.getClass();
		Field[] declaredFields = resultClass.getDeclaredFields();
		for (Field field : declaredFields) {
			// 取出所有被DecryptTransaction注解的字段
			DecryptTransaction decryptTransaction = field.getAnnotation(DecryptTransaction.class);
			if (!Objects.isNull(decryptTransaction)) {
				field.setAccessible(true);
				Object object = field.get(result);
				// String的解密
				if (object instanceof String) {
					String value = (String) object;
					// 对注解的字段进行逐一解密
					try {
						field.set(result, AESUtil2.decrypt(value));
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}
		return result;
	}

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {

	}
}
package com.xinke.ebid.common.utils;

import com.xinke.ebid.common.utils.ca.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;


public class AESUtil2 {
	private static final String defaultV = "6859505890402435";
	private static final String key = "1061697007556132";

	private static SecretKeySpec getKey(String strKey) throws Exception {
		byte[] arrBTmp = strKey.getBytes();
		byte[] arrB = new byte[16]; // 创建一个空的16位字节数组(默认值为0)
		for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) {
			arrB[i] = arrBTmp[i];
		}
		SecretKeySpec skeySpec = new SecretKeySpec(arrB, "AES");
		return skeySpec;
	}

	/**
	 * 加密
	 * 
	 * @param content
	 * @return
	 * @throws Exception
	 */
	public static String encrypt(String content) throws Exception {
		if (StringUtils.isNotBlank(content)) {
			final Base64.Encoder encoder = Base64.getEncoder();
			SecretKeySpec skeySpec = getKey(key);
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			IvParameterSpec iv = new IvParameterSpec(defaultV.getBytes());
			cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
			byte[] encrypted = cipher.doFinal(content.getBytes());
			String encodedText = encoder.encodeToString(encrypted);
			return encodedText;
		}
		return content;
	}

	/**
	 * 解密
	 * 
	 * @param content
	 * @return
	 * @throws Exception
	 */
	public static String decrypt(String content) {
		try {
			final Base64.Decoder decoder = Base64.getDecoder();
			SecretKeySpec skeySpec = getKey(key);
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			IvParameterSpec iv = new IvParameterSpec(defaultV.getBytes());
			cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
			byte[] base64 = decoder.decode(content);
			byte[] original = cipher.doFinal(base64);
			String originalString = new String(original);
			return originalString;
		} catch (Exception e) {
			return content;
		}
	}

}

使用mybatis-plus时,加密的工具类使用

package com.funong.matrix.core.annotation;

import com.funong.matrix.core.utils.AESUtil2;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.plugin.*;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.util.Objects;
import java.util.Properties;
import java.util.Map;
/**
 * 用于处理请求数据字段加密
 * 
 * @author CGY
 *
 */
@Component
@Intercepts({ @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class), })
public class ParameterInterceptor implements Interceptor {

	// 更新时的参数名称,ParamMap的key值
	private static final String CRYPT = "et";

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		// @Signature 指定了 type= parameterHandler 后,这里的 invocation.getTarget()
		// 便是parameterHandler
		// 若指定ResultSetHandler ,这里则能强转为ResultSetHandler
		ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
		// 获取参数对像,即 mapper 中 paramsType 的实例
		Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");
		parameterField.setAccessible(true);
		// 取出实例
		Object parameterObject = parameterField.get(parameterHandler);
        if (parameterObject != null) {
            Object sensitiveObject = null;
            if (parameterObject instanceof MapperMethod.ParamMap) {
                // 更新操作被拦截
                Map paramMap = (Map) parameterObject;
                if (paramMap.containsKey(CRYPT)) {
                    sensitiveObject = paramMap.get(CRYPT);
                }
                // 插入操作被拦截,parameterObject即为待插入的实体对象
            } else {
                // 插入操作被拦截,parameterObject即为待插入的实体对象
                sensitiveObject = parameterObject;
            }
            // 获取不到数据就直接放行
            if (Objects.isNull(sensitiveObject)) {
                return invocation.proceed();
            }
            // 校验该实例的类是否被@EncryptTransaction所注解
            Class<?> sensitiveObjectClass = sensitiveObject.getClass();
            Field[] declaredFields = sensitiveObjectClass.getDeclaredFields();
            encrypt(declaredFields, sensitiveObject);
        }
		return invocation.proceed();
	}

	public <T> T encrypt(Field[] declaredFields, T paramsObject) throws IllegalAccessException {
		for (Field field : declaredFields) {
			// 取出所有被EncryptTransaction注解的字段
			EncryptTransaction encryptTransaction = field.getAnnotation(EncryptTransaction.class);
			if (!Objects.isNull(encryptTransaction)) {
				field.setAccessible(true);
				Object object = field.get(paramsObject);
				// 暂时只实现String类型的加密
				if (object instanceof String) {
					String value = (String) object;
					// 加密
					try {
						field.set(paramsObject, AESUtil2.encrypt(value));
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}
		return paramsObject;
	}

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {

	}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值