2024/1/5:java反序列化:fastjson

参见b站白日梦组长,前置需要JNDI注入知识

流程一览

对于fastjson的简单示例:

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.example.fastjsonlearn;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class FastjsonlearnApplication {

public static void main(String[] args) {
String s = "{\"param1\":\"a\",\"param2\":\"b\"}";
String ss = "{\"age\":\"1\",\"name\":\"b\"}";
String sss = "{\"@type\":\"com.example.fastjsonlearn.Person\",\"age\":1,\"name\":\"b\"}";
String f = "{\"@type\":\"com.example\",\"age\":1,\"name\":\"b\"}";

JSONObject jsonObject = JSON.parseObject(s);
System.out.println(jsonObject);
System.out.println(jsonObject.getString("param1"));
System.out.println(jsonObject.getString("a"));
//SpringApplication.run(FastjsonlearnApplication.class, args);

System.out.println("");

Person person = JSON.parseObject(ss, Person.class);
//将输入的json解析成类的形式
System.out.println(person.getName());

System.out.println("");

JSONObject jsonObject1 = JSON.parseObject(sss);
//传入了一个Person类,解析为Person
//如果这里@type后没有找到这个类,会把type也作为参数
System.out.println(jsonObject1);
JSONObject jsonObject2 = JSON.parseObject(f);
System.out.println(jsonObject2);
}
}
/*
输出:
{"param1":"a","param2":"b"}
a
null

Constructor
setAge
setName
getName
b

Constructor
setAge
setName
getAge
getName
{"name":"b","age":1}
{"@type":"com.example","name":"b","age":1}

进程已结束,退出代码0
*/

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
package com.example.fastjsonlearn;

public class Person {
private int age;
private String name;
public Person(){
System.out.println("Constructor");
}
public int getAge(){
System.out.println("getAge");
return age;
}
public void setAge(int age){
System.out.println("setAge");
this.age = age;
}
public String getName(){
System.out.println("getName");
return name;
}
public void setName(String name){
System.out.println("setName");
this.name = name;
}
}

注意到解析为类时会调用类里面对应参数的get set方法,如果有的话,比如getName(),随便换个名字不行

怎么工作的

首先传进去的字符串会被解析为键值对,在DefaultJSONParser.java进行字符匹配:

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
public Object parse(Object fieldName) {
final JSONLexer lexer = this.lexer;
switch (lexer.token()) {
case SET://值为int 21
lexer.nextToken();
HashSet<Object> set = new HashSet<Object>();
parseArray(set, fieldName);
return set;
case TREE_SET://22
lexer.nextToken();
TreeSet<Object> treeSet = new TreeSet<Object>();
parseArray(treeSet, fieldName);
return treeSet;
case LBRACKET://值为14 [
JSONArray array = new JSONArray();
parseArray(array, fieldName);
if (lexer.isEnabled(Feature.UseObjectArray)) {
return array.toArray();
}
return array;
case LBRACE://{
JSONObject object = new JSONObject(lexer.isEnabled(Feature.OrderedField));
return parseObject(object, fieldName);
case LITERAL_INT://2
Number intValue = lexer.integerValue();
lexer.nextToken();
return intValue;
case LITERAL_FLOAT://3
Object value = lexer.decimalValue(lexer.isEnabled(Feature.UseBigDecimal));
lexer.nextToken();
return value;
case LITERAL_STRING://4
String stringLiteral = lexer.stringVal();
lexer.nextToken(JSONToken.COMMA);

if (lexer.isEnabled(Feature.AllowISO8601DateFormat)) {
JSONScanner iso8601Lexer = new JSONScanner(stringLiteral);
try {
if (iso8601Lexer.scanISO8601DateIfMatch()) {
return iso8601Lexer.getCalendar().getTime();
}
} finally {
iso8601Lexer.close();
}
}

return stringLiteral;
case NULL://8
lexer.nextToken();
return null;
case UNDEFINED://23
lexer.nextToken();
return null;
case TRUE://6
lexer.nextToken();
return Boolean.TRUE;
case FALSE://7
lexer.nextToken();
return Boolean.FALSE;
case NEW://9
lexer.nextToken(JSONToken.IDENTIFIER);

if (lexer.token() != JSONToken.IDENTIFIER) {
throw new JSONException("syntax error");
}
lexer.nextToken(JSONToken.LPAREN);

accept(JSONToken.LPAREN);
long time = ((Number) lexer.integerValue()).longValue();
accept(JSONToken.LITERAL_INT);

accept(JSONToken.RPAREN);

return new Date(time);
case EOF://20
if (lexer.isBlankInput()) {
return null;
}
throw new JSONException("unterminated json string, " + lexer.info());
case ERROR://1
default:
throw new JSONException("syntax error, " + lexer.info());
}
}

这里就是具体解析json的地方,在上面注意到接受大括号后调了这个类的另一个方法,parseObject

在这个方法中有:

1
if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect))

DEFAULT_TYPE_KEY就是"@type"

在这个语句内会进行反序列化操作

首先会加载这个类,之后就会到这个语句:

1
ObjectDeserializer deserializer = config.getDeserializer(clazz);

之后会执行自行实现的getDeclaredFields和getMethods方法,类似反射的操作,在JavaBeanInfo.java

与原生反序列化异同点

1.不需要实现serializable

2.变量有对应的set或get或public,fastjson能自己完成反序列化,保证它能找到就行

3.入口类是get set

4.都需要利用反射和动态类加载

利用链