Mybatis拦截器改写请求参数和结果
2021-10-16 本文已影响0人
晴天哥_王志
系列
开篇
- 近期因为项目的需求抽空研究了下mybatis的拦截器,因为之前有mybatis的源码的基础,所以熟悉起来稍微顺利一些。
- 关于Mybatis的原理可以参考MyBatis拦截器原理介绍的文章,算是打个基础吧。
请求参数拦截改写
/**
* 通过注解来表明,我们需要对那个字段进行加密
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface ParamAnnotation {
String[] srcKey() default {};
String[] destKey() default {};
}
- 定义注解用来在方法上,指定加密前字段和加密后字段。
@Repository
public interface UserManageMapper {
@ParamAnnotation(srcKey = {"phone"}, destKey = {"phone"})
Integer addOneUser(UserInfoVo userInfoVo);
}
- 在方法上增加注解,指定加密后前后的字段,后续解析到方法层面存在注解我们就就去修改参数。
- 之所以在方法层面使用注解是为了减少影响面,降低已有业务的风险。
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.util.Properties;
@Intercepts({
@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class)
})
public class ParamInterceptor implements Interceptor {
private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
private static final ReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory();
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 获取拦截器拦截的设置参数对象DefaultParameterHandler
ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
// 通过mybatis的反射来获取对应的值
MetaObject metaResultSetHandler = MetaObject.forObject(parameterHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, REFLECTOR_FACTORY);
MappedStatement mappedStatement = (MappedStatement) metaResultSetHandler.getValue("mappedStatement");
Object parameterObject = metaResultSetHandler.getValue("parameterObject");
// id字段对应执行的SQL的方法的全路径,包含类名和方法名
String id = mappedStatement.getId();
String className = id.substring(0, id.lastIndexOf("."));
String methodName = id.substring(id.lastIndexOf(".") + 1);
// 动态加载类并获取类中的方法
final Method[] methods = Class.forName(className).getMethods();
// 遍历类的所有方法并找到此次调用的方法
for (Method method : methods) {
if (method.getName().equalsIgnoreCase(methodName)
&& method.isAnnotationPresent(ParamAnnotation.class)) {
// 获取方法上的注解以及注解对应的参数
ParamAnnotation paramAnnotation = method.getAnnotation(ParamAnnotation.class);
String srcKey = paramAnnotation.srcKey()[0];
String destKey = paramAnnotation.destKey()[0];
// 反射获取参数对象
MetaObject param = MetaObject.forObject(parameterObject, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, REFLECTOR_FACTORY);
Object srcValue = param.getValue(srcKey);
// 动态加工指定参数
String destValue = String.valueOf(srcValue) + "fix";
// 将修改后的动态参数添加到请求参数当中
param.setValue(destKey, destValue);
break;
}
}
// 回写parameterObject对象
metaResultSetHandler.setValue("parameterObject", parameterObject);
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
- 针对方法参数的拦截器定义在ParameterHandler的setParameters方法。
- MetaObject.forObject是mybatis提供的反射方法,简便了反射获取和修改字段。
- ParameterHandler的拦截器的Invocation的target为ParameterHandler对象。
- 核心的步骤在代码中通过注释标明了,可以自行阅读了解。
请求结果拦截改写
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface EncryptResultFieldAnnotation {
/**
* 加密策略 -- 和加密字段,一一对应
*/
Class<? extends IEncryptResultFieldStrategy>[] encryptStrategy() default {};
/**
* 加密字段对应的key
*/
String[] fieldKey() default {};
}
- 定义注解用来在方法上,指定加密前字段和加密方法。
@Repository
public interface UserManageMapper {
@EncryptResultFieldAnnotation(fieldKey = "password", encryptStrategy = PasswordEncryptStrategy.class)
UserInfoVo getOneUser();
}
- 在方法层面使用注解,减少影响面,降低风险。
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import java.lang.reflect.Method;
import java.sql.Statement;
import java.util.*;
/**
* 通过拦截器对返回结果中的某个字段进行加密处理
*/
@Intercepts({
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}
)
})
public class EncryptResultFieldInterceptor implements Interceptor {
private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
private static final ReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory();
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 获取到返回结果
ResultSetHandler resultSetHandler = (ResultSetHandler) invocation.getTarget();
MetaObject metaResultSetHandler = MetaObject.forObject(resultSetHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, REFLECTOR_FACTORY);
MappedStatement mappedStatement = (MappedStatement) metaResultSetHandler.getValue("mappedStatement");
EncryptResultFieldAnnotation annotation = getEncryptResultFieldAnnotation(mappedStatement);
Object returnValue = invocation.proceed();
if (annotation != null && returnValue != null) {
String[] fieldKeyList = annotation.fieldKey();
Class<? extends IEncryptResultFieldStrategy>[] strategyClassList = annotation.encryptStrategy();
if (strategyClassList.length != 0 && fieldKeyList.length == strategyClassList.length) {
Map<String, Class<? extends IEncryptResultFieldStrategy>> strategyMap = null;
for (int index = 0; index < fieldKeyList.length; index++) {
if (strategyMap == null) {
strategyMap = new HashMap<>();
}
strategyMap.put(fieldKeyList[index], strategyClassList[index]);
}
// 对结果进行处理
try {
if (returnValue instanceof ArrayList<?>) {
List<?> list = (ArrayList<?>) returnValue;
for (int index = 0; index < list.size(); index++) {
Object returnItem = list.get(index);
if (returnItem instanceof String) {
List<String> stringList = (List<String>) list;
IEncryptResultFieldStrategy encryptStrategy = strategyMap.get(fieldKeyList[0]).newInstance();
stringList.set(index, encryptStrategy.encrypt((String) returnItem));
} else {
MetaObject metaReturnItem = MetaObject.forObject(returnItem, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, REFLECTOR_FACTORY);
for (Map.Entry<String, Class<? extends IEncryptResultFieldStrategy>> entry : strategyMap.entrySet()) {
String fieldKey = entry.getKey();
IEncryptResultFieldStrategy fieldEncryptStrategy = entry.getValue().newInstance();
Object fieldValue = metaReturnItem.getValue(fieldKey);
if (fieldValue instanceof String) {
metaReturnItem.setValue(fieldKey, fieldEncryptStrategy.encrypt((String) fieldValue));
}
}
}
}
}
} catch (Exception e) {
// ignore
}
}
}
return returnValue;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
/**
* 获取方法上的EncryptResultFieldAnnotation注解
*
* @param mappedStatement MappedStatement
* @return EncryptResultFieldAnnotation注解
*/
private EncryptResultFieldAnnotation getEncryptResultFieldAnnotation(MappedStatement mappedStatement) {
EncryptResultFieldAnnotation encryptResultFieldAnnotation = null;
try {
String id = mappedStatement.getId();
String className = id.substring(0, id.lastIndexOf("."));
String methodName = id.substring(id.lastIndexOf(".") + 1);
final Method[] method = Class.forName(className).getMethods();
for (Method me : method) {
if (me.getName().equals(methodName) && me.isAnnotationPresent(EncryptResultFieldAnnotation.class)) {
encryptResultFieldAnnotation = me.getAnnotation(EncryptResultFieldAnnotation.class);
break;
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
return encryptResultFieldAnnotation;
}
}
- 针对请求结果的拦截器定义在ResultSetHandler的handleResultSets方法。
- 通过MetaObject.forObject在返回结果中获取指定字段并经过处理设置到返回结果当中。