【问题标题】:How to alter the body of a http response in a filter如何在过滤器中更改 http 响应的正文
【发布时间】:2014-08-18 14:13:50
【问题描述】:

我正在尝试使用过滤器来检查响应正文中的 HTML 标记。问题是,如果我更改过滤器中的主体,它在到达客户端时不会更改。我尝试了此处显示的解决方案:Looking for an example for inserting content into the response using a servlet filter 但它没有帮助。

我有两个过滤器。 SecureWrapperFilter 将请求/响应对象包装在我们的自定义包装器中,XSSFilter 使用 OWASP 编码对 html 内容进行编码。过滤器如下所示:

public class SecureWrapperFilter implements Filter {

    @Override
    public void init(final FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(final ServletRequest request, final ServletResponse response,
        final FilterChain chain) throws IOException, ServletException
    {
        final ServletRequestWrapper securityRequest =
            new ServletRequestWrapper((HttpServletRequest)request);
        final ServletResponseWrapper securityResponse =
            new ServletResponseWrapper((HttpServletResponse)response);
        ESAPI.httpUtilities().setCurrentHTTP(securityRequest, securityResponse);
        chain.doFilter(ESAPI.currentRequest(), ESAPI.currentResponse());
    }

    @Override
    public void destroy() {
    }
}

和:

public class XSSFilter implements Filter {

    @Override
    public void init(final FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(final ServletRequest request, final ServletResponse response,
        final FilterChain chain) throws IOException, ServletException
    {      
        final ServletRequestWrapper requestWrapper = (ServletRequestWrapper)request;
        final String body = Encode.forHtmlContent(requestWrapper.getBody());
        requestWrapper.setBody(body);
        chain.doFilter(requestWrapper, response);
        final ServletResponseWrapper responseWrapper = (ServletResponseWrapper)response;
        final byte[] copy = responseWrapper.getCopy();
        final String oldBody = new String(copy, response.getCharacterEncoding());
        final String newBody = Encode.forHtmlContent(oldBody);
        if (!StringUtils.equals(oldBody, newBody)) {
            responseWrapper.getResponse().getOutputStream().write(newBody.getBytes());
        }
    }

    @Override
    public void destroy() {
    }
}

如果我添加一些调试日志,我可以看到 securityResponse 在 SecureWrapperFilter 中具有修改后的主体,但在客户端,主体看起来好像从未修改过。

任何建议将不胜感激。谢谢。

【问题讨论】:

    标签: java httpresponse servlet-filters


    【解决方案1】:

    问题是在我的 XSSFilter 中,我将新的响应主体附加到旧响应主体上。这导致无效的 json 像 {"x"="y"}{"escapedx"="escapedy")

    我们的客户端反序列化器只打印第一个 json 对象,所以 {"x"=y"} 是我们在客户端看到的所有内容。

    为了解决这个问题,我在 XSSFilter 中添加了以下行:

    responseWrapper.getResponse().resetBuffer();
    

    之前

    responseWrapper.getResponse().getOutputStream().write(newBody.getBytes());
    

    这会清除缓冲区,让我可以在下面的行中重写它。我在客户端的 json 现在看起来像:{"escapedx"="escapedy"}

    【讨论】:

      【解决方案2】:

      您需要确保 HttpResponse 被缓冲。如果缓冲区不够大,那么在调用您的过滤器之前,响应将被流式传输到客户端。

      或者也许 servler 正在响应中调用 flush()?

      【讨论】:

      • 感谢您的回复。我刚刚在 XSSFilter 的末尾添加了responseWrapper.flushBuffer(),但并没有什么不同。这是唯一调用 flushBuffer() 的地方。
      • 在调用doFilter()之前,调用"res.setBufferSize(8 * 1024); // 8K缓冲区",你可能需要8K以上。您需要足够的资源来缓冲整个响应。
      • 这个我试过了,并没有解决问题。我的反应体很小,所以我怀疑这是罪魁祸首。我现在想知道 javax rest 响应是否会在缓冲区到达我的过滤器之前刷新缓冲区。
      • 感谢您的帮助。我找到了问题所在,感谢您促使我再次查看缓冲区。
      【解决方案3】:

      用jackson可以发回json:

      val res = response as HttpServletResponse
      
      res.status = HttpStatus.UNAUTHORIZED.value()
      res.contentType = MediaType.APPLICATION_JSON_UTF8_VALUE
      
      res.outputStream.write(ObjectMapper().writeValueAsString(ResponseError(
          "two_factor_auth_failed", "Two Factor Authorization is required to proceed."
      )).toByteArray())
      

      【讨论】:

        猜你喜欢
        • 2017-02-07
        • 2019-01-20
        • 1970-01-01
        • 1970-01-01
        • 2020-07-21
        • 2017-10-20
        • 1970-01-01
        • 1970-01-01
        • 2023-03-10
        相关资源
        最近更新 更多