springboot 1.x 2.x tomcat支持特殊字符,URL中有{}[]等報400

springboot 1.x 2.x tomcat支持特殊字符

現象

正常訪問一個get請求,頁面返回400:
在這裏插入圖片描述
後臺日誌報錯:

2018-08-09 21:39:28.915  INFO 6750 --- [nio-8080-exec-1] o.apache.coyote.http11.Http11Processor   : Error parsing HTTP request header
 Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.

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) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:684) ~[tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:800) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1471) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_111]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_111]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.32.jar:8.5.32]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_111]

稍微百度一下就可以知道這是URL中有特殊字符,新版本的Tomcat嚴格按照RFC 3986規範進行訪問解析,而 RFC 3986規範規定Url中只允許包含英文字母(a-zA-Z)、數字(0-9)、-_.~4個特殊字符以及所有保留字符(RFC3986中指定了以下字符爲保留字符:! * ’ ( ) ; : @ & = + $ , / ? # [ ])

所以這個問題特別容易出現在升級spring boot版本的時候,spring boot內嵌的tomcat也會升級,老版的tomcat運行正常,新版的tomcat就會出錯。而深究特殊字符來源,一般是get請求中包含json字符串、搜索特殊字符關鍵字等。

解決方案

如果是在開發新業務過程中出現這個問題,可以選擇新的方案,避免在GET請求中使用! * ’ ( ) ; : @ & = + $ , / ? # [ ])等字符,畢竟符合規範是最好的出路。

如果是升級,可以使用下面的方式來解決:

sprintboot 1.x(1.5.21測試有效)

import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Create by IntelliJ IDEA
 *
 * @author chenlei
 * @dateTime 2019/5/23 18:09
 * @description TomcatConfig
 */
@Configuration
public class TomcatConfig {

    @Bean
    public EmbeddedServletContainerCustomizer containerCustomizer() {
        return new MyCustomizer();
    }

    private static class MyCustomizer implements EmbeddedServletContainerCustomizer {

        @Override
        public void customize(ConfigurableEmbeddedServletContainer factory) {
            if (factory instanceof TomcatEmbeddedServletContainerFactory) {
                customizeTomcat((TomcatEmbeddedServletContainerFactory) factory);
            }
        }

        void customizeTomcat(TomcatEmbeddedServletContainerFactory factory) {
            factory.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> {
                connector.setAttribute("relaxedPathChars", "<>[\\]^`{|}");
                connector.setAttribute("relaxedQueryChars", "<>[\\]^`{|}");
            });
        }

    }
}

springboot 2.x(2.1.3測試有效)

import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Create by IntelliJ IDEA
 *
 * @author chenlei
 * @dateTime 2019/5/23 18:09
 * @description TomcatConfig
 */
@Configuration
public class TomcatConfig {

    @Bean
    public ServletWebServerFactory webServerFactory() {
        TomcatServletWebServerFactory fa = new TomcatServletWebServerFactory();
        fa.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> connector.setProperty("relaxedQueryChars", "[]{}"));
        return fa;
    }

}

總結

這次問題出現的原因是升級springboot導致的,因爲之前使用的較低版本的springboot(1.5.10.RELEASE),升級到1.5.21.RELEASE後出現了該問題。因爲之前在springboot 2.x上遇到過這個問題,因此知道問題所在,但springboot 1.x和2.x的解決方案有一點差異,這裏記錄一下。

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