美文网首页mybatis
Mybatis拦截器改写请求参数和结果

Mybatis拦截器改写请求参数和结果

作者: 晴天哥_王志 | 来源:发表于2021-10-16 22:04 被阅读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在返回结果中获取指定字段并经过处理设置到返回结果当中。

    相关文章

      网友评论

        本文标题:Mybatis拦截器改写请求参数和结果

        本文链接:https://www.haomeiwen.com/subject/ckaboltx.html