途径
代码遵循正确的方法:
在wrapRequest() ,它实例化HttpServletRequestWrapper并覆盖触发请求解析的4个方法:
public String getParameter(String name)
public Map getParameterMap()
public Enumeration getParameterNames()
public String[] getParameterValues(String name)
doFilter()方法使用包装请求调用过滤器链,这意味着后续过滤器和目标servlet(URL映射)将提供包装请求。
问题
在底层“原始请求”上调用4个方法中的任何一个getParameterXXX()方法会触发所有请求参数的隐式解析(通过服务器内部方法,如Request.parseRequestParameters or parsePameters )。 这4种方法是触发此类解析的唯一方法。
在请求被包装之前,在parseRequest() ,您的代码在底层请求上调用此类方法:request.getParameterNames(); request.getParameterValues(paramName);
解
您需要控制解析逻辑。 从输入流中读取原始字节并进行自己的URL解码过于复杂 - 这意味着要替换大量紧密耦合的服务器代码。 相反,最简单的方法是只替换执行实际URL解码的方法。 这意味着您可以保留prev节中提到的request.getParameterXXX调用。
不确定您正在托管ColdFusion的服务器,但以下描述基于Glassfish / Tomcat,但可以进行调整。 这篇文章的底部是Glassfish的内部请求解析方法(Tomcat的修改版本)。 JD-decompiler对于将.class文件转换为.java的修补非常有用。
找到进行URL解码的类(对于Glassfish,这是来自grizzly-utils.jar com.sun.grizzly.util.http.Parameters ,如下所示,对于Tomcat,这是来自tomcat-coyote.jar org.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解码发生之前,使用您的类StrippedParmeters替换默认类Parameters 。 获取参数后,将它们复制回容器类。 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容器代码 - 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)));
}