天天看點

Java程式員注意:Tomcat Get請求的巨坑!:通常用于表示書簽或者錨點

image

Tomcat8.5,當Get請求中包含了未經編碼的中文字元時,會報以下錯誤,請求未到應用程式在Tomcat層就被攔截了。

Tomcat報錯:

java.lang.IllegalArgumentException: Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986

傳回400錯誤:

Transfer-Encoding--->[chunked]

null--->[HTTP/1.1 400 Bad Request]

Server--->[Apache-Coyote/1.1]

Connection--->[close]

Date--->[Wed, 07 Feb 2018 03:19:04 GMT]

根據錯誤找到了Tomcat最新的源碼:

org/apache/coyote/http11/LocalStrings.properties

iib.invalidRequestTarget=Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
           

org/apache/coyote/http11/Http11InputBuffer.java

boolean parseRequestLine(boolean keptAlive) throws IOException {

...
 
    } else if (HttpParser.isNotRequestTarget(chr)) {
        throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
    }

...

}
           

java/org/apache/tomcat/util/http/parser/HttpParser.java

public static boolean isNotRequestTarget(int c) {
    // Fast for valid request target characters, slower for some incorrect
    // ones
    try {
        return IS_NOT_REQUEST_TARGET[c];
    } catch (ArrayIndexOutOfBoundsException ex) {
        return true;
    }
}
           

查源碼發現在Tomcat7.0.73就已經添加了RFC 3986這個規範。

RFC 3986文檔對Url的編解碼問題做出了詳細的建議,指出了哪些字元需要被編碼才不會引起Url語義的轉變,以及對為什麼這些字元需要編碼做出了相應的解釋。

RFC 3986文檔規定,Url中隻允許包含英文字母(a-zA-Z)、數字(0-9)、-_.~4個特殊字元以及所有保留字元(! * ' ( ) ; : @ & = + $ , / ? # [ ])。

還有一些字元當直接放在Url中的時候,可能會引起解析程式的歧義,這些字元被視為不安全字元。

  • 空格:Url在傳輸的過程,或者使用者在排版的過程,或者文本處理程式在處理Url的過程,都有可能引入無關緊要的空格,或者将那些有意義的空格給去掉。
  • 引号以及<>:引号和尖括号通常用于在普通文本中起到分隔Url的作用
  • :通常用于表示書簽或者錨點

  • %:百分号本身用作對不安全字元進行編碼時使用的特殊字元,是以本身需要編碼
  • {}|^[]`~:某一些網關或者傳輸代理會篡改這些字元

對于此問題,有以下幾種解決方案。

1、切換版本到7.0.73以下,這個不實際。

2、修改Tomcat源碼,這個也不實際。

3、前端請求對URL編碼。

4、修改Get方法為Post方法。

5、因{}是不安全字元,預設被 tomcat攔截。如果需要在URL中傳輸json資料,在catalina.properties中添加支援。

tomcat.util.http.parser.HttpParser.requestTargetAllow=|{}

總結

如果Get請求在合作方,而合作方不願意修改代碼,那1、2種方法可以嘗試。如果Get請求在自己,可以嘗試3、4種方法。僅需要在URL上傳輸json資料,使用第5種方法即可。

推薦:

成為架構師的十階段學習資料

如果對你有用,歡迎分享到朋友圈