1.Mybatis执行增删改的底层核心接口是Executor.update,如果我们想对它进行拦截处理,则需要拦截此方法
2.Mybatis执行查询的底层核心接口是ResultSetHandler.handleResultSets,如果我们想对它进行拦截处理,则需要拦截此方法
定义增删改的拦截器:
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
})
@Slf4j
public class IbatisUpdateInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {try {if (invocation.getTarget() instanceof Executor && invocation.getArgs().length == 2) {return invokeUpdate(invocation);}} finally {// 回滚到原生对象MybatisContextHolder.rollback();}return invocation.proceed();}private Object invokeUpdate(Invocation invocation) throws Exception {Executor executor = (Executor) invocation.getTarget();// 获取第一个参数MappedStatement ms = (MappedStatement) invocation.getArgs()[0];// 非insert/update,不处理if (ms.getSqlCommandType() != SqlCommandType.INSERT && ms.getSqlCommandType() != SqlCommandType.UPDATE) {return invocation.proceed();}Object paramObj = invocation.getArgs()[1];// 无参数不处理if (paramObj == null) {return invocation.proceed();}if (!needEncryptDecrypt(paramObj) && !isBatchUpdate(paramObj)) {return invocation.proceed();}// 加密if (paramObj instanceof Map) {Map map = (Map) paramObj;// 批量更新if (map.containsKey("list")) {encryptList((Collection) map.get("list"));} else {for (Object updateObj : map.values()) {if (needEncryptDecrypt(updateObj)) {encryptSensitiveFields(updateObj);}}}} else if(paramObj instanceof Collection) {encryptList((Collection) paramObj);} else {encryptSensitiveFields(paramObj);}return executor.update(ms, paramObj);}private static boolean needEncryptDecrypt(Object obj) {Class<?> clazz = obj.getClass();EncryptDecrypt annotation = AnnotationUtils.findAnnotation(clazz, EncryptDecrypt.class);return annotation != null && annotation.needEncryptDecrypt();}private void encryptList(Collection params) throws Exception {for (Object obj : params) {encryptSensitiveFields(obj);}}private boolean isBatchUpdate(Object paramObj) {if (!(paramObj instanceof Map)) {return false;}Map map = (Map) paramObj;if (map.containsKey("list")) {// 针对批量更新return needEncryptDecrypt(map.get("list"));} else {for (Object updateObj : map.values()) {// 针对updateByExample和updateByExampleSelectiveif (needEncryptDecrypt(updateObj)) {return true;}}}return false;}public static List<Field> getAllFields(Class clazz) {List<Field> allFields = Lists.newArrayList();while (clazz != null && clazz != Object.class) {Field[] tmpFields = clazz.getDeclaredFields();if (tmpFields != null) {allFields.addAll(Arrays.asList(tmpFields));}clazz = clazz.getSuperclass();}return allFields;}public static boolean isStatic(Field f){return (f.getModifiers() & Modifier.STATIC) == Modifier.STATIC;}private Object encryptSensitiveFields(Object paramObj) throws InvocationTargetException, IllegalAccessException {List<Field> allFields = getAllFields(paramObj.getClass());for (Field field : allFields) {Sensitive sensitive = field.getAnnotation(Sensitive.class);if (sensitive == null) {continue;}PropertyDescriptor ps = BeanUtils.getPropertyDescriptor(paramObj.getClass(), field.getName());if (ps.getReadMethod() == null || ps.getWriteMethod() == null) {continue;}Object value = ps.getReadMethod().invoke(paramObj);if (value != null) {ps.getWriteMethod().invoke(paramObj, encryptDbField(value));// register rollbackMybatisContextHolder.registerRollbackEvent(() -> ps.getWriteMethod().invoke(paramObj, value));}}return paramObj;}private Object encryptDbField(Object value) {//加密return null;}@Overridepublic Object plugin(Object target) {return null;}@Overridepublic void setProperties(Properties properties) {}
}
定义查询结果拦截器:
@Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = Statement.class)
})
@Slf4j
public class IbatisResultInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {Object result = invocation.proceed();// 解密if (result instanceof Collection) {Collection<Object> objList = (Collection) result;List<Object> decryptList = Lists.newArrayList();for (Object obj : objList) {decryptList.add(decrypt(obj));}return decryptList;} else {return decrypt(result);}}private Object decrypt(Object obj) throws InvocationTargetException, IllegalAccessException {List<Field> allFields = getAllFields(obj.getClass());for (Field field : allFields) {Sensitive sensitive = field.getAnnotation(Sensitive.class);// 非敏感信息if (sensitive == null) {continue;}PropertyDescriptor ps = BeanUtils.getPropertyDescriptor(obj.getClass(), field.getName());if (ps.getReadMethod() == null || ps.getWriteMethod() == null) {continue;}Object value = ps.getReadMethod().invoke(obj);if (value != null) {try {ps.getWriteMethod().invoke(obj, decryptDbField((String) value));} catch (Exception e) {log.error("decrypt error of field:{}", field.getName());throw e;}}}return obj;}public List<Field> getAllFields(Class clazz) {List<Field> allFields = Lists.newArrayList();while (clazz != null && clazz != Object.class) {Field[] tmpFields = clazz.getDeclaredFields();if (tmpFields != null) {allFields.addAll(Arrays.asList(tmpFields));}clazz = clazz.getSuperclass();}return allFields;}/*** 对字段解密* @param value* @return*/private Object decryptDbField(String value) {//解密,rsa,aes,desreturn null;}@Overridepublic Object plugin(Object target) {return null;}@Overridepublic void setProperties(Properties properties) {}
}
注册拦截器:
@Bean@ConditionalOnProperty(value = "mybatis.encrypt.enabled", havingValue = "true")public IbatisResultInterceptor myIbatisResultInterceptor (SqlSessionFactory sqlSessionFactory) {IbatisResultInterceptor interceptor = new IbatisResultInterceptor();Properties properties = new Properties();interceptor.setProperties(properties);sqlSessionFactory.getConfiguration().addInterceptor(interceptor);return interceptor;}