【问题记录】fastjson序列化MultipartFile接口入参导致的异常

一、异常场景复现

1、maven

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.jaxrs/jackson-jaxrs-json-provider -->
<dependency>
    <groupId>com.fasterxml.jackson.jaxrs</groupId>
    <artifactId>jackson-jaxrs-json-provider</artifactId>
    <version>2.13.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
    <scope>provided</scope>
</dependency>
<!-- 切面 spring-boot-starter-aop -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.6.2</version>
</dependency>

2、java

2-1、Java-aspect

package com.example.temp.apo;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Slf4j
public class LogAspect {

    @Pointcut("execution(public * com.example.temp.controller..*.*(..))")
    public void logPointCut() {
    }

    @Before("logPointCut()")
    public void doBefore(JoinPoint joinPoint) {
        log.info(" |---- 请求体: {}", JSON.toJSONString(joinPoint.getArgs()));
    }

    @After("logPointCut()")
    public void doAfter() {
    }

    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void afterThrowing(Throwable e) {
    }
}

2-2、java-controller

package com.example.temp.controller;

import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequestMapping("file")
public class FileController {
    @PostMapping(value = "/upload")
    public Object importLicense(@RequestParam("file") MultipartFile file) {
        if (file == null || file.isEmpty()) {
            return "上传文件不能为空";
        }
        return null;
    }
}

3、触发

postman

二、异常堆栈

2023-07-23 07:03:49.148 [traceId:]  ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] -175 -Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.alibaba.fastjson.JSONException: write javaBean error, fastjson version 1.2.83, class org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile, fieldName : 0, write javaBean error, fastjson version 1.2.83, class org.springframework.web.multipart.MultipartFileResource, fieldName : resource] with root cause
java.io.FileNotFoundException: MultipartFile resource [file] cannot be resolved to absolute file path
	at org.springframework.core.io.AbstractResource.getFile(AbstractResource.java:138)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.alibaba.fastjson.util.FieldInfo.get(FieldInfo.java:571)
	at com.alibaba.fastjson.serializer.FieldSerializer.getPropertyValueDirect(FieldSerializer.java:143)
	at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:284)
	at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:154)
	at com.alibaba.fastjson.serializer.FieldSerializer.writeValue(FieldSerializer.java:318)
	at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:472)
	at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:154)
	at com.alibaba.fastjson.serializer.JSONSerializer.writeWithFieldName(JSONSerializer.java:360)
	at com.alibaba.fastjson.serializer.JSONSerializer.writeWithFieldName(JSONSerializer.java:338)
	at com.alibaba.fastjson.serializer.ObjectArrayCodec.write(ObjectArrayCodec.java:118)
	at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:312)
	at com.alibaba.fastjson.JSON.toJSONString(JSON.java:793)
	at com.alibaba.fastjson.JSON.toJSONString(JSON.java:731)
	at com.alibaba.fastjson.JSON.toJSONString(JSON.java:688)
	at com.example.temp.apo.LogAspect.doBefore(LogAspect.java:20)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)

三、堆栈分析

  1. nested exception is com.alibaba.fastjson.JSONException: write javaBean error ,提示到fastjson序列化javaBean时出错,且后面提示class org.springframework.web.multipart.MultipartFileResource,指出MultipartFile相关。
  2. **at com.example.temp.apo.LogAspect.doBefore(LogAspect.java:20)**指明了异常所在代码位置,该行代码为:
log.info(" |---- 请求体: {}", JSON.toJSONString(joinPoint.getArgs()));

四、问题定位

基本可以断定,异常源自于JSON.toJSONString(joinPoint.getArgs()),该语句目的是为了打印接口参数信息,但【上传】接口的参数为MultipartFile file,导致fastjson序列化异常。

五、问题解决

1、思路分析

由于日志切面是公共切面,去除**JSON.toJSONString(joinPoint.getArgs())参数打印信息,属于因噎废食下下策,那么只能从【上传】文件接口入手了。
既然fastjson与MultipartFile不合,那么我们可以考虑将MultipartFile封装入一个Po对象中,并在MultipartFile属性字段上加上注解
@JSONField(serialize = false)**这样一来,对于日志切面而言,【上传】
接口的参数就不是MultipartFile,而是一个自定义的普通java对象,且MultipartFile不必序列化。

2、代码实现

2-1、controller

@PostMapping(value = "/upload")
    public Object importLicense(FileParam fileParam) {

        if (fileParam == null
                || fileParam.getFile() == null
                || fileParam.getFile().isEmpty()) {
            return "上传文件不能为空";
        }
        return null;
    }

2-2、FileParam

package com.example.temp.param;

import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;

@Data
public class FileParam {
    @JSONField(serialize = false)
    private MultipartFile file;
    private String Context;
}

六、再次调用,无异常

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Fastjson是一款流行的Java JSON库,用于序列化和反序列化Java对象和JSON数据。在Fastjson 1.2.24及之前的版本中存在一个安全漏洞,被称为Fastjson序列化漏洞(Fastjson Deserialization Vulnerability)或Fastjson序列化漏洞。 该漏洞的主要原因是Fastjson在反序列化过程中存在一些不安全的默认行为,可能导致恶意攻击者利用特制的JSON数据触发远程代码执行。攻击者可以构造恶意JSON数据,利用漏洞触发任意代码执行、命令执行、远程命令执行等攻击。 Fastjson团队在发现漏洞后迅速发布了修复版本,并建议所有使用Fastjson的开发者升级到最新版本以解决安全问题。此外,开发者还可以采取以下措施来防止Fastjson序列化漏洞的利用: 1. 及时升级:确保使用的Fastjson版本是修复了该漏洞的最新版本。 2. 输入验证:在接收JSON数据并进行反序列化之前,对输入进行严格验证和过滤,确保只接受可信任的数据。 3. 白名单机制:限制反序列化过程中可以实例化的类和调用的方法,使用白名单机制来控制允许的操作。 4. 安全配置:通过配置Fastjson的ParserConfig,禁用自动类型识别(autoTypeSupport)或限制白名单(setAccept)等来增强安全性。 总结而言,Fastjson序列化漏洞是由于Fastjson在反序列化过程中的不安全默认行为导致的安全问题。及时升级Fastjson版本、输入验证、白名单机制和安全配置等措施可以帮助防止该漏洞的利用。 请注意,本回答仅涉及Fastjson序列化漏洞的概述,具体防范措施可能因应用场景和需求而有所不同。建议在实际开发中仔细研究并采取适合的安全措施来保护应用程序免受潜在攻击。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值