前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用户输入的虎狼之词,怎么校验之后不见了?

用户输入的虎狼之词,怎么校验之后不见了?

作者头像
一个程序员的成长
发布2020-11-25 14:53:22
6100
发布2020-11-25 14:53:22
举报
文章被收录于专栏:bingfeng-技术bingfeng-技术

不知道你们有没有对用户输入的东西进行过敏感校验,如果不进行校验,用户属于一些攻击脚本,那么我们的服务就挂逼啦!所以我们首先需要通过过滤器将用户的数据读出来进行安全校验,这里面涉及到一个动作,就是需要将用户的数据在过滤器中读出来,进行校验,通过之后再放行。

问题

如果我们的数据是get请求倒还好,但是如果是一些数据量比较大,我们需要通过post json的方式来说传递数据的时候,这个时候其实是通过流的方式传递的,如果在过滤器中将参数读取出来之后,然后放行,等到到Servlet的时候,@RequestBody是无法获取到数据的,因为post json使用流传递,流被读取之后就不存在了,所以我们在过滤器中读取之后,@ReqeustBody自然就读不到数据了,同时会报如下一个错误。

  • 在过滤器中读取body中的数据
代码语言:javascript
复制
@WebFilter
@Slf4j
public class CheckUserFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;

        // 在过滤器中读取数据
        BufferedReader reader = request.getReader();

        StringBuilder sb = new StringBuilder();

        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
        reader.close();

        System.out.println(sb.toString());

        filterChain.doFilter(request, res);
    }
}
  • 出现异常,就是说内容已经被读取了,你不能调用了
代码语言:javascript
复制
{	"id":"1",	"username":"bingfeng"}
java.lang.IllegalStateException: UT010003: Cannot call getInputStream(), getReader() already called
	at io.undertow.servlet.spec.HttpServletRequestImpl.getInputStream(HttpServletRequestImpl.java:666)
	at javax.servlet.ServletRequestWrapper.getInputStream(ServletRequestWrapper.java:152)
	at javax.servlet.ServletRequestWrapper.getInputStream(ServletRequestWrapper.java:152)

解决

  • HttpServletRequestWrapper

那么出现这种问题怎么办呢?能不能通过一个中间的变量将这些数据保存下来,然后我们就可以一直读取了,这样不就解决了这个问题了吗?那保存在哪里呢?这个时候 HttpServletRequestWrapper 就派上用场了。

这个其实你可以把它理解为Request的包装类,Reqeust中有的方法它都有,我们通过继承这个类,重写该类中的方法,将body中的参数保存一个byte数组中,然后放行的时候将这个包装类传递进去,不就可以一直拿到参数了?

  • 封装Request类
代码语言:javascript
复制
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] body;

    /**
     * 所有参数的集合
     */
    private Map<String, String[]> parameterMap;


    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        BufferedReader reader = request.getReader();
        body = readBytes(reader);
        parameterMap = request.getParameterMap();
    }


    @Override
    public BufferedReader getReader() throws IOException {

        ServletInputStream inputStream = getInputStream();

        if (null == inputStream) {
            return null;
        }

        return new BufferedReader(new InputStreamReader(inputStream));
    }

    @Override
    public Enumeration<String> getParameterNames() {
        Vector<String> vector = new Vector<>(parameterMap.keySet());
        return vector.elements();
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

        if (body == null) {
            return null;
        }

        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream() {

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

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

            @Override
            public void setReadListener(ReadListener listener) {

            }

            @Override
            public int read() throws IOException {
                return bais.read();
            }
        };
    }

    /**
     * 通过BufferedReader和字符编码集转换成byte数组
     *
     * @param br
     * @return
     * @throws IOException
     */
    private byte[] readBytes(BufferedReader br) throws IOException {
        String str;
        StringBuilder retStr = new StringBuilder();
        while ((str = br.readLine()) != null) {
            retStr.append(str);
        }
        if (StringUtils.isNotBlank(retStr.toString())) {
            return retStr.toString().getBytes(StandardCharsets.UTF_8);
        }
        return null;
    }
}
  • 将过滤器改造
代码语言:javascript
复制
@WebFilter
@Slf4j
public class CheckUserFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;

        BodyReaderHttpServletRequestWrapper requestWrapper = new BodyReaderHttpServletRequestWrapper(request);

        // 从Request的包装类中读取数据
        BufferedReader reader = requestWrapper.getReader();

        StringBuilder sb = new StringBuilder();

        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
        reader.close();

        System.out.println(sb.toString());

        filterChain.doFilter(requestWrapper, res);
    }
}

经过这样的配置之后,我们即使在过滤器中获取了参数,请求也会到达Servlet。

如果基础知识IO那块不是很扎实的话,第一眼看到这个问题确实挺懵逼的。我也是百度之后解决的,确实值得记录一下,有时候我们会对所有请求进来的参数进行保存输出什么的,这个时候如果是post json数据的话,如果不是特别明白,可能也会出现这种问题。

日拱一卒,功不唐捐

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-05-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一个程序员的成长 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题
  • 解决
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档