前有log4j高危漏洞,后有fastjson注入风险,可见即便是强大的中间件jar也不可避免的存在bug,今天花时间写了一个SpringBoot动态注入bean到容器中的demo,直接上代码

这是一个暴漏出去的Controller,用来接收注入的bean。

/**
 * 动态注入IocBean的Controller: 项目中使用有风险,需谨慎使用
 */
@RestController
public class DynamicInjectionController {
    
    @PostMapping("/injectBean")
    public Map injectBean(@RequestBody Map<String, String> map){

        return SpringbootMavenTestApplication.injectBean(map);
    }
}

用过SpringBoot的朋友不难看出,这是一个SpringBoot的主启动类,
SpringApplication.run(SpringbootMavenTestApplication.class, args);
只不过比常见的启动类多了2个方法,injectBean方法是用来注入bean的方法,参数map是前端controller接受的所有参数,
我们只需要指定beanName(ioc容器中每个bean都有一个beanName)
和location(利用jdk的反射机制去加载位置location的class文件),
然后把加载后的class放到容器中得到beanDefinition对象(ioc容器中每个bean都有一个beanDefinition,类似于每个被虚拟机加载过的类都有一个class对象)

@SpringBootApplication
public class SpringbootMavenTestApplication {
    
    private static ApplicationContext beanFactory;
    
    public static void main(String[] args) {
        beanFactory = SpringApplication.run(SpringbootMavenTestApplication.class, args);

        System.out.println("====================SpringBoot启动成功====================");
    }
    
    public static Map injectBean(Map<String, String> map){
        Map resultMap = new HashMap();
        //如果Ioc容器中不存在该beanName,则会抛出异常(被捕获后不做任何处理)
        try {
            Object beanName = beanFactory.getBean(map.get("beanName"));
            if (beanName != null){
                //如果beanName存在直接返回容器
                resultMap.put("msg", "已经存在使用该beanName的bean");
                return resultMap;
            }
        }catch (Exception e){
        }
        //通过反射获取location位置的类
        Class<?> aClass = null;
        try {
             aClass = Class.forName(map.get("location"));
        } catch (ClassNotFoundException e) {
            resultMap.put("msg", "已经存在使用该b不存在location信息匹配的类eanName的bean");
            return resultMap;
        }
        //创建Spring的BeanDefinitionBuilder
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(aClass);
        //设置注入bean的属性值
        for (int i = aClass.getDeclaredFields().length - 1; i >= 0; i--) {
            if (map.get(aClass.getDeclaredFields()[i].getName()) != null){
                beanDefinitionBuilder.addPropertyValue(aClass.getDeclaredFields()[i].getName(), map.get(aClass.getDeclaredFields()[i].getName()));
            }else {
                beanDefinitionBuilder.addPropertyValue(aClass.getDeclaredFields()[i].getName(), "123456");
            }
        }
        //注册bean
        ((BeanDefinitionRegistry)beanFactory).registerBeanDefinition(map.get("beanName"), beanDefinitionBuilder.getBeanDefinition());
        //结果信息封装
        resultMap = getClassAndBeanInfo(aClass, ((BeanDefinitionRegistry) beanFactory).getBeanDefinition(map.get("beanName")));
        return resultMap;
    }

    /**
     * 获取类信息和beanDefinition信息封装到结果集中
     */
    public static Map getClassAndBeanInfo(Class aClass, BeanDefinition beanDefinition){
        Map resultMap = new HashMap();
        //java原生类aClass信息
        List<Object> classFieldList = new ArrayList<>();
        for (Field field : aClass.getDeclaredFields()) {
            classFieldList.add(field.getType());
        }
        List<Object> classMethodList = new ArrayList<>();
        for (Method method : aClass.getDeclaredMethods()) {
            classMethodList.add(method.getName());
        }
        resultMap.put("class", aClass);
        resultMap.put("class字段", classFieldList);
        resultMap.put("class方法", classMethodList);
        resultMap.put("beanDefinition", beanDefinition.toString());
        return resultMap;
    }

}

结果如下:
在这里插入图片描述

可见我们通过controller传入的参数生效了,位于location位置的class文件被注入到了bean容器中

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