Spring Boot 校验用户上传的图片文件(两种方式)

 更新时间:2023年11月09日 12:02:13   作者:Doker 多克  
图片上传是现代应用中非常常见的一种功能,也是风险比较高的一个地方,恶意用户可能会上传一些病毒、木马,本文给大家介绍两种对图片文件进行校验的方法,感兴趣的朋友一起看看吧

图片上传是现代应用中非常常见的一种功能,也是风险比较高的一个地方。恶意用户可能会上传一些病毒、木马。这些东西不仅严重威胁服务器的安全还浪费了带宽,磁盘等资源。所以,在图片上传的接口中,一定要对用户上传的文件进行严格的校验

本文介绍了 2 种对图片文件进行验证的方法可供你参考。

一、文件后缀校验

通过文件后缀(也就是文件扩展名,通常用于表示文件的类型),进行文件类型校验这是最常见的做法。

图片文件的后缀类型有很多,常见的只有:jpgjpeggifpngwebp。我们可以在配置或者代码中定义一个“允许上传的图片后缀”集合,用于校验用户上传的图片文件。

package cn.springdoc.demo.web.controller;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Set;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
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.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@RestController
@RequestMapping("/upload")
public class UploadController {
    // 允许上传的图片类型的后缀集合
    static final Set<String> imageSuffix = Set.of("jpg", "jpeg", "gif", "png", "webp");
    @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseEntity<String> upload (@RequestParam("file") MultipartFile file ) throws IllegalStateException, IOException{
        // 文件的原始名称
        String fileName = file.getOriginalFilename();
        if (fileName == null) {
            return ResponseEntity.badRequest().body("文件名称不能为空");
        }
        // 解析出文件后缀
        int index = fileName.lastIndexOf(".");
        if (index == -1) {
            return ResponseEntity.badRequest().body("文件后缀不能为空");
        }
        String suffix = fileName.substring(index + 1);
        if (!imageSuffix.contains(suffix.trim().toLowerCase())) {
            return ResponseEntity.badRequest().body("非法的文件类型");
        }
        // IO 到程序运行目录下的 public 目录,这是默认的静态资源目录
        Path dir = Paths.get(System.getProperty("user.dir"), "public");
        if (!Files.isDirectory(dir)) {
            // 创建目录
            Files.createDirectories(dir);
        }
        file.transferTo(dir.resolve(fileName));
        // 返回相对访问路径
        return ResponseEntity.ok("/" + fileName);
    }
}

如上,代码很简单。先是获取客户端文件的名称,再从名称获取到文件的后缀。确定是合法文件后再 IO 到本地磁盘。

二、使用 ImageIO 校验

由于文件的后缀是可编辑的,恶意用户可以把一个 exe 文件的后缀改为 jpg 再上传到服务器。于是这种情况下,上面的这种校验方式就会失效,恶意文件会被 IO 到磁盘。

在基于上面的方法进行校验后,我们可以先把文件 IO 到临时目录,再使用 ImageIO 类去加载图片文件,如果 Images 加载的文件不是图片,则会返回 null

package cn.springdoc.demo.web.controller;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Set;
import java.util.UUID;
import javax.imageio.ImageIO;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
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.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@RestController
@RequestMapping("/upload")
public class UploadController {
    // 允许上传的图片类型的后缀集合
    static final Set<String> imageSuffix = Set.of("jpg", "jpeg", "gif", "png", "webp");
    @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseEntity<String> upload(@RequestParam("file") MultipartFile file)
            throws IllegalStateException, IOException {
        // 文件的原始名称
        String fileName = file.getOriginalFilename();
        if (fileName == null) {
            return ResponseEntity.badRequest().body("文件名称不能为空");
        }
        // 解析出文件后缀
        int index = fileName.lastIndexOf(".");
        if (index == -1) {
            return ResponseEntity.badRequest().body("文件后缀不能为空");
        }
        String suffix = fileName.substring(index + 1);
        if (!imageSuffix.contains(suffix.trim().toLowerCase())) {
            return ResponseEntity.badRequest().body("非法的文件类型");
        }
        // 获取系统中的临时目录
        Path tempDir = Paths.get(System.getProperty("java.io.tmpdir"));
        // 临时文件使用 UUID 随机命名
        Path tempFile = tempDir.resolve(Paths.get(UUID.randomUUID().toString()));
        // copy 到临时文件
        file.transferTo(tempFile);
        try {
            // 使用 ImageIO 读取文件
            if (ImageIO.read(tempFile.toFile()) == null) {
                return ResponseEntity.badRequest().body("非法的文件类型");
            }
            // 至此,这的确是一个图片资源文件
            // IO 到运行目录下的 public 目录
            Path dir = Paths.get(System.getProperty("user.dir"), "public");
            if (!Files.isDirectory(dir)) {
                // 创建目录
                Files.createDirectories(dir);
            }
            Files.copy(tempFile, dir.resolve(fileName));
            // 返回相对访问路径
            return ResponseEntity.ok("/" + fileName);
        } finally {
            // 始终删除临时文件
            Files.delete(tempFile);
        }
    }
}

这种方式更为严格,不但要校验文件后缀还要校验文件内容。弊端也显而易见,会耗费更多的资源!

1、ImageIO.read 方法

最后说一下 ImageIO.read 方法,它会从系统中已注册的 ImageReader 中选择一个 Reader 对图片进行解码,如果没有 Reader 能够解码文件,则返回 null。也就是说,如果上传的图片类型,在系统中没有对应的 ImageReader 也会被当做是“非法文件”。

例如:webp 类型的图片文件,ImageIO.read 就读取不了,因为 JDK 没有预置读取 webp 图片的 ImageReader

要解决这个问题,可以添加一个 webp-imageio 依赖。

<!-- https://mvnrepository.com/artifact/org.sejda.imageio/webp-imageio -->
<dependency>
    <groupId>org.sejda.imageio</groupId>
    <artifactId>webp-imageio</artifactId>
    <version>0.1.6</version>
</dependency>

webp-imageio 库提供了 webp 图片的 ImageReader 实现,并以 SPI 的形式注册到了系统中,不要写其他任何代码就可以成功读取。

到此这篇关于Spring Boot 校验用户上传的图片文件的文章就介绍到这了,更多相关Spring Boot 校验上传图片内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • JAVA(SpringBoot)集成Jasypt进行加密、解密功能

    JAVA(SpringBoot)集成Jasypt进行加密、解密功能

    Jasypt是一个Java库,专门用于简化加密和解密操作,提供多种加密算法支持,集成到SpringBoot等框架中,通过使用Jasypt,可以有效保护配置文件中的敏感信息,如数据库密码等,避免被未授权访问,Jasypt还支持自定义加密器,提高扩展性和安全性,适用于各种需要加密保护应用场景
    2024-09-09
  • Hibernate中Session.get()方法和load()方法的详细比较

    Hibernate中Session.get()方法和load()方法的详细比较

    今天小编就为大家分享一篇关于Hibernate中Session.get()方法和load()方法的详细比较,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-03-03
  • Java文件断点续传实现原理解析

    Java文件断点续传实现原理解析

    这篇文章主要介绍了Java文件断点续传实现原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-05-05
  • 深入浅析java中flyway使用简介

    深入浅析java中flyway使用简介

    Flyway是独立于数据库的应用、管理并跟踪数据库变更的数据库版本管理工具。这篇文章主要介绍了flyway使用简介,需要的朋友可以参考下
    2020-07-07
  • Spring排序机制之接口与注解的使用方法

    Spring排序机制之接口与注解的使用方法

    本文介绍了Spring中多种排序机制,包括Ordered接口、PriorityOrdered接口、@Order注解和@Priority注解,提供了详细示例,并指导如何选择合适的排序机制来控制Bean的加载顺序和优先级,感兴趣的朋友一起看看吧
    2025-02-02
  • SpringBoot整合MyCat实现读写分离的方法

    SpringBoot整合MyCat实现读写分离的方法

    这篇文章主要介绍了SpringBoot整合MyCat实现读写分离的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • SpringBoot入门之集成JSP的示例代码

    SpringBoot入门之集成JSP的示例代码

    这篇文章主要介绍了SpringBoot入门之集成JSP的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-07-07
  • SpringMVC post请求中文乱码问题解决

    SpringMVC post请求中文乱码问题解决

    这篇文章主要介绍了SpringMVC post请求中文乱码问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • IDEA-Maven项目的jdk版本设置方法

    IDEA-Maven项目的jdk版本设置方法

    我们需要设置jdk的版本,不然会提示导致语法错误,这篇文章主要介绍了IDEA-Maven项目的jdk版本设置方法,小编觉得不错,一起来了解一下
    2019-04-04
  • 详解Spring Boot中MyBatis的使用方法

    详解Spring Boot中MyBatis的使用方法

    mybatis初期使用比较麻烦,需要各种配置文件、实体类、dao层映射关联、还有一大推其它配置。当然mybatis也发现了这种弊端。下面通过本文给大家详细介绍Spring Boot中MyBatis的使用方法,感兴趣的朋友一起看看吧
    2017-07-07

最新评论