在filter中获取request的输入流以至于出现getInputStream() has already been called for this request问题的解决

通过Filter处理request数据,并对其进行校验

问题需求

现在需要对所有用户的输入数据请求进行处理,要求用户请求数据的数据不含有html的标签。对于该问题首先我们可以写一个filter过滤器,取出request中的数据,请求数据包括param和body的形式,也就是字符串拼接或者放在请求体中。取出数据后,在进行正则表达式校验就可以了。

出现的问题

思路就是这样,在写的过程中,但是由于我们从request中调用了getInputStream(),这样会导致,在controller层,我们使用@RequestBody注解获取数据时,出现getInputStream() has already been called for this request异常。因为request中的getRead() 和 getInputStream()在读取一次后标记为-1,无法再次被读取,而在@RequestBody在ServletServerHttpRequest中,也调用了getInputStream()方法所以如果数据在filter中被读取,将无法在@RequestBody中再次读取。当然如果传输的参数中存在文件,任然会有getInputStream() has already been called for this request异常,这个时候可以将自己文件上传的url过滤掉。

解决方法

我们设置一个私有变量用于存储原始request的body数据或者param数据,并重写getReader()和getInputStream()方法,这样我们在filter处理了body数据,又能在controller中用@RequestBody中获取到数据了此时解决方案,将request进行包装(装饰者模式),并重写getInputStream()和getRead()方法。

最后代码

filter类:
@Configuration
@Slf4j
public class HttpRequestParamFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) {
        log.info("HttpRequestParamFilter init...");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        //此处可以查询出所有的文件导入接口,排除掉文件上传导致ResponseBody失效引发的getInputStream() has already been called for this request异常
        List<String> uploadFileUrlList = new ArrayList<>();
        uploadFileUrlList.add("/ios/sysmgr/template/import");
        uploadFileUrlList.add("/ios/auth/app/upload");
        uploadFileUrlList.add("/doc.html");
        boolean exitUrl = true;
        for (String url : uploadFileUrlList) {
            if (url.equals(request.getServletPath())) {
                exitUrl = false;
                break;
            }
        }
        //获取param 参数并校验param中的参数
        String result = "";
        IRequestParamWrapper iRequestParamWrapper = new IRequestParamWrapper(request);
        String paramData = iRequestParamWrapper.getParam();
        if (paramData != null && !StringUtils.isEmpty(paramData)) {
            log.info("param===>{}", paramData);
            Map<String, String> resultMap = new HashMap<>(2);
            resultMap.put("code", "1");
            resultMap.put("illegalData", paramData);
            response.setContentType("application/x-www-form-urlencoded;charset=UTF-8");
            response.getOutputStream().write(JSON.toJSONString(resultMap).getBytes("UTF-8"));
            return;
        }
        //获取body参数
        IRequestBodyWrapper iRequestWrapper = null;
        if (exitUrl) {
            iRequestWrapper = new IRequestBodyWrapper(request);
            String bodyData = iRequestWrapper.getBody();
            if (bodyData != null && !StringUtils.isEmpty(bodyData)) {
                log.info("body====>{}", bodyData);
                result = CheckRequestParamUtil.regexBody(bodyData);
            }
            if (result != null && !StringUtils.isEmpty(result)) {
                log.info("body存在非法输入");
                Map<String, String> resultMap = new HashMap<>(2);
                resultMap.put("code", "1");
                resultMap.put("illegalData", result);
                response.setContentType(request.getContentType() + ";charset=UTF-8");
                response.getOutputStream().write(JSON.toJSONString(resultMap).getBytes("UTF-8"));
                return;
            }
        }
        filterChain.doFilter(iRequestWrapper != null ? iRequestWrapper : request, servletResponse);
    }

    @Override
    public void destroy() {
        log.info("HttpRequestParamFilter destroy....");
    }
}
//获取body数据
@Slf4j
public class IRequestBodyWrapper extends HttpServletRequestWrapper {

    /**
     * 记录request的body数据
     */
    private final String body;

    /**
     * 获取request body数据
     *
     * @param request
     * @throws IOException
     */
    public IRequestBodyWrapper(HttpServletRequest request) throws IOException {
        super(request);
        this.body = CheckRequestParamUtil.readHttpRequestBody(request);
    }

    public String getBody() {
        return body;
    }

    
    @Override
    public ServletInputStream getInputStream() {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }

            @Override
            public int read() {
                return byteArrayInputStream.read();
            }
        };
    }

    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
}
//获取param数据,并校验
@Slf4j
public class IRequestParamWrapper extends HttpServletRequestWrapper {

    /**
     * 记录request的body数据
     */
    private final String param;

    /**
     * 获取request param数据
     *
     * @param request
     * @throws IOException
     */
    public IRequestParamWrapper(HttpServletRequest request) throws IOException {
        super(request);
        this.param = CheckRequestParamUtil.readHttpRequestParam(request);
        System.out.println("param=" + param);
    }

    public String getParam() {
        return param;
    }


    @Override
    public ServletInputStream getInputStream() {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(param.getBytes());
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }

            @Override
            public int read() {
                return byteArrayInputStream.read();
            }
        };
    }

    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
}
//正则匹配工具类
@Slf4j
public class CheckRequestParamUtil {
    /**
     * 缓冲区大小
     */
    private static final int BUFFER_SIZE = 1024 * 8;
    /**
     * 匹配<script></script>标签
     */
    public static final String SCRIPT = "<script[^>]*>([^<]*)</script>";
    /**
     * 匹配<div></div> 标签正则
     */
    public static final String DIV = "<div[^>/]*>(.*?)</div>";
    /**
     * 匹配<form></form> 标签正则
     */
    public static final String FORM = "<form[^>]*>([^<]*)</form>";
    /**
     * 匹配<a></a> 标签正则表达式
     */
    public static final String A = "<a[^>]*>([^<]*)</a>";
    /**
     * 匹配input框标签
     */
    public static final String INPUT = "<input[^>/]*/>";

    public static final Map<String, String> regexMap = new HashMap<>(16);

    static {
        regexMap.put("<script></script>", SCRIPT);
        regexMap.put("<div></div>", DIV);
        regexMap.put("<input />", INPUT);
        regexMap.put("<a></a>", A);
        regexMap.put("<form></form>", FORM);
    }

    /**
     * 获取request请求中的参数
     *
     * @param request
     * @return
     */
    public static String readHttpRequestParam(HttpServletRequest request) {
        Map<String, Object> requestParamMap = new HashMap<>(16);
        Enumeration<String> paramNames = request.getParameterNames();
        while (paramNames.hasMoreElements()) {
            String paramName = paramNames.nextElement();
            String[] paramValues = request.getParameterValues(paramName);
            if (paramValues.length > 0) {
                String paramValue = paramValues[0];
                if (paramValue.length() != 0) {
                    requestParamMap.put(paramName, paramValue);
                }
            }
        }
        Set<Map.Entry<String, Object>> set = requestParamMap.entrySet();
        StringBuffer result = new StringBuffer();
        for (Map.Entry entry : set) {
            log.debug(entry.getKey() + ":" + entry.getValue());
            String temp = regexBody(entry.getValue().toString());
            if (temp != null || !StringUtils.isEmpty(temp)) {
                result.append(temp);
            }
        }
        return result.toString();
    }

    /**
     * 读取body中的数据
     *
     * @param request
     * @return
     * @throws IOException
     */
    public static String readHttpRequestBody(HttpServletRequest request) throws IOException {
        BufferedReader bufferedReader = request.getReader();
        StringWriter writer = new StringWriter();
        write(bufferedReader, writer);
        return writer.getBuffer().toString();
    }

    public static long write(Reader reader, Writer writer) throws IOException {
        return write(reader, writer, BUFFER_SIZE);
    }

    public static long write(Reader reader, Writer writer, int bufferSize) throws IOException {
        int read;
        long total = 0;
        char[] buf = new char[bufferSize];
        while ((read = reader.read(buf)) != -1) {
            writer.write(buf, 0, read);
            total += read;
        }
        return total;
    }

    /**
     * 校验是否存在完整html的标签数据
     *
     * @param body body数据
     * @return java.lang.String 提示信息
     */
    public static String regexBody(String body) {
        Iterator<Map.Entry<String, String>> entryIterator = regexMap.entrySet().iterator();
        StringBuffer result = new StringBuffer();
        while (entryIterator.hasNext()) {
            Map.Entry<String, String> entry = entryIterator.next();
            Pattern pattern = Pattern.compile(entry.getValue());
            Matcher matcher = pattern.matcher(body);
            if (matcher.find()) {
                result.append("(不合法标签内的内容:").append(matcher.group(0)).append(");");
            }
        }
        return result.toString();
    }
}

以上就是大致实现思路。

测试

测试字符串:
在这里插入图片描述
在这里插入图片描述

运行截图
在这里插入图片描述
在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值