2020-03-02 url中包含類似"|{}"等特殊字符導(dǎo)致請求報400錯誤的解決辦法。

1. 問題

今天測試突然說一個5年前的功能,報400錯誤,并提示如下錯誤信息:

HTTP Status 400 – Bad Request
Type Exception Report

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

Description The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).

Exception

java.lang.IllegalArgumentException: Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
    org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:468)
    org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:260)
    org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860)
    org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1598)
    org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    java.base/java.lang.Thread.run(Thread.java:830)
Note The full stack trace of the root cause is available in the server logs.

2. 解決方案

問題非常明顯,提示已經(jīng)足夠清楚了。
經(jīng)過復(fù)現(xiàn)測試提供的問題,發(fā)現(xiàn)部分老的js代碼沒有遵循規(guī)范,在url的請求參數(shù)中攜帶了原始的沒有經(jīng)過url encode 轉(zhuǎn)碼的json字符串。json字符串中的{}顯然不屬于 RFC 7230 和 RFC 3986 允許的特殊字符。

2.1. 徹底的解決方案

徹底的解決方案顯然應(yīng)該是重構(gòu)代碼,讓代碼遵循規(guī)范。

  • 所有的url參數(shù)都應(yīng)該經(jīng)過url encode,確保不會出現(xiàn)特殊字符
  • 復(fù)雜請求參數(shù)應(yīng)當(dāng)通過 post,放在Request Body中傳遞,而不是 get 的url中顯式傳遞。

2.2. 臨時解決方案

問題已經(jīng)出現(xiàn)。而且經(jīng)過初步檢查,發(fā)現(xiàn)存在類似問題的不規(guī)范老代碼太多,一時半會不能保證能全部修改完畢。那么徹底的解決方案就不可行的。對應(yīng)急處理,還是有辦法的:

2.2.1. 對tomcat6、tomcat7、tomcat8:

在 ${tomcat.dir}/conf/catalina.properties 文件中,找到被注釋調(diào)的參數(shù) tomcat.util.http.parser.HttpParser.requestTargetAllow,取消備注,將該參數(shù)的值指定為:tomcat.util.http.parser.HttpParser.requestTargetAllow=|{}。這樣,“|{}”這三個特殊字符就不再被攔截了。

2.2.2. 對tomcat9:

tomcat9不支持參數(shù) tomcat.util.http.parser.HttpParser.requestTargetAllow,但有類似的配置項。
在 ${tomcat.dir}/conf/server.xml 文件中,找到你使用的Connector,添加參數(shù) relaxedPathChars="|{}" relaxedQueryChars="|{}"。

2.2.3. 對spring-boot的內(nèi)嵌tomcat9以前版本:

如果是內(nèi)嵌的 tomcat9以前版本,可以通過如下代碼配置:

@SpringBootApplication
public class XxApplication {
    public static void main(String[] args) {
        System.setProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow","|{}");
        SpringApplication.run(XxApplication.class, args);
    }
    ...
}    

2.2.4. 對spring-boot內(nèi)嵌的tomcat9:

可以通過類似如下代碼添加Connector的自定義參數(shù):
spring-boot-1.*可以這樣:

@Component
public class MyEmbeddedServletContainerCustomizer implements EmbeddedServletContainerCustomizer {

    @Override
    public void customize(ConfigurableEmbeddedServletContainer container) {
        TomcatEmbeddedServletContainerFactory factory = (TomcatEmbeddedServletContainerFactory) container;
        factory.addConnectorCustomizers((connector) -> {
            connector.setAttribute("relaxedPathChars", "|{}");
            connector.setAttribute("relaxedQueryChars", "|{}");
        });
    }
}

spring-boot-2.*可以這樣

@Component
public class MyTomcatServletWebServerFactoryCustomizer extends TomcatServletWebServerFactoryCustomizer {

    /**
     * 構(gòu)造函數(shù)
     * @param serverProperties
     */
    public MyTomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
        super(serverProperties);
    }

    @Override
    public void customize(TomcatServletWebServerFactory factory) {
        super.customize(factory);
        factory.addConnectorCustomizers((connector) -> {
            connector.setAttribute("relaxedPathChars", "|{}");
            connector.setAttribute("relaxedQueryChars", "|{}");
        });
    }
}

2.3. 最后忠告

  • 當(dāng)然,你可以根據(jù)自己的需要,添加其他參數(shù),但要注意如果添加了類似 “<>”這樣的參數(shù),可能會引發(fā)其他問題,導(dǎo)致服務(wù)器無法啟動,或無法正常工作。
  • 再次強調(diào),這個方法,只是臨時解決方案,用于問題發(fā)生后給自己爭取時間來實施徹底解決方案,不能作為常規(guī)解決方案。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容