使用servlet过滤器从发布的数据中删除表单参数

时间:2013-07-11 18:59:08

标签: java servlets servlet-filters

供应商已经通过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());
        }
    };
  }
}

2 个答案:

答案 0 :(得分: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);

希望它能解决你的问题。

答案 1 :(得分: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-decompiler对于将.class文件转换为.java进行修补非常有用。

  1. 找到进行网址解码的类(对于Glassfish,com.sun.grizzly.util.http.Parameters来自grizzly-utils.jar,如下所示,对于Tomcat,这是来自org.apache.tomcat.util.http.Parameters的{​​{1}}
  2. 将整个源代码复制到新类tomcat-coyote.jar
  3. 找到解码参数值的代码行(下面:somepackage.StrippedParameters
  4. 更改此代码,使其仅在参数名称与所需参数匹配时进行解码:

    value = urlDecode(this.tmpValue)
  5. 现在是棘手的部分:在发生URL解码之前,用类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; } } 替换默认类Parameters。获取参数后,将它们复制回容器类。对于Glassfish,将方法parseRequestParameters复制到HttpServletRequestWrapper的实现中(对于Tomcat,StrippedParmeters中的类parseParameters中的相应方法为org.apache.catalina.connector.Request

    • 替换此行:

      catalina.jar

      使用:

      Parameters parameters = this.coyoteRequest.getParameters();
      
    • 在方法的底部,添加:

      Parameters parameters = new somepackage.StrippedParameters();
      
  6. Glassfish容器代码 - Tomcat的修改版本,Grizzly取代Coyote (Catalina Servlet Engine仍然指的是Coyote对象,但它们是像Coyote一样被嘲笑的Grizzly实例)

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

    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;
        }
    }
    

    }