java web ServletRequest过滤

在实际的java web开发过程中,经常会遇到需要对ServletRequest对象内容进行过滤的情况,有两种可选方法:在需要引用ServletRequest对象内容的地方进行内容过滤;添加过滤器对所有ServletRequest对象内容进行过滤。前者实现简单,但维护起来十分麻烦,程序冗余严重;后者一劳永逸,但由于ServletRequest对象的内容不能直接被修改,所以,需要重新构造ServletRequest对象内容。本文描述了一种在实际应用当中,对web请求进行过滤的方法。从添加过滤器到实施过程中遇到的各种问题,再到最终满足需求的程序完成。并将解决各种问题的参考链接加入到了文中。

开发过滤ServletRequest内容的类

由于ServletRequest对象的内容不能直接被修改,所以,需要重新构造ServletRequest对象内容,通常采用的做法是:新建一个类继承HttpServletRequestWrapper,并添加构造函数如下:

public class SecurityHttpServletRequestWrapper extends HttpServletRequestWrapper {
    public SecurityHttpServletRequestWrapper(HttpServletRequest request) {
       super(request);
}

接下来,就是要对其内容进行过滤了,在对经常使用到的getParameter()方法结果进行过滤,最简单的方式是在SecurityHttpServletRequestWrapper 类中添加 private Map

    @Override
    public String getParameter(String name) {
        String[] values = params.get(name);
        if(values == null || values.length == 0) return null;
        return values[0];
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        return params;
    }

    @Override
    public String[] getParameterValues(String name) {
        return params.get(name);
    }

这里参考了:https://blog.csdn.net/tyyytcj/article/details/78528499

本以为到这里就完美结束了,但是,实际并非如此!现象:在系统中使用了spring @RequestBody注解的地方,未能获得页面所发送的请求内容。

经过一番源码查看,终于发现:@RequestBody注解调用了ServletRequest的getInputStream()方法。

那又是为什么使用getInputStream()就获取不到内容呢?

归根结底,程序在获取请求内容的时候,根据不同场景会使用到request.getParameter()、request.getInputStream()和request.getReader()这三者中的其中一个,而这三种方法是有冲突的,因为流只能被读一次,具体原因参考:https://www.cnblogs.com/v5hanhan/p/5646054.html

那么知道原因之后,我们现在要做的就是,重写getInputStream()方法,使之能够返回有效内容。此处参考:https://www.cnblogs.com/bigVGod/p/7240778.html

实践中还发现中文编码存在问题,解决方案参考:https://www.zhihu.com/question/35728721
调整之后,新的构造函数代码如下:

    // request.getParameter()、request.getInputStream()和request.getReader()这三种方法是有冲突的,
    // 原因是InputStream内容只能被读取一次,
    // 所以需要针对三种方法分别制作有效返回内容。其中request.getReader()被用到的情况比较少,暂未制作对应返回。
    private ServletInputStream servletInputStream;
    private Map<String, String[]> params = new HashMap<String, String[]>();
    public SecurityHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);

        String finalStr = null;
        try {
            byte[] originBytes = StreamUtils.copyToByteArray(request.getInputStream());
            //此处对内容进行过滤
            String safeStr = clearThreatChar(new String(originBytes));
            //字符串解码,如果不执行此操作,中文将无法正常显示
            finalStr = URLDecoder.decode(safeStr,"utf-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
        //获取请求方式为get的请求参数,此类参数无法从InputStream中获取到。
        params.putAll(request.getParameterMap());
        //从InputStream中解析参数,并添加至params中。
        for(String k:finalStr.split("&")){
            int index = k.indexOf("=");
            if(index > 0){
                String key = k.split("=")[0];
                String value = "";
                if((index+1) < k.length()){
                    value = k.substring(index+1);
                }
                params.put(key,new String[]{value});
            }
        }

        byte[] requestBody = finalStr.getBytes();
        if(requestBody == null) requestBody = new byte[0];
        servletInputStream = generateInputStream(new ByteArrayInputStream(requestBody));
    }

另外关于request.getReader()相关方法重写参考:http://hae.iteye.com/blog/2175117

完整的代码由于有一百多行,就不贴在这里了,可以前往(https://download.csdn.net/download/chenjhit/10357697)下载,顺便挣两个积分,或者加我的QQ,我直接发送给你们!

开发Filter过滤器

这里直接贴代码,主要就是需要跳过负载过大的请求,避免内存溢出问题。

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by Administrator on 18-4-16.
 */
public class SecurityFilter implements Filter {
    // 用于排除不用进行安全过滤的请求;
    // 原因是由于部分请求内容过大,会导致溢出异常;
    // 使用Map的理由是:不使用for循环,节省时间。
    Map<String,String> excludeRequestMap = new HashMap<String,String>(){
        {
            put("upload",""); //请求示例
        }
    };

    public void init(FilterConfig filterConfig) throws ServletException {

    }

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        String path = ((HttpServletRequest)servletRequest).getServletPath();
        if(excludeRequestMap.containsKey(path)){
            filterChain.doFilter(servletRequest,servletResponse);
        }else {
            filterChain.doFilter(new SecurityHttpServletRequestWrapper((HttpServletRequest)servletRequest),servletResponse);
        }
    }

    public void destroy() {

    }
}

安全过滤

跨站脚本攻击(XSS)过滤的两种方法:https://blog.csdn.net/jwdstef/article/details/42082427

SQL注入过滤:https://blog.csdn.net/chenjhit/article/details/78253216

拓展思考

其实过滤还可以利用spring 的aop,在切面中进行过滤,可以跳过许多不必要的请求过滤,减少系统资源占用,但是由于原有系统代码凌乱,不敢轻易放过任何一个请求,所以还是写在了filter里,大家可以尝试在aop中实现,整体思路是差不多的。

==================================
==疑问?帮助?批评?欢迎评论 | QQ:593159978==
==================================

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值