解決Invalid c found in the request targetThe valid characters are defined in RFC 7230 and RFC 3986

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:806)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)
 

一、爲什麼要編碼轉義

通常如果一樣東西需要編碼,說明這樣東西並不適合傳輸。原因多種多樣,如Size過大,包含隱私數據,對於Url來說,之所以要進行編碼,是因爲Url中有些字符會引起歧義。

例如Url參數字符串中使用key=value鍵值對這樣的形式來傳參,鍵值對之間以&符號分隔,如/s?q=abc&ie=utf-8。如果你的value字符串中包含了=或者&,那麼勢必會造成接收Url的服務器解析錯誤,因此必須將引起歧義的&和=符號進行轉義,也就是對其進行編碼。

又如,Url的編碼格式採用的是ASCII碼,而不是Unicode,這也就是說你不能在Url中包含任何非ASCII字符,例如中文。否則如果客戶端瀏覽器和服務端瀏覽器支持的字符集不同的情況下,中文可能會造成問題。

Url編碼的原則就是使用安全的字符(沒有特殊用途或者特殊意義的可打印字符)去表示那些不安全的字符。

 

 

二、哪些字符需要編碼

 

1、URL特殊字符轉義,URL中一些字符的特殊含義,基本編碼規則如下:

1、空格換成加號(+)
2、正斜槓(/)分隔目錄和子目錄
3、問號(?)分隔URL和查詢
4、百分號(%)制定特殊字符
5、#號指定書籤
6、&號分隔參數

 

2、不需要編碼的字符:

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

1、在US-ASCII字符集中沒有的可打印字符:Url中只允許使用可打印字符。US-ASCII碼中的10-7F字節全都表示控制字符,這些字符都不能直接出現在Url中。同時,對於80-FF字節(ISO-8859-1),由於已經超出了US-ACII定義的字節範圍,因此也不可以放在Url中。

