一、背景
事情是這樣的,前幾天做一個基本的數據庫“增刪改查”的需求,前端傳參的方式是“JSON字符串”,后端接收到此參數后,使用阿里巴巴fastjson進行解析,然后入庫。需求很簡單吧,但是偏偏遇到問題了。
我發現,JSON字符串里面無數組,純粹的都是json結構的時候,即都是“{}”時,不會報錯,傳參入庫沒問題。但是只要傳參的值里面有數組,即有“[]”的結構時,就報錯。報錯內容如下(我的tomcat版本是8.5.45):
java.lang.IllegalArgumentException: Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986 at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:479) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:684) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:800) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1471) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:748)
二、原因
tomcat的原因。 tomcat嚴格按照RFC規范進行范文解析,隨着網絡環境的變化,RFC規范也在不斷的修改和升級中,發布了好多版本。而tomcat的不同版本中,采用的RFC規范的版本是不同的。所以你會在下文發現,有的低版本tomcat沒有這個問題。
tomcat自tomcat 8.0.35版本之后對URL參數做了比較規范的限制,必須遵循RFC 7230 and RFC 3986規范,對於非保留字字符(json格式的請求參數)必須做轉義操作。例如:RFC 3986規范定義了Url中只允許包含英文字母(a-zA-Z)、數字(0-9)、-_.~4個特殊字符以及所有保留字符(RFC3986中指定了以下字符為保留字符:! * ’ ( ) ; : @ & = + $ , / ? # [ ])。
Request For Comments(RFC),是一系列以編號排定的文件。文件收集了有關互聯網相關信息,以及UNIX和互聯網社區的軟件文件。目前RFC文件是由Internet Society(ISOC)贊助發行。基本的互聯網通信協議都有在RFC文件內詳細說明。RFC文件還額外加入許多在標准內的論題,例如對於互聯網新開發的協議及發展中所有的記錄。因此幾乎所有的互聯網標准都有收錄在RFC文件之中——百度百科。
附上網絡大牛的源碼分析:
分析的是org.apache.tomcat.util.http.parser.HttpParser //tomcat 8.2.3 版本及 tomcat 7.0.82 ,都有如下代碼,讀取配置 String prop = System.getProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow"); if (prop != null) { for (int i = 0; i < prop.length(); i++) { char c = prop.charAt(i); if (c == '{' || c == '}' || c == '|') { REQUEST_TARGET_ALLOW[c] = true; } else { log.warn(sm.getString("httpparser.invalidRequestTargetCharacter",Character.valueOf(c))); } } } 而tomcat 8.0.14 版本中並沒有讀取配置,對 | { } 的處理,而是默認為合法字符。 static { for (int i = 0; i < 128; i++) { if (i < 32) { isToken[i] = false; } else if (i == '(' || i == ')' || i == '<' || i == '>' || i == '@' || i == ',' || i == ';' || i == ':' || i == '\\' || i == '\"' || i == '/' || i == '[' || i == ']' || i == '?' || i == '=' || i == '{' || i == '}' || i == ' ' || i == '\t') { isToken[i] = false; } else { isToken[i] = true; } if (i >= '0' && i <= '9' || i >= 'A' && i <= 'F' ||i >= 'a' && i <= 'f') { isHex[i] = true; } else { isHex[i] = false; } } } 可以看出在 8.0.x 左右的一些版本中,tomcat.util.http.parser.HttpParser. requestTargetAllow (下文方法三)這個配置是沒有生效的,即| { } 這3個符號認為是合法的。
三、解決
注:我是使用“方法五”解決問題的,推薦“方法五”。
方法一:換到低版本的Tomcat。
方法二:在Catalina.properties中添加tomcat.util.http.parser.HttpParser.requestTargetAllow=|{}這個東西明顯是允許“|”和大括號的,但是我現在的問題是中括號。
方法三:添加tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true這個是允許url中帶有特殊字符的。試過了,也不好使。
方法四:對傳遞的“JSON字符串”進行url編碼后在傳遞,可以規避這個方括號。前端用“encodeURI(xxx)”方法編碼,后端用“URLDecoder.decode(xxx, "utf-8")”解碼即可。
方法五:在tomcat目錄的conf文件夾下,server.xml的Connector中添加了這個relaxedQueryChars="[,]"。
注:
1、如果還有其他特殊的字符串,也可以直接添加到這個屬性里;
2、如果你是springboot項目,可以在SpringBootApplication的的main方法中增加:System.setProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow","[]");
參考
1、https://blog.csdn.net/Hitler698/article/details/85720156
2、https://my.oschina.net/pding/blog/1794176