系列
開篇
- 近期因?yàn)轫椖康男枨蟪榭昭芯苛讼耺ybatis的攔截器碉考,因?yàn)橹坝衜ybatis的源碼的基礎(chǔ)塌计,所以熟悉起來稍微順利一些。
- 關(guān)于Mybatis的原理可以參考MyBatis攔截器原理介紹的文章侯谁,算是打個基礎(chǔ)吧锌仅。
請求參數(shù)攔截改寫
/**
* 通過注解來表明,我們需要對那個字段進(jìn)行加密
*/
@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);
}
- 在方法上增加注解,指定加密后前后的字段轴合,后續(xù)解析到方法層面存在注解我們就就去修改參數(shù)晓殊。
- 之所以在方法層面使用注解是為了減少影響面,降低已有業(yè)務(wù)的風(fēng)險魁衙。
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 {
// 獲取攔截器攔截的設(shè)置參數(shù)對象DefaultParameterHandler
ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
// 通過mybatis的反射來獲取對應(yīng)的值
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字段對應(yīng)執(zhí)行的SQL的方法的全路徑报腔,包含類名和方法名
String id = mappedStatement.getId();
String className = id.substring(0, id.lastIndexOf("."));
String methodName = id.substring(id.lastIndexOf(".") + 1);
// 動態(tài)加載類并獲取類中的方法
final Method[] methods = Class.forName(className).getMethods();
// 遍歷類的所有方法并找到此次調(diào)用的方法
for (Method method : methods) {
if (method.getName().equalsIgnoreCase(methodName)
&& method.isAnnotationPresent(ParamAnnotation.class)) {
// 獲取方法上的注解以及注解對應(yīng)的參數(shù)
ParamAnnotation paramAnnotation = method.getAnnotation(ParamAnnotation.class);
String srcKey = paramAnnotation.srcKey()[0];
String destKey = paramAnnotation.destKey()[0];
// 反射獲取參數(shù)對象
MetaObject param = MetaObject.forObject(parameterObject, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, REFLECTOR_FACTORY);
Object srcValue = param.getValue(srcKey);
// 動態(tài)加工指定參數(shù)
String destValue = String.valueOf(srcValue) + "fix";
// 將修改后的動態(tài)參數(shù)添加到請求參數(shù)當(dāng)中
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) {
}
}
- 針對方法參數(shù)的攔截器定義在ParameterHandler的setParameters方法。
- MetaObject.forObject是mybatis提供的反射方法剖淀,簡便了反射獲取和修改字段纯蛾。
- ParameterHandler的攔截器的Invocation的target為ParameterHandler對象。
- 核心的步驟在代碼中通過注釋標(biāo)明了纵隔,可以自行閱讀了解翻诉。
請求結(jié)果攔截改寫
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface EncryptResultFieldAnnotation {
/**
* 加密策略 -- 和加密字段炮姨,一一對應(yīng)
*/
Class<? extends IEncryptResultFieldStrategy>[] encryptStrategy() default {};
/**
* 加密字段對應(yīng)的key
*/
String[] fieldKey() default {};
}
- 定義注解用來在方法上,指定加密前字段和加密方法米丘。
@Repository
public interface UserManageMapper {
@EncryptResultFieldAnnotation(fieldKey = "password", encryptStrategy = PasswordEncryptStrategy.class)
UserInfoVo getOneUser();
}
- 在方法層面使用注解剑令,減少影響面,降低風(fēng)險拄查。
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.*;
/**
* 通過攔截器對返回結(jié)果中的某個字段進(jìn)行加密處理
*/
@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 {
// 獲取到返回結(jié)果
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]);
}
// 對結(jié)果進(jìn)行處理
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;
}
}
- 針對請求結(jié)果的攔截器定義在ResultSetHandler的handleResultSets方法吁津。
- 通過MetaObject.forObject在返回結(jié)果中獲取指定字段并經(jīng)過處理設(shè)置到返回結(jié)果當(dāng)中。