2、保留字符:Url可以劃分成若干個組件,協議、主機、路徑等。有一些字符(:/?#[]@)是用作分隔不同組件的。例如:冒號用於分隔協議和主機,/用於分隔主機和路徑,?用於分隔路徑和查詢參數,等等。還有一些字符(!$&'()*+,;=)用於在每個組件中起到分隔作用的,如=用於表示查詢參數中的鍵值對,&符號用於分隔查詢多個鍵值對。當組件中的普通數據包含這些特殊字符時,需要對其進行編碼。

 

RFC3986文檔規定,Url中只允許包含以下四種:

                   1、英文字母(a-zA-Z)

                   2、數字(0-9)

                   3、-_.~ 4個特殊字符

                   4、所有保留字符,RFC3986中指定了以下字符爲保留字符(英文字符):     ! * ' ( ) ; : @ & = + $ , / ? # [ ]

 

3、需要編碼的字符:

如果需要在URL中用到特殊字符,需要將這些特殊字符換成相應的十六進制的值

 

不安全字符:有一些字符,當他們直接放在Url中的時候,可能會引起解析程序的歧義。這些字符被視爲不安全字符,原因有很多。

  • 空格:Url在傳輸的過程,或者用戶在排版的過程,或者文本處理程序在處理Url的過程,都有可能引入無關緊要的空格,或者將那些有意義的空格給去掉。
  • 引號以及<>:引號和尖括號通常用於在普通文本中起到分隔Url的作用
  • #:通常用於表示書籤或者錨點
  • %:百分號本身用作對不安全字符進行編碼時使用的特殊字符,因此本身需要編碼
  • {}|\^[]`~:某一些網關或者傳輸代理會篡改這些字符

需要注意的是,對於Url中的合法字符,編碼和不編碼是等價的,但是對於上面提到的這些字符,如果不經過編碼,那麼它們有可能會造成Url語義的不同。因此對於Url而言,只有普通英文字符和數字,特殊字符$-_.+!*'()還有保留字符,才能出現在未經編碼的Url之中。其他字符均需要經過編碼之後才能出現在Url中。

但是由於歷史原因,目前尚存在一些不標準的編碼實現。例如對於~符號,雖然RFC3986文檔規定,對於波浪符號~,不需要進行Url編碼,但是還是有很多老的網關或者傳輸代理會。

 

 

三、如何對Url中的非法字符進行編碼

Url編碼通常也被稱爲百分號編碼(Url Encoding,also known as percent-encoding),是因爲它的編碼方式非常簡單,使用%百分號加上兩位的字符——0123456789ABCDEF——代表一個字節的十六進制形式。Url編碼默認使用的字符集是US-ASCII。例如a在US-ASCII碼中對應的字節是0x61,那麼Url編碼之後得到的就是%61,我們在地址欄上輸入http://g.cn/search?q=%61%62%63,實際上就等同於在google上搜索abc了。又如@符號在ASCII字符集中對應的字節爲0x40,經過Url編碼之後得到的是%40。

對於非ASCII字符,需要使用ASCII字符集的超集進行編碼得到相應的字節,然後對每個字節執行百分號編碼。對於Unicode字符,RFC文檔建議使用utf-8對其進行編碼得到相應的字節,然後對每個字節執行百分號編碼。如"中文"使用UTF-8字符集得到的字節爲0xE4 0xB8 0xAD 0xE6 0x96 0x87,經過Url編碼之後得到"%E4%B8%AD%E6%96%87"。

如果某個字節對應着ASCII字符集中的某個非保留字符,則此字節無需使用百分號表示。例如"Url編碼",使用UTF-8編碼得到的字節是0x55 0x72 0x6C 0xE7 0xBC 0x96 0xE7 0xA0 0x81,由於前三個字節對應着ASCII中的非保留字符"Url",因此這三個字節可以用非保留字符"Url"表示。最終的Url編碼可以簡化成"Url%E7%BC%96%E7%A0%81" ,當然,如果你用"%55%72%6C%E7%BC%96%E7%A0%81"也是可以的。

 

列舉帶有特殊字符的參數替換成另一些代替的參數,如下所示 

字符    -    URL編碼值

空格    -    %20 (URL中的空格可以用+號或者編碼值表示)
"          -    %22
#         -    %23
%        -    %25
&         -    %26
(          -    %28
)          -    %29
+         -    %2B
,          -    %2C
/          -    %2F
:          -    %3A
;          -    %3B
<         -    %3C
=         -    %3D
>         -    %3E
?         -    %3F
@       -    %40
\          -    %5C
|          -    %7C 

{          -    %7B

}          -    %7D

 

 

四、具體編碼處理方法

 

用URLEncode先對你原始url做個編碼,然後使用編碼後的String。

encodeURIComponent(JSON.stringify(files))  加一下encodeURIComponen 處理即可。

 

 web端:Javascript的escape(),encodeURIComponent(),encodeURI ()這三個函數進行URL編碼,防止特殊字符接收不到。

 Java代碼:

 

 

 

五、案例

在將tomcat升級到7.0.81版後,發現系統的有些功能不能使用了,查詢日誌發現是有些地址直接被tomcat認爲存在不合法字符,返回HTTP 400錯誤響應,錯入信息如下:

1、原因分析

經瞭解,這個問題是高版本tomcat中的新特性:就是嚴格按照 RFC 3986規範進行訪問解析,而 RFC 3986規範定義了Url中只允許包含英文字母(a-zA-Z)、數字(0-9)、-_.~4個特殊字符以及所有保留字符(RFC3986中指定了以下字符爲保留字符:! * ’ ( ) ; : @ & = + $ , / ? # [ ])。而我們的系統在通過地址傳參時,在url中傳了一段json,傳入的參數中有"{"不在RFC3986中的保留字段中,所以會報這個錯。

根據(https://bz.apache.org/bugzilla/show_bug.cgi?id=60594) ,從以下版本開始,有配置項能夠關閉/配置這個行爲:
8.5.x系列的:8.5.12 onwards
8.0.x系列的:8.0.42 onwards
7.0.x系列的:7.0.76 onwards

 

2、處理方法

.../conf/catalina.properties中,找到最後註釋掉的一行 #tomcat.util.http.parser.HttpParser.requestTargetAllow=|  ,改成tomcat.util.http.parser.HttpParser.requestTargetAllow=|{},表示把{}放行

Ps:當然也可以在參數傳遞前對URL先編碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章