java 使用 servlet 过滤器从发布的数据中删除表单参数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17601279/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Use servlet filter to remove a form parameter from posted data
提问by Matt
A vendor has been posting XML data over HTTPS within a form variable named XMLContent to my Coldfusion application server. I recently moved to a more recent version of the application server and those requests are throwing 500 server errors. It is throwing the error because a second form parameter's content is not properly urlencoded, but I don't need that parameter anyway. (I contacted the vendor to fix this but they are forcing me to pay to fix their mistake so I'm looking to fix it myself if possible.)
一个供应商一直在一个名为 XMLContent 的表单变量中通过 HTTPS 将 XML 数据发布到我的 Coldfusion 应用服务器。我最近转移到更新版本的应用程序服务器,这些请求引发了 500 个服务器错误。它抛出错误是因为第二个表单参数的内容未正确进行 urlencoded,但无论如何我都不需要该参数。(我联系了供应商来解决这个问题,但他们强迫我付费来修复他们的错误,所以如果可能的话,我希望自己修复它。)
How would I utilize a servlet filter to remove all but the form parameter named: XMLContent I have tried various attempts to explicitly remove the offending parameter "TContent" but it never gets removed.
我将如何利用 servlet 过滤器删除除名为 XMLContent 的表单参数之外的所有内容我尝试了各种尝试来明确删除有问题的参数“TContent”,但它从未被删除。
A snippet of the data being received:
正在接收的数据片段:
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...........
The code I've tried:
我试过的代码:
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());
}
};
}
}
回答by ajgautam
We face these situations every day while handling locale. If user locale is different than browser locale, we have to update the request. But its not mutable.
我们在处理语言环境时每天都会遇到这些情况。如果用户区域设置与浏览器区域设置不同,我们必须更新请求。但它不是可变的。
Solution: create a request wrapper class. Copy existing request parameters (the ones you want) into it and pass this wrapper class in filterchain.doFilter()
解决方案:创建一个请求包装类。将现有的请求参数(你想要的)复制到其中并在 filterchain.doFilter() 中传递这个包装类
sample wrapper class:
示例包装类:
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.
}
Now in your filter code, do following.
现在在您的过滤器代码中,执行以下操作。
wrapRequest = new WrappedRequest(hreq);
filterChain.doFilter(wrapRequest, servletResponse);
Hopefully it will solve your problem.
希望它能解决你的问题。
回答by Glen Best
Approach
方法
The code follows the correct approach:
代码遵循正确的方法:
in
wrapRequest()
, it instantiatesHttpServletRequestWrapper
and overrides the 4 methods that trigger request parsing:public String getParameter(String name)
public Map<String, String[]> getParameterMap()
public Enumeration<String> getParameterNames()
public String[] getParameterValues(String name)
the
doFilter()
method invokes the filter chain using the wrapped request, meaning subsequent filters, plus the target servlet (URL-mapped) will be supplied the wrapped request.
在 中
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 映射)将被提供包装的请求。
Problem
问题
- Calling any of the 4 methods getParameterXXX() methods on the underlying 'original request' triggers implicit parsing of allrequest parameters (via a server internal method such as
Request.parseRequestParameters or parsePameters
). These 4 methods are the only way to trigger such parsing. Before the request is wrapped, in
parseRequest()
, your code calls such methods on the underlying request:request.getParameterNames(); request.getParameterValues(paramName);
- 在底层“原始请求”上调用 4 个方法 getParameterXXX() 中的任何一个都会触发所有请求参数的隐式解析(通过服务器内部方法,例如
Request.parseRequestParameters or parsePameters
)。这4种方法是触发这种解析的唯一方法。 在请求被包装之前,在 中
parseRequest()
,您的代码在底层请求上调用这些方法:request.getParameterNames(); request.getParameterValues(paramName);
Solution
解决方案
You need to control the parsing logic. Reading raw bytes from the input stream and doing your own URL decoding is too complex - it would mean replacing a large volume of tightly-coupled server code. Instead, the simplest approach is to just replace the method that does the actual URL decoding. That means you can leave in place the request.getParameterXXX
calls mentioned in prev section.
您需要控制解析逻辑。从输入流中读取原始字节并进行自己的 URL 解码太复杂了——这意味着要替换大量紧密耦合的服务器代码。相反,最简单的方法是替换进行实际 URL 解码的方法。这意味着您可以保留上request.getParameterXXX
一节中提到的调用。
Not sure what server you're hosting ColdFusion on, but following description is based on Glassfish/Tomcat, but can be adapted. At the bottom of this post is the internal request parsing method from Glassfish (a modified version of Tomcat). JD-decompileris useful for this tinkering for converting .class files to .java.
不确定您在哪个服务器上托管 ColdFusion,但以下描述基于 Glassfish/Tomcat,但可以进行调整。这篇文章的底部是来自Glassfish(Tomcat的修改版)的内部请求解析方法。JD-decompiler对于将 .class 文件转换为 .java 的这种修补很有用。
- Find the class that does URL decoding (for Glassfish this is
com.sun.grizzly.util.http.Parameters
fromgrizzly-utils.jar
as shown below, for Tomcat this isorg.apache.tomcat.util.http.Parameters
fromtomcat-coyote.jar
) - Copy the entire source code to a new class
somepackage.StrippedParameters
- Find the line of code that decodes the parameter value (below:
value = urlDecode(this.tmpValue)
) Change this code so that it only decodes when the parameter name matches your desired parameter:
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; } }
Now the tricky part: replace the default class
Parameters
with your classStrippedParmeters
just before URL decoding occurs. Once the parameters are obtained, copy them back to the container class. For Glassfish copy the method parseRequestParameters into your implementation of HttpServletRequestWrapper (for Tomcat, the corresponding method isparseParameters
in classorg.apache.catalina.connector.Request
incatalina.jar
)replace this line:
Parameters parameters = this.coyoteRequest.getParameters();
with:
Parameters parameters = new somepackage.StrippedParameters();
at the bottom of the method, add this:
Parameters coyoteParameters = this.coyoteRequest.getParameters(); for (String paramName : parameters.getParameterNames()) { String paramValue = parameters.getParameterValue(paramName); coyoteParameters.addParameter(paramName, paramValue); }
- 找到进行 URL 解码的类(Glassfish
com.sun.grizzly.util.http.Parameters
来自grizzly-utils.jar
如下图,Tomcatorg.apache.tomcat.util.http.Parameters
来自tomcat-coyote.jar
) - 将整个源代码复制到一个新类
somepackage.StrippedParameters
- 找到的代码,所述参数值进行解码的线(在下面:
value = urlDecode(this.tmpValue)
) 更改此代码,使其仅在参数名称与所需参数匹配时才解码:
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; } }
现在是棘手的部分:在 URL 解码发生之前
Parameters
用您的类替换默认类StrippedParmeters
。获取参数后,将它们复制回容器类。对于 Glassfish,将方法 parseRequestParameters 复制到您的 HttpServletRequestWrapper 实现中(对于 Tomcat,相应的方法parseParameters
在类org.apache.catalina.connector.Request
中catalina.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 Container Code- Modified Version of Tomcat, with Grizzly replacing Coyote (Catalina Servlet Engine still refers to Coyote objects, but they are Grizzly instances mocked like Coyote)
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)));
}
}