使用servletfilter从发布的数据中删除表单参数

供应商已经通过HTTPS在名为XMLContent的表单变量中将XML数据发布到我的Coldfusion应用程序服务器。 我最近转移到更新版本的应用程序服务器,这些请求丢失了500个服务器错误。 它抛出了错误,因为第二个表单参数的内容没有正确地进行urlencoded,但我还是不需要那个参数。 (我联系了供应商来解决这个问题,但是他们强迫我付钱修理他们的错误,所以如果可能的话,我希望自己修理它。)

我如何利用servletfilter删除除了名为的forms参数之外的所有参数:XMLContent我已尝试过各种尝试来显式删除有问题的参数“TContent”,但它永远不会被删除。

收到的数据片段:

XMLContent=%3C%3Fxml+version%3D%221.0%22+encoding%3D%22UTF-8%22%3F%3E%0A%3CCheck+xmlns%3D%22http .........&TContent=<check xmlns="http........... 

我试过的代码:

 import java.io.IOException; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.util.*; public class MultipartFilter implements Filter { // Init ---------------------------------------------------------------- public FilterConfig filterConfig; // Actions ------------------------------------------------------------- public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; } /** * Check the type request and if it is a HttpServletRequest, then parse the request. * @throws ServletException If parsing of the given HttpServletRequest fails. * @see javax.servlet.Filter#doFilter( * javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { // Check type request. if (request instanceof HttpServletRequest) { // Cast back to HttpServletRequest. HttpServletRequest httpRequest = (HttpServletRequest) request; // Parse HttpServletRequest. HttpServletRequest parsedRequest = parseRequest(httpRequest); // Continue with filter chain. chain.doFilter(parsedRequest, response); } else { // Not a HttpServletRequest. chain.doFilter(request, response); } } /** * @see javax.servlet.Filter#destroy() */ public void destroy() { this.filterConfig = null; } private HttpServletRequest parseRequest(HttpServletRequest request) throws ServletException { // Prepare the request parameter map. Map parameterMap = new HashMap(); // Loop through form parameters. Enumeration parameterNames = request.getParameterNames(); while (parameterNames.hasMoreElements()) { String paramName = parameterNames.nextElement(); String[] paramValues = request.getParameterValues(paramName); // Add just the XMLContent form parameter if (paramName.equalsIgnoreCase("xmlcontent")) { parameterMap.put(paramName, new String[] { paramValues[0] }); } } // Wrap the request with the parameter map which we just created and return it. return wrapRequest(request, parameterMap); } // Utility (may be refactored to public utility class) --------------- /** * Wrap the given HttpServletRequest with the given parameterMap. * @param request The HttpServletRequest of which the given parameterMap have to be wrapped in. * @param parameterMap The parameterMap to be wrapped in the given HttpServletRequest. * @return The HttpServletRequest with the parameterMap wrapped in. */ private static HttpServletRequest wrapRequest( HttpServletRequest request, final Map parameterMap) { return new HttpServletRequestWrapper(request) { public Map getParameterMap() { return parameterMap; } public String[] getParameterValues(String name) { return parameterMap.get(name); } public String getParameter(String name) { String[] params = getParameterValues(name); return params != null && params.length > 0 ? params[0] : null; } public Enumeration getParameterNames() { return Collections.enumeration(parameterMap.keySet()); } }; } } 

我们在处理语言环境时每天都会遇到这些情况。 如果用户区域设置与浏览器区域设置不同,我们必须更新请求。 但它不可变。

解决方案:创建请求包装类。 将现有的请求参数(您想要的参数)复制到其中,并在filterchain.doFilter()中传递此包装类

样本包装类:

 public class WrappedRequest extends HttpServletRequestWrapper { Map parameterMap; public WrappedRequest(final HttpServletRequest request) { //build your param Map here with required values } @Override public Map getParameterMap() { //return local param map } //override other methods if needed. } 

现在在您的filter代码中,执行以下操作

 wrapRequest = new WrappedRequest(hreq); filterChain.doFilter(wrapRequest, servletResponse); 

希望它能解决你的问题。

途径

代码遵循正确的方法:

  • wrapRequest() ,它实例化HttpServletRequestWrapper并覆盖触发请求解析的4个方法:

    • public String getParameter(String name)
    • public Map getParameterMap()
    • public Enumeration getParameterNames()
    • public String[] getParameterValues(String name)
  • doFilter()方法使用包装请求调用filter链,这意味着后续filter和目标servlet(URL映射)将提供包装请求。

问题

  • 在底层“原始请求”上调用4个方法中的任何一个getParameterXXX()方法会触发所有请求参数的隐式解析(通过服务器内部方法,如Request.parseRequestParameters or parsePameters )。 这4种方法是触发此类解析的唯一方法。
  • 在请求被包装之前,在parseRequest() ,您的代码在底层请求上调用此类方法:

     request.getParameterNames(); request.getParameterValues(paramName); 

您需要控制解析逻辑。 从输入流中读取原始字节并进行自己的URL解码过于复杂 – 这意味着要替换大量紧密耦合的服务器代码。 相反,最简单的方法是只替换执行实际URL解码的方法。 这意味着您可以保留上一节中提到的request.getParameterXXX调用。

不确定您正在托管ColdFusion的服务器,但以下描述基于Glassfish / Tomcat,但可以进行调整。 在这篇文章的底部是Glassfish的内部请求解析方法(Tomcat的修改版本)。 JD-decompiler对于将.class文件转换为.java的修补非常有用。

  1. 找到进行URL解码的类(对于Glassfish,这是来自grizzly-utils.jar com.sun.grizzly.util.http.Parameters ,如下所示,对于Tomcat,这是来自tomcat-coyote.jar org.apache.tomcat.util.http.Parameters tomcat-coyote.jar
  2. 将整个源代码复制到一个新类somepackage.StrippedParameters
  3. 找到解码参数值的代码行(下面: value = urlDecode(this.tmpValue)
  4. 更改此代码,以便仅在参数名称与所需参数匹配时进行解码:

     if (decodeName) name = urlDecode(this.tmpName); else name = this.tmpName.toString(); // // !! THIS IF STATEMENT ADDED TO ONLY DECODE DESIRED PARAMETERS, // !! OTHERS ARE STRIPPED: // if ("XMLContent".equals(name)) { String value; String value; if (decodeValue) value = urlDecode(this.tmpValue); else { value = this.tmpValue.toString(); } try { addParameter(name, value); } catch (IllegalStateException ise) { logger.warning(ise.getMessage()); break; } } 
  5. 现在是棘手的部分:在URL解码发生之前,用类StrippedParmeters替换默认类Parameters 。 获取参数后,将它们复制回容器类。 对于Glassfish,将方法parseRequestParameters复制到HttpServletRequestWrapper的实现中(对于Tomcat,相应的方法是parseParameters中的类org.apache.catalina.connector.Request中的parseParameters

    • 替换此行:

       Parameters parameters = this.coyoteRequest.getParameters(); 

      有:

       Parameters parameters = new somepackage.StrippedParameters(); 
    • 在方法的底部,添加以下内容:

       Parameters coyoteParameters = this.coyoteRequest.getParameters(); for (String paramName : parameters.getParameterNames()) { String paramValue = parameters.getParameterValue(paramName); coyoteParameters.addParameter(paramName, paramValue); } 

Glassfish容器代码 – Tomcat的修改版本,Grizzly取代Coyote(Catalina Servlet Engine仍然指Coyote对象,但它们是像Coyote一样被嘲笑的Grizzly实例)

 package org.apache.catalina.connector; .... public class Request implements HttpRequest, HttpServletRequest { .... protected com.sun.grizzly.tcp.Request coyoteRequest; .... // This is called from the 4 methods named 'getParameterXXX' protected void parseRequestParameters() { Parameters parameters = this.coyoteRequest.getParameters(); parameters.setLimit(getConnector().getMaxParameterCount()); String enc = getCharacterEncoding(); this.requestParametersParsed = true; if (enc != null) { parameters.setEncoding(enc); parameters.setQueryStringEncoding(enc); } else { parameters.setEncoding("ISO-8859-1"); parameters.setQueryStringEncoding("ISO-8859-1"); } parameters.handleQueryParameters(); if ((this.usingInputStream) || (this.usingReader)) { return; } if (!getMethod().equalsIgnoreCase("POST")) { return; } String contentType = getContentType(); if (contentType == null) { contentType = ""; } int semicolon = contentType.indexOf(';'); if (semicolon >= 0) contentType = contentType.substring(0, semicolon).trim(); else { contentType = contentType.trim(); } if ((isMultipartConfigured()) && ("multipart/form-data".equals(contentType))) { getMultipart().init(); } if (!"application/x-www-form-urlencoded".equals(contentType)) { return; } int len = getContentLength(); if (len > 0) { int maxPostSize = ((Connector)this.connector).getMaxPostSize(); if ((maxPostSize > 0) && (len > maxPostSize)) { log(sm.getString("coyoteRequest.postTooLarge")); throw new IllegalStateException("Post too large"); } try { byte[] formData = getPostBody(); if (formData != null) parameters.processParameters(formData, 0, len); } catch (Throwable t) { } } } } 

 package com.sun.grizzly.tcp; import com.sun.grizzly.util.http.Parameters; public class Request { .... private Parameters parameters = new Parameters(); .... public Parameters getParameters() { return this.parameters; } } 

 package com.sun.grizzly.util.http; public class Parameters { .... public void processParameters(byte[] bytes, int start, int len) { processParameters(bytes, start, len, getCharset(this.encoding)); } public void processParameters(byte[] bytes, int start, int len, Charset charset) { if (debug > 0) { try { log(sm.getString("parameters.bytes", new String(bytes, start, len, "ISO-8859-1"))); } catch (UnsupportedEncodingException e) { logger.log(Level.SEVERE, sm.getString("parameters.convertBytesFail"), e); } } int decodeFailCount = 0; int end = start + len; int pos = start; while (pos < end) { int nameStart = pos; int nameEnd = -1; int valueStart = -1; int valueEnd = -1; boolean parsingName = true; boolean decodeName = false; boolean decodeValue = false; boolean parameterComplete = false; do { switch (bytes[pos]) { case 61: if (parsingName) { nameEnd = pos; parsingName = false; pos++; valueStart = pos; } else { pos++; } break; case 38: if (parsingName) { nameEnd = pos; } else { valueEnd = pos; } parameterComplete = true; pos++; break; case 37: case 43: if (parsingName) decodeName = true; else { decodeValue = true; } pos++; break; default: pos++; } } while ((!parameterComplete) && (pos < end)); if (pos == end) { if (nameEnd == -1) nameEnd = pos; else if ((valueStart > -1) && (valueEnd == -1)) { valueEnd = pos; } } if ((debug > 0) && (valueStart == -1)) { try { log(sm.getString("parameters.noequal", Integer.valueOf(nameStart), Integer.valueOf(nameEnd), new String(bytes, nameStart, nameEnd - nameStart, "ISO-8859-1"))); } catch (UnsupportedEncodingException e) { logger.log(Level.SEVERE, sm.getString("parameters.convertBytesFail"), e); } } if (nameEnd <= nameStart) { if (logger.isLoggable(Level.INFO)) { if (valueEnd >= nameStart) try { new String(bytes, nameStart, valueEnd - nameStart, "ISO-8859-1"); } catch (UnsupportedEncodingException e) { logger.log(Level.SEVERE, sm.getString("parameters.convertBytesFail"), e); } else { logger.fine(sm.getString("parameters.invalidChunk", Integer.valueOf(nameStart), Integer.valueOf(nameEnd), null)); } } } else { this.tmpName.setCharset(charset); this.tmpValue.setCharset(charset); this.tmpName.setBytes(bytes, nameStart, nameEnd - nameStart); this.tmpValue.setBytes(bytes, valueStart, valueEnd - valueStart); if (debug > 0) try { this.origName.append(bytes, nameStart, nameEnd - nameStart); this.origValue.append(bytes, valueStart, valueEnd - valueStart); } catch (IOException ioe) { logger.log(Level.SEVERE, sm.getString("parameters.copyFail"), ioe); } try { String name; String name; if (decodeName) name = urlDecode(this.tmpName); else name = this.tmpName.toString(); String value; String value; if (decodeValue) value = urlDecode(this.tmpValue); else { value = this.tmpValue.toString(); } try { addParameter(name, value); } catch (IllegalStateException ise) { logger.warning(ise.getMessage()); break; } } catch (IOException e) { decodeFailCount++; if ((decodeFailCount == 1) || (debug > 0)) { if (debug > 0) { log(sm.getString("parameters.decodeFail.debug", this.origName.toString(), this.origValue.toString()), e); } else if (logger.isLoggable(Level.INFO)) { logger.log(Level.INFO, sm.getString("parameters.decodeFail.info", this.tmpName.toString(), this.tmpValue.toString()), e); } } } this.tmpName.recycle(); this.tmpValue.recycle(); if (debug > 0) { this.origName.recycle(); this.origValue.recycle(); } } } if ((decodeFailCount > 1) && (debug <= 0)) logger.info(sm.getString("parameters.multipleDecodingFail", Integer.valueOf(decodeFailCount))); 

}