8

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

我将如何利用 servlet 过滤器来删除除名为 XMLContent 的表单参数之外的所有参数 我已经尝试了各种尝试来显式删除有问题的参数“TContent”,但它从未被删除。

正在接收的数据片段:

XMLContent=%3C%3Fxml+version%3D%221.0%22+encoding%3D%22UTF-8%22%3F%3E%0A%3CCheck+xmlns%3D%22http .........&TContent=<!--?xml version="1.0" encoding="UTF-8"?--><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<String, String[]> parameterMap = new HashMap<String, String[]>();

    // Loop through form parameters.
Enumeration<String> 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<String, String[]> parameterMap)
{
    return new HttpServletRequestWrapper(request) {
        public Map<String, String[]> 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<String> getParameterNames() {
            return Collections.enumeration(parameterMap.keySet());
        }
    };
  }
}
4

2 回答 2

6

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

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

示例包装类:

public class WrappedRequest extends HttpServletRequestWrapper
{
  Map<String, String[]> parameterMap;
  public WrappedRequest(final HttpServletRequest request)
  {
   //build your param Map here with required values
  }

  @Override
  public Map<String, String[]> getParameterMap()
  {
    //return local param map
  }

  //override other methods if needed.

}

现在在您的过滤器代码中,执行以下操作。

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

希望它能解决你的问题。

于 2013-07-19T10:08:32.780 回答
5

方法

代码遵循正确的方法:

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

    • public String getParameter(String name)
    • public Map<String, String[]> getParameterMap()
    • public Enumeration<String> getParameterNames()
    • public String[] getParameterValues(String name)
  • doFilter()方法使用包装的请求调用过滤器链,这意味着后续过滤器以及目标 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 反编译器对于将 .class 文件转换为 .java 的修补很有用。

  1. 找到进行 URL 解码的类(对于 Glassfish,这是com.sun.grizzly.util.http.Parameters来自grizzly-utils.jar如下所示,对于 Tomcat,这是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 解码发生之前Parameters用您的类替换默认类。StrippedParmeters获取参数后,将它们复制回容器类。对于 Glassfish,将方法 parseRequestParameters 复制到您的 HttpServletRequestWrapper 实现中(对于 Tomcat,相应的方法parseParameters在类org.apache.catalina.connector.Requestcatalina.jar

    • 替换这一行:

      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 引擎仍然引用 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)));

}

于 2013-07-23T07:40:53.087 回答