对信息进行加密保存到数据库中,在读取数据库时能够看到解密后的数据。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DecryptField {
String value() default "";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {
String value() default "";
}
@Target({ElementType.TYPE})//该参数代表是作用在类、方法上的
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveBean {
String value() default "";
}
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
这个注解代表着拦截执行器Executor的所有查询操作和所有更新操作(insert,update,delete)
//以下是拦截器需要实现的方法
@Override
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement statement = (MappedStatement) invocation.getArgs()[0];
// 获取该sql语句的类型,例如update,insert
String methodName = invocation.getMethod().getName();
// 获取该sql语句放入的参数
Object parameter = invocation.getArgs()[1];
// 如果是查询操作,并且返回值不是敏感实体类对象,并且传入参数不为空,就直接调用执行方法,返回这个方法的返回值
// 方法中可以判断这个返回值是否是多条数据,如果有数据,就代表着是select 操作,没有就代表是update insert delete,
// 因为mybatis的dao层不能为非select操作设置返回值
if (statement.getResultMaps().size() > 0) {
// 获取到这个返回值的类属性
Class> type = statement.getResultMaps().get(0).getType();
// 返回值没有带敏感实体类对象注解
if (!type.isAnnotationPresent(SensitiveBean.class)) {
// 直接执行语句并返回
return invocation.proceed();
}
}
// 如果该参数为空,就不进行判断敏感实体类,直接执行sql
// 并且
// 如果判断该参数带有敏感实体类的注解,才对这个实体类进行扫描查看是否有加密解密的注解
if (parameter != null && sensitiveBean(parameter)) {
// 如果有就扫描是否是更新操作和是否有加密注解
// 如果是更新或者插入时,就对数据进行加密后保存在数据库
if (StringUtils.equalsIgnoreCase("update", methodName) ||
StringUtils.equalsIgnoreCase("insert", methodName)) {
// 对参数内含注解的字段进行加密
encryptField(parameter);
}
}
// 继续执行sql语句,调用当前拦截的执行方法
Object returnValue = invocation.proceed();
try {
// 当返回值类型为数组集合时,就判断是否需要进行数据解密
if (returnValue instanceof ArrayList>) {
List> list = (List>) returnValue;
// 判断结果集的数据是否为空
if (list == null || list.size() < 1) {
return returnValue;
}
Object object = list.get(0);
// 为空值就返回数据
if (object == null) {
return returnValue;
}
// 判断第一个对象是否有解密注解
Field[] fields = object.getClass().getDeclaredFields();
// 定义一个临时变量
int len;
if (fields != null && 0 < (len = fields.length)) {
for (Object o : list) {
//调用解密,在这个方法中针对某个带有解密注解的字段进行解密
decryptField(o);
}
}
}
} catch (Exception e) {
e.printStackTrace();
return returnValue;
}
return returnValue;
}
/**
* 声明这是一个泛型方法,让所有参数都能够调用这个方法扫描带有解密注解的字段,
* 进行解密,然后显示在前端页面中
*
* @param
*/
public void decryptField(T t) {
// 获取对象的域
Field[] declaredFields = t.getClass().getDeclaredFields();
if (declaredFields != null && declaredFields.length > 0) {
// 遍历这些字段
for (Field field : declaredFields) {
// 如果这个字段存在解密注解就进行解密
if (field.isAnnotationPresent(DecryptField.class) && field.getType().toString().endsWith("String")) {
field.setAccessible(true);
try {
// 获取这个字段的值
String fieldValue = (String) field.get(t);
// 判断这个字段的数值是否不为空
if (StringUtils.isNotEmpty(fieldValue)) {
// 首先调用一个方法判断这个数据是否是未经过加密的,如果可以解密就进行解密,解密失败就返回元数据
boolean canDecrypt;
canDecrypt = UpdateUtils.judgeDataForView(fieldValue);
if (canDecrypt) {
// 进行解密
String encryptData = Cryptos.aesDecrypt(fieldValue);
if (encryptData.equals("解密失败")) {
logger.error("该字段不是被加密的字段,需要联系管理员进行修改数据");
}
// 将值反射到对象中
field.set(t, encryptData);
} else {
// 不能解密的情况下,就不对这个对象做任何操作,即默认显示元数据
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
/**
* 查看这个类是否带有敏感实体类注解,有就返回true,否则返回false
*
* @param t
* @param
* @return
*/
public boolean sensitiveBean(T t) {
// 判断是否带有敏感实体类的注解
if (t.getClass().isAnnotationPresent(SensitiveBean.class)) {
logger.info("带有敏感实体类的注解");
return true;
} else {
return false;
}
}
/**
* 声明这是一个泛型方法,让所有参数都能够调用这个方法扫描带有加密注解的字段,
* 进行加密,然后存在数据库中
*
* @param
*/
public void encryptField(T t) {
Field[] declaredFields = t.getClass().getDeclaredFields();
if (declaredFields != null && declaredFields.length > 0) {
for (Field field : declaredFields) {
// 查找当字段带有加密注解,并且字段类型为字符串类型
if (field.isAnnotationPresent(EncryptField.class) && field.getType().toString().endsWith("String")) {
field.setAccessible(true);
String fieldValue = null;
try {
// 获取这个值
fieldValue = (String) field.get(t);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// 判断这个值是否为空
if (StringUtils.isNotEmpty(fieldValue)) {
try {
// 不为空时,就进行加密
field.set(t, Cryptos.aesEncrypt(fieldValue));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
}
所有经由mybatis层执行的sql语句(不包括hibernate和jdbc)
mybatis拦截器(插件)
https://blog.csdn.net/wizard_rp/article/details/79821671