解决request流只能读取一遍的问题

文件上传的漏洞需要解决,需要使用过滤器来实现。在过滤器中对文件进行判断处理

遇到一个问题,我这里读取了一遍文件之后,request流被读完了,导致真正获取文件的时候,获取不到

使用包装类,重写

import org.apache.commons.io.IOUtils;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;

public class RequestWrapper extends HttpServletRequestWrapper {
    private final byte[] body;
    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        /*StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        try {
            InputStream inputStream = request.getInputStream();
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            } else {
                stringBuilder.append("");
            }
        } catch (IOException ex) {
            throw ex;
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException ex) {
                    throw ex;
                }
            }
        }*/
        body = IOUtils.toByteArray(request.getInputStream());
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
        ServletInputStream servletInputStream = new ServletInputStream() {
            public boolean isFinished() {
                return false;
            }
            public boolean isReady() {
                return false;
            }
            /*public void setReadListener(ReadListener readListener) {}*/
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
        return servletInputStream;

    }
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
//    public String getBody() {
//        return this.body;
//    }
    public byte[] getBody() {
        return this.body;
    }

}

调用

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    servletRequest.setCharacterEncoding("UTF-8");
    servletResponse.setContentType("text/html;charset=utf-8");

    //判断提交上来的数据是否是上传表单的数据
    if (!ServletFileUpload.isMultipartContent(request)) {
        filterChain.doFilter(servletRequest, servletResponse);
        return;
    }

    HttpServletRequest requestWrapper = null;
    if(servletRequest instanceof HttpServletRequest) {
        requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);
    }
    if (requestWrapper != null) {
        try {
            //1、创建一个DiskFileItemFactory工厂
            DiskFileItemFactory factory = new DiskFileItemFactory();
            //2、创建一个文件上传解析器
            ServletFileUpload upload = new ServletFileUpload(factory);
            List<FileItem> fileList = upload.parseRequest(requestWrapper);
            //上传文件对象集合
			
			//TODO:这个用一个集合来接收,因为会对 FileItem的文件名进行校验,但是 上传文件时,FileItem会有一个无效的文件。所以这里先把有效的存储一遍。
            List<FileItem> fileItemsList = new ArrayList<FileItem>();
            //临时表单控件对象
            FileItem fileItem = null;
            Iterator<FileItem> it = fileList.iterator();
            while (it.hasNext()) {
                fileItem = (FileItem) it.next();
                // 获取上传号码文件
                if (!fileItem.isFormField() && fileItem.getName().length() > 0) {
                    //有效文件对象存放到集合
                    fileItemsList.add(fileItem);
                }
            }
            //进行过滤
            for (FileItem item : fileItemsList) {
                //如果fileitem中封装的是上传文件
                if (!item.isFormField()) {
                    UploadFileCheckUtil.fileCheck(item);
                }
            }

            filterChain.doFilter(requestWrapper, servletResponse);
            return;
        }catch (Exception e) {
            logger.error( e, "过滤文件异常");
        }
    }

    filterChain.doFilter(servletRequest, servletResponse);
}

文件上传校验工具 UploadFileCheckUtil

package com.montnets.emp.common.util;

import org.apache.commons.fileupload.FileItem;
import org.eclipse.jetty.util.ConcurrentHashSet;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 文件上传校验
 *
 * @author :  raymond
 * @version :  V1.0
 * @date :  2022-01-14 11:23
 */
public class UploadFileCheckUtil {
    /**
     * 文件类型白名单
     */
    private final static Set<String> FILE_NAME_WHITE_LIST = new ConcurrentHashSet<String>();
    /**
     * 文件类型黑名单
     */
    private final static Set<String> FILE_NAME_BLACK_LIST = new ConcurrentHashSet<String>();
    /**
     * 文件类型大小
     */
    private final static Map<String, Long> FILE_TYPE_SIZE = new ConcurrentHashMap<String, Long>();
    /**
     * 需要过滤内容的类型
     */
    private final static Set<String> CONTENT_FILTER_FILE_TYPE = new ConcurrentHashSet<String>();
    /**
     * 内容过滤的字符
     */
    private final static Set<String> CONTENT_COMMAND = new ConcurrentHashSet<String>();
    /**
     * 文件头信息对应的文件类型
     */
    private final static Map<String, String> FILE_TYPE_HEAD = new ConcurrentHashMap<String, String>();


    static {
        //白名单
        FILE_NAME_WHITE_LIST.add(".txt");
        FILE_NAME_WHITE_LIST.add(".xlsx");
        FILE_NAME_WHITE_LIST.add(".rar");
        FILE_NAME_WHITE_LIST.add(".jpg");
        FILE_NAME_WHITE_LIST.add(".gif");
        FILE_NAME_WHITE_LIST.add(".jpeg");
        FILE_NAME_WHITE_LIST.add(".png");
        FILE_NAME_WHITE_LIST.add(".mp4");
        FILE_NAME_WHITE_LIST.add(".zip");
        FILE_NAME_WHITE_LIST.add(".et");
        FILE_NAME_WHITE_LIST.add(".csv");
        FILE_NAME_WHITE_LIST.add(".xls");
        FILE_NAME_WHITE_LIST.add(".tms");
        FILE_NAME_WHITE_LIST.add(".mid");
        FILE_NAME_WHITE_LIST.add(".midi");
        FILE_NAME_WHITE_LIST.add(".amr");
        //黑名单
        FILE_NAME_BLACK_LIST.add(".exe");
        FILE_NAME_BLACK_LIST.add(".bat");
        FILE_NAME_BLACK_LIST.add(".sh");
        FILE_NAME_BLACK_LIST.add(".jar");
        FILE_NAME_BLACK_LIST.add(".jsp");
        FILE_NAME_BLACK_LIST.add(".php");
        FILE_NAME_BLACK_LIST.add(".asp");
        FILE_NAME_BLACK_LIST.add(".js");
        //文件类型的大小
        FILE_TYPE_SIZE.put(".jpg", 20 * 1024 * 1024L);
        FILE_TYPE_SIZE.put(".gif", 20 * 1024 * 1024L);
        FILE_TYPE_SIZE.put(".jpeg", 20 * 1024 * 1024L);
        FILE_TYPE_SIZE.put(".png", 20 * 1024 * 1024L);
        FILE_TYPE_SIZE.put(".mp4", 20 * 1024 * 1024L);
        //需要过滤内容的类型
        CONTENT_FILTER_FILE_TYPE.add(".jpg");
        CONTENT_FILTER_FILE_TYPE.add(".gif");
        CONTENT_FILTER_FILE_TYPE.add(".jpeg");
        CONTENT_FILTER_FILE_TYPE.add(".png");
        CONTENT_FILTER_FILE_TYPE.add(".mp4");
        //内容过滤的字符
        CONTENT_COMMAND.add("cmd");
        CONTENT_COMMAND.add("exec");
        CONTENT_COMMAND.add("spy");
        CONTENT_COMMAND.add("shell");
        CONTENT_COMMAND.add("execute");
        CONTENT_COMMAND.add("system");
        CONTENT_COMMAND.add("command");
        //文件头信息对应的文件类型
        FILE_TYPE_HEAD.put(".jpg","ffd8ffe0");
        FILE_TYPE_HEAD.put(".jpeg","ffd8ffe0");
        FILE_TYPE_HEAD.put(".png","89504E47");
        FILE_TYPE_HEAD.put(".gif","47494638");
    }

    private UploadFileCheckUtil() {
    }

    public static void fileCheck(FileItem file) {
        if (file == null || file.isFormField()) {
            return;
        }
        String name = file.getName();
        if (name == null || name.trim().length() == 0) {
            throw new RuntimeException("Failed to obtain file name");
        }
        whiteBlackListCheck(name);
        String fileType = name.substring(name.lastIndexOf("."));
        fileSizeCheck(fileType, file.getSize());
        if (isFilterContent(fileType)) {
            filterContent(new String(file.get()));
        }
    }

    public static void fileCheck(String fileName, String content) {
        if (fileName == null || fileName.trim().length() == 0) {
            throw new RuntimeException("Failed to obtain file name");
        }
        whiteBlackListCheck(fileName);
        String fileType = fileName.substring(fileName.lastIndexOf("."));
        fileSizeCheck(fileType, content.length());
        if (isFilterContent(fileType)) {
            filterContent(content);
        }
    }


    public static void fileSizeCheck(String fileType, long fileSize) {
        Long maxSize = FILE_TYPE_SIZE.get(fileType.toLowerCase());
        if (maxSize != null && fileSize > maxSize) {
            throw new RuntimeException("The file size is too long, fileSize:" + fileSize + ",maxSize:" + maxSize);
        }
    }

    public static void whiteBlackListCheck(String name) {
        whiteListCheck(name);
        blackListCheck(name);
    }

    public static void whiteListCheck(String name) {
        for (String whiteList : FILE_NAME_WHITE_LIST) {
            if (name.toLowerCase().endsWith(whiteList)) {
                return;
            }
        }
        throw new RuntimeException("The file name is not in the whitelist, fileName:" + name);
    }


    public static void blackListCheck(String name) {
        for (String blackList : FILE_NAME_BLACK_LIST) {
            if (name.toLowerCase().contains(blackList)) {
                throw new RuntimeException("The file name is in the blacklist, fileName:" + name);
            }
        }
    }

    public static boolean isFilterContent(String fileType) {
        for (String type : CONTENT_FILTER_FILE_TYPE) {
            if (fileType.toLowerCase().contains(type)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 过滤内容中是否有命令字符
     * @param content 内容
     */
    public static void filterContent(String content) {
        String lowerCase = content.toLowerCase();
        for (String command : CONTENT_COMMAND) {
            String quotesCommand = "'" + command + "'";
            String blankCommand = " " + command + " ";
            if (lowerCase.startsWith(command) || lowerCase.endsWith(command) ||
                    lowerCase.contains("'" + command) || lowerCase.contains(" " + command) ||
                    lowerCase.contains(quotesCommand) || lowerCase.contains(blankCommand)) {
                throw new RuntimeException("Content includes commands, commands:" + command);
            }
        }
    }


    public static void headCheck(String fileType, String head) {
        if (fileType == null || fileType.trim().length() == 0 || head == null || head.trim().length() == 0) {
            throw new RuntimeException("Failed to get the file type or file header");
        }
        if (FILE_TYPE_HEAD.containsKey(fileType.toLowerCase())) {
            if (!head.toLowerCase().equals(FILE_TYPE_HEAD.get(fileType))) {
                throw new RuntimeException("Type that does not match, fileType:" + fileType);
            }
        }
    }

    /**
     * 新增白名单文件类型
     * @param fileType 文件类型
     */
    public static void addWhiteList(String fileType) {
        FILE_NAME_WHITE_LIST.add(fileType);
    }

    /**
     * 新增黑名单文件类型
     * @param fileType 文件类型
     */
    public static void addBlackList(String fileType) {
        FILE_NAME_BLACK_LIST.add(fileType);
    }

    /**
     * 新增需要过滤内容的文件类型
     * @param fileType 文件类型
     */
    public static void addFilterContentFileType(String fileType) {
        CONTENT_FILTER_FILE_TYPE.add(fileType);
    }

    /**
     * 新增过滤文件内容
     * @param command 需要过滤的文件内容
     */
    public static void addFilterContent(String command) {
        CONTENT_COMMAND.add(command);
    }

    /**
     * 移除白名单文件类型
     * @param fileType 文件类型
     */
    public static void removeWhiteList(String fileType) {
        FILE_NAME_WHITE_LIST.remove(fileType);
    }

    /**
     * 移除黑名单文件类型
     * @param fileType 文件类型
     */
    public static void removeBlackList(String fileType) {
        FILE_NAME_BLACK_LIST.remove(fileType);
    }

    /**
     * 移除需要过滤内容的文件类型
     * @param fileType 文件类型
     */
    public static void removeFilterContentFileType(String fileType) {
        CONTENT_FILTER_FILE_TYPE.remove(fileType);
    }

    /**
     * 移除过滤文件内容
     * @param command 需要过滤的文件内容
     */
    public static void removeFilterContent(String command) {
        CONTENT_COMMAND.remove(command);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值