通过二次代理来实现Mybatis的加解密

170 阅读2分钟
  • 前言

开始先闲聊下吧,距离上一篇的博客又过去了许久,总是拖延拖延,再拖延,回归正题,大家肯定都有做过加解密吧,我这里就来聊聊这个。

  • 方案分析

    我司的ORM框架是Mybatis,我这里就通过Mybatis来讲讲吧。Mybatis最主流是方式是通过Mybatis的拦截器,这个是基于动态代理和责任链实现的,我实现的时候考虑过这个方案,我发现这个方案有一个问题就是,拦截器是全局生效的,不存针对某个mapper,这就会造成性能上的浪费,那么有没有什么方案可以避免性能上的浪费呢?此刻我就想到了是否能对需要加解密的mapper进行动态代理呢?答案肯定是可以!

  • 方案

    大家都知道Mybatis在Spring中是一个MapperFactoryBean,然后FactoryBean会执行两次的BeanPostProcessor的处理,一次是MapperFactoryBean,第二次就是真正的经过动态代理的Mapper了,那么我们就可以利用这里一点在第二次操作的时候进行动态代理然后进行注入。我们先定义一个注解进行标注mapper是否需要进行加解密操作

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    @Documented
    /**
     * 标记 mpper 进行脱敏
     */
    public @interface Desensitization {
    }
    

核心逻辑就是如下:

public class DesensitizationMapperBeanPostProcessor implements BeanPostProcessor {

    private static final Map<String, Class> BEANS = new ConcurrentHashMap<>(32);

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MapperFactoryBean) {
            MapperFactoryBean mapperFactoryBean = (MapperFactoryBean) bean;
            BEANS.put(beanName, mapperFactoryBean.getMapperInterface());
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MapperFactoryBean) {
            return bean;
        }
        final Class aClass = BEANS.get(beanName);
        if (aClass != null) {
            final Desensitization annotation = AnnotationUtils.findAnnotation(aClass, Desensitization.class);
            if (annotation != null) {
                return Proxy.newProxyInstance(aClass.getClassLoader(), new Class[]{aClass}, new DesensitizationInvocationHandler(bean));
            }
        }
        return bean;

    }
}

核心逻辑就是在第一次调用BeanPostProcessor时进行记录,然后在第二次调用时,找到被注解@Desensitization标注的Object进行动态代理。然后DesensitizationInvocationHandler就是加解密的具体逻辑啦,因为篇幅原因,我就不再详述啦,总之Spring真的留了特别多的扩展点,nice!