Java 从 HttpServletRequestWrapper 覆盖 getInputStream 以在 JSON 中转义 HTML

Java Overide getInputStream from HttpServletRequestWrapper to escape HTML in JSON

我正在尝试覆盖 HttpServletRequestWrapper#getInputStream()。我正在尝试读取正文中的 JSON 并转义 HTML 标签以防止 XSS。我正在处理具有多个端点的大型应用程序。我正在使用 JacksonMapper 将 JASON 转换为 POJO,因此想在实际映射之前清理输入。我的代码看起来像

    @Override
    public ServletInputStream getInputStream() throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    Map<String, Object> jsonMap = mapper.readValue(super.getInputStream(),
            Map.class);

    for (String key : jsonMap.keySet()) {
        if (jsonMap.get(key) != null) {
            if (jsonMap.get(key).getClass() == String.class)
                jsonMap.put(key, (Object) StringEscapeUtils.escapeHtml((String) jsonMap.get(key)));
        }
    }
    ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
    ObjectOutputStream out = new ObjectOutputStream(byteOut);
    mapper.writeValue(out, jsonMap);
    final ByteArrayInputStream byteIn = new ByteArrayInputStream(
            byteOut.toByteArray());
    ServletInputStream inputStream = new ServletInputStream() {

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

我收到如下 Jackson 映射异常:

8:29:20,323 WARN  [org.jboss.resteasy.core.SynchronousDispatcher] (http-/0.0.0.0:7080-7) Failed executing PUT /portal/api/tenant/2: org.jboss.resteasy.spi.ReaderException: 
org.codehaus.jackson.JsonParseException: Unexpected character ('¬' (code 172)): 
expected a valid value (number, String, array, object, 'true', 'false' or 'null')                                                                                                                                                                                    
at [Source: com.dell.em.controller.RequestWrapper@4ed35486; line: 1, column: 2]                                                                                                                                                                               
    at org.jboss.resteasy.core.MessageBodyParameterInjector.inject(MessageBodyParameterInjector.java:202) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]                                                                                     
    at org.jboss.resteasy.core.MethodInjectorImpl.injectArguments(MethodInjectorImpl.java:136) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]                                                                                                
    at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:159) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]                                                                                                         
    at org.jboss.resteasy.core.ResourceMethod.invokeOnTarget(ResourceMethod.java:269) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]                                                                                                         
    at org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:227) [resteasy-jaxrs-2.3.6.Final-redhat-1.jar:2.3.6.Final-redhat-1]                

我不确定字符来自哪里。我有一个简单的 json 作为输入:

{"id":2,"tenantName":"Test","tenantLogo":null,"address1":"<script>alert(\"o1\")</script>","city":"test","state":"Alabama","zip":"78769","country":"United States","timezone":"US/Central","contactFirstName":"Test","contactLastName":"Test","contactEmail":"Test@rest.com","contactPhone":"9794444444","address2":null,"domainName":null}

以防有人遇到同样的问题。我必须对新的 InputStream 进行编码,而不是 writeVAlue 我需要 writeValueAsString 来在 json.

周围加上引号
@Override
public ServletInputStream getInputStream() throws IOException {
    ObjectMapper mapper = new ObjectMapper();
    InputStream in = super.getInputStream();
    Map<String, Object> jsonMap = mapper.readValue(in, Map.class);

    for (String key : jsonMap.keySet()) {
        if (jsonMap.get(key) != null) {
            if (jsonMap.get(key).getClass() == String.class)
                jsonMap.put(key, (Object) StringEscapeUtils
                        .escapeHtml((String) jsonMap.get(key)));
        }
    }

    final ByteArrayInputStream byteIn = new ByteArrayInputStream(Charset
            .forName("UTF-16").encode(mapper.writeValueAsString(jsonMap))
            .array());


    ServletInputStream inputStream = new ServletInputStream() {

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

    return inputStream;
}

我已经修改了@Rahul Dhar 的解决方案以适合我的情况,因为我有几个休息服务接受常规输入以及 json 输入(比如接受正文作为用户的 ID )

@Override
public ServletInputStream getInputStream() throws IOException {

        ObjectMapper mapper = new ObjectMapper();
        InputStream in = super.getInputStream();
        String inputString = IOUtils.toString(in, "utf-8");

        if (inputString.contains("{")) {    // is JSON
            @SuppressWarnings("unchecked")
            Map<String, Object> jsonMap = mapper.readValue(inputString, Map.class);

            for (String key : jsonMap.keySet()) {
                Object value = jsonMap.get(key);
                if (value != null) {
                    if (value.getClass() == String.class)
                        jsonMap.put(key, (Object) stripXSS((String) value)); // (stripXSS is a private method that do spit out xss characters)
                }
            }
            return new CustomServletInputStream(
                    Charset.forName("utf-8").encode(mapper.writeValueAsString(jsonMap)).array());
        } else {
            return new CustomServletInputStream(Charset.forName("utf-8").encode(inputString).array());
        }
}