SpringBoot动态注入bean (系统注入漏洞原理)
前有log4j高危漏洞,后有fastjson注入风险,可见即便是强大的中间件jar也不可避免的存在bug,今天花时间写了一个SpringBoot动态注入bean到容器中的demo,直接上代码这是一个暴漏出去的Controller,用来接收注入的bean。/*** 动态注入IocBean的Controller: 项目中使用有风险,需谨慎使用*/@RestControllerpublic class
前有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容器中
更多推荐
所有评论(0)