tomcat容器下 http 请求参数中特殊字符(+、&、%)的问题

xiaoxiao2021-02-28  76

一、现象

 大家在java web 开发过程中,应该遇到过请求参数中有特殊字符导致后台获取参数时异常的情况。

     参数中有 + : 比如参数是 voteless+cz ,在后台用 request.getParameter(); 获取时,发现变成了 voteless cz。就是 + 变成了 空格;

     参数中有& : 比如参数是  voteless&cz,在后台用 request.getParameter(); 获取时,发现变成了 voteless。就是 &和之后的参数会丢失。

     参数中有%: 比如参数是  voteless%cz,在后台用 request.getParameter(); 获取时,发现变成了 voteless�。%和后面的两个字符会变成乱码

     参数中有%: 比如参数是  voteless%c,在后台用 request.getParameter(); 获取时,发现是null。获取不到

 

二、原因

      原因是tomcat容器会丢对请求的参数(QueryString)进行类似 URLDecoder.decode的操作(GET和POST的请求都会,POST是 application/x-www-form-urlencoded )。

      1、&问题

         大家都知道&的目的是分隔 key=value。而value中有& 的话,其实很自然可以想到value中 & 后面的字符串会丢失,大家看如下的源码片段就明白了。

      org.apache.tomcat.util.http.Parameters 的 processParameters(byte bytes[], int start, int len, Charset charset) 方法

 

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 '=': if (parsingName) { // Name finished. Value starts from next character nameEnd = pos; parsingName = false; valueStart = ++pos; } else { // Equals character in value pos++; } break; case '&': if (parsingName) { // 重点在这个地方,value中&后面的字符串会当做是没有value的key // Name finished. No value. nameEnd = pos; } else { // Value finished valueEnd = pos; } parameterComplete = true; pos++; break; case '%': case '+': // Decoding required if (parsingName) { decodeName = true; } else { decodeValue = true; } pos ++; break; default: pos ++; break; } } while (!parameterComplete && pos < end); if (pos == end) { if (nameEnd == -1) { nameEnd = pos; } else if (valueStart > -1 && valueEnd == -1){ valueEnd = pos; } }

 

 

 

      2、+和 % 问题

 

         上面提到tomcat会对参数进行解码,解码部分的源码如下  org.apache.tomcat.util.buf.UDecoder 的 String convert(String str, boolean query)

while (strPos < strLen) { int laPos; // lookahead position // look ahead to next URLencoded metacharacter, if any for (laPos = strPos; laPos < strLen; laPos++) { char laChar = str.charAt(laPos); if ((laChar == '+' && query) || (laChar == '%')) { break; } } // if there were non-metacharacters, copy them all as a block if (laPos > strPos) { dec.append(str.substring(strPos,laPos)); strPos = laPos; } // shortcut out of here if we're at the end of the string if (strPos >= strLen) { break; } // process next metacharacter char metaChar = str.charAt(strPos); if (metaChar == '+') { dec.append(' '); strPos++; continue; } else if (metaChar == '%') { // We throw the original exception - the super will deal with // it // try { char res = (char) Integer.parseInt( str.substring(strPos + 1, strPos + 3), 16); if (noSlash && (res == '/')) { throw new IllegalArgumentException("noSlash"); } dec.append(res); strPos += 3; } }

 

 

 

      如上代码可以很明显看到 + 为什么变成了 空格;%部分可以看到代码截取%后的两位字符,然后放到StringBuilder,自如如上面例子中 voteless%cz,会将cz 以16进制转为 char ,得到的是怪怪的字符;如果是 voteless%c ,%后面只有一位,就会导致异常,使得获取不到value值(null)。

 

  

 

转载请注明原文地址: https://www.6miu.com/read-83209.html

最新回复(0)