1. 問題
今天測試使用get請求參數合同編號包含一些特殊字符([]或者{}),報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. 解決方案
問題非常明顯,提示已經足夠清楚了。
經過復現測試提供的問題,發現部分老的js代碼沒有遵循規范,在url的請求參數中攜帶了原始的沒有經過url encode 轉碼的json字符串。json字符串中的{}顯然不屬于 RFC 7230 和 RFC 3986 允許的特殊字符。
2.1. 徹底的解決方案
徹底的解決方案顯然應該是重構代碼,讓代碼遵循規范。
所有的url參數都應該經過url encode,確保不會出現特殊字符
復雜請求參數應當通過 post,放在Request Body中傳遞,而不是 get 的url中顯式傳遞。
2.2. 臨時解決方案
問題已經出現。而且經過初步檢查,發現存在類似問題的不規范老代碼太多,一時半會不能保證能全部修改完畢。那么徹底的解決方案就不可行的。對應急處理,還是有辦法的:
2.2.1. 對tomcat6、tomcat7、tomcat8:
在 ${tomcat.dir}/conf/catalina.properties 文件中,找到被注釋調的參數tomcat.util.http.parser.HttpParser.requestTargetAllow,取消備注,將該參數的值指定為:tomcat.util.http.parser.HttpParser.requestTargetAllow=|{}。這樣,“|{}”這三個特殊字符就不再被攔截了。
2.2.2. 對tomcat9:
tomcat9不支持參數tomcat.util.http.parser.HttpParser.requestTargetAllow,但有類似的配置項。
在 ${tomcat.dir}/conf/server.xml 文件中,找到你使用的Connector,添加參數relaxedPathChars="|{}" relaxedQueryChars="|{}"。
2.2.3. 對spring-boot的內嵌tomcat9以前版本:
如果是內嵌的 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內嵌的tomcat9:
可以通過類似如下代碼添加Connector的自定義參數:
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 {? ? ? public MyTomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
super(serverProperties); }
@Override public void customize(TomcatServletWebServerFactory factory) {
super.customize(factory);
factory.addConnectorCustomizers((connector) -> {
connector.setAttribute("relaxedPathChars", "|{}");
connector.setAttribute("relaxedQueryChars", "|{}");
});
}
}