解決springboot URL帶有斜杠的轉義字符百分之2F導致的400錯誤
springboot URL帶有斜杠的轉義字符百分之2F導致的400錯誤
今天項目上出現(xiàn)一個問題,是前端的GET請求url中帶有路徑參數(shù),這個參數(shù)中有/這個特殊字符,前端已經(jīng)轉移成了%2F,后端用的是springboot,并沒有收到這個請求,直接返回了400的錯誤
原因
據(jù)說是tomcat默認是不支持轉義的,需要手動設置一下轉化,這個搜索tomcat的設置可以找到,但是這個是springboot,有內(nèi)置的tomcat,但是在yml中找不到相關的配置。
解決方式
修改一下啟動類,加一個系統(tǒng)參數(shù),重寫WebMvcConfigurerAdapter的configurePathMatch方法
@SpringBootApplication public class Application extends WebMvcConfigurerAdapter { public static void main(String[] args) throws Exception { System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true"); SpringApplication.run(Application.class, args); } @Override public void configurePathMatch(PathMatchConfigurer configurer) { UrlPathHelper urlPathHelper = new UrlPathHelper(); urlPathHelper.setUrlDecode(false); configurer.setUrlPathHelper(urlPathHelper); } }
springboot 1.x 2.x tomcat支持特殊字符
URL中有{}[]等報400
現(xiàn)象
正常訪問一個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規(guī)范進行訪問解析,而 RFC 3986規(guī)范規(guī)定Url中只允許包含英文字母(a-zA-Z)、數(shù)字(0-9)、-_.~4個特殊字符以及所有保留字符(RFC3986/7320中指定了以下字符為保留字符:! * ' ( ) ; : @ & = + $ , / ? # [ ]) 。
3.2.6. Field Value Components Most HTTP header field values are defined using common syntax components (token, quoted-string, and comment) separated by whitespace or specific delimiting characters. Delimiters are chosen from the set of US-ASCII visual characters not allowed in a token (DQUOTE and “(),/:;<=>?@[]{}”).
所以這個問題特別容易出現(xiàn)在升級spring boot版本的時候,spring boot內(nèi)嵌的tomcat也會升級,老版的tomcat運行正常,新版的tomcat就會出錯。而深究特殊字符來源,一般是get請求中包含json字符串、搜索特殊字符關鍵字等。
解決方案
如果是在開發(fā)新業(yè)務過程中出現(xiàn)這個問題,可以選擇新的方案,避免在GET請求中使用! * ' ( ) ; : @ & = + $ , / ? # [ ])等字符,畢竟符合規(guī)范是最好的出路。
如果是升級,可以使用下面的方式來解決:
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; } }
總結
這次問題出現(xiàn)的原因是升級springboot導致的,因為之前使用的較低版本的springboot(1.5.10.RELEASE),升級到1.5.21.RELEASE后出現(xiàn)了該問題。因為之前在springboot 2.x上遇到過這個問題,因此知道問題所在,但springboot 1.x和2.x的解決方案有一點差異,這里記錄一下。
后續(xù)
后面再做了一次Tomcat升級,從9.0.21升級到9.0.31,突然又出現(xiàn)這個問題,問題原因是一樣的,tomcat對非法字符的控制更加嚴格了,嚴格遵循最新的RFC7230,我們除了把所有的非法字符全部加到relaxedQueryChars以外,還添加了另一項配置rejectIllegalHeader:
@Configuration public class TomcatConfig { @Bean public ServletWebServerFactory webServerFactory() { TomcatServletWebServerFactory fa = new TomcatServletWebServerFactory(); fa.addConnectorCustomizers(connector -> { connector.setProperty("relaxedQueryChars", "(),/:;<=>?@[\\]{}"); connector.setProperty("rejectIllegalHeader", "false"); }); return fa; } }
關于這個配置的解釋參考:tomcat-9.0-doc
rejectIllegalHeader
If an HTTP request is received that contains an illegal header name or value (e.g. the header name is not a token) this setting determines if the request will be rejected with a 400 response (true) or if the illegal header be ignored (false). The default value is true which will cause the request to be rejected.
這樣配置后(1.x的配置類似),大部分URI和Header都可以兼容,但是正如文檔里所說的,rejectIllegalHeader會導致非法的header忽略,即header信息將不會被服務器接收。
所以一旦Header里面有非法字符,對應的Header項將被忽略,服務器不會報400,但會跳過這個header項,比如升級過程中我們發(fā)現(xiàn)有API在header里傳輸中文,導致服務啟報錯,加了rejectIllegalHeader=false后,不報400,但程序找不到對應的Header,最后不得不刪除這些不規(guī)范的header。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家.
相關文章
Java實現(xiàn)一鍵生成表controller,service,mapper文件
這篇文章主要為大家詳細介紹了如何利用Java語言實現(xiàn)一鍵生成表controller,service,mapper文件,文中的示例代碼講解詳細,需要的可以收藏一下2023-05-05java實用小技巧之判斷l(xiāng)ist是否有重復項簡單例子
這篇文章主要給大家介紹了關于java實用小技巧之判斷l(xiāng)ist是否有重復項的相關資料,在開發(fā)工作中我們有時需要去判斷List集合中是否含有重復的元素,需要的朋友可以參考下2023-10-10JAVA數(shù)據(jù)寫入生成excel文件和發(fā)送郵件
這篇文章主要介紹了JAVA數(shù)據(jù)寫入生成excel文件和發(fā)送郵件,流程:先導包 => 郵箱開啟配置 => java寫好配置類 => 測試發(fā)送 => 數(shù)據(jù)寫入excel => 郵件帶附件發(fā)送2024-06-06