最近在半夜升级一套基于SpringBoot开发的Feedbcak系统时,遇到了意料之外的状况。本来预想可能会有一些业务参数兼容性问题,结果这个问题没出现,却出现大量的的Tomcat报错,请求没有进入业务代码,直接在Tomcat层面
400
了。
具体的异常如下:
java.lang.IllegalArgumentException: Invalid character found in the
request target [/feedback?xxx=asdasd&yyy=abc|dedg|hijk].
The valid characters are defined in RFC 7230 and RFC 3986
为了避免不必要的麻烦,上面的异常堆栈删改了一些无关的内容。java.lang.IllegalArgumentException
,这一看就是参数中出现不该出现的字符了,赶紧把参数复制下来,在本地IDE环境和测试Docker环境验证,结果都没有复现这个问题,请求可以正常进入业务代码。检查了Tomcat版本,本地、测试环境和线上全都是9.0.39
,这就奇了怪了!
2024-10-11 00:02:03,165[][main]INFO(DirectJDKLog.javalog173)-Initializing ProtocolHandler ["http-nio-0.0.0.0-8080"]
2024-10-11 00:02:03,166[][main]INFO(DirectJDKLog.javalog173)-Starting service [Tomcat]
2024-10-11 00:02:03,166[][main]INFO(DirectJDKLog.javalog173)-Starting Servlet engine: [Apache Tomcat/9.0.39]
结合The valid characters are defined in RFC 7230 and RFC 3986
这段提示,一搜就知道了原因,原来是tomcat8.0以上版本遵从RFC规范添加了对Url的特殊字符的限制,url中只允许包含英文字母(a-zA-Z)、数字(0-9)、-_.~四个特殊字符以及保留字符( ! * ’ ( ) ; : @ & = + $ , / ? # [ ] ) 等各种特殊字符均不被允许,否则就会报上面的错误。
仔细一看,请求的参数中果然有受限制的字符|
,即:yyy=abc|dedg|hijk
。
知道了原因,那就好办了,直接问大模型AI如何解决吧(相比于搜索引擎哼哧哼哧漫无边际的找,AI确实在编程辅助方面有很大作用)。
马上就有了答案,在不降低Tomcat版本的前提下,Tomcat提供了接触限值的方法,分别支持对请求参数和路径设置例外字符的能力。在SpringBoot项目中,有两种方法进行设置。
直接在application.properties
或application.yml
设置。
在application.properties中
server.tomcat.relaxed-path-chars=[,],|,
server.tomcat.relaxed-query-chars=[,],{,},
在application.yml中,你可以这样设置:
server:
tomcat:
relaxed-path-chars: "[,],|,"
relaxed-query-chars: "[,],{,},"
在项目中创建一个TomcatServletWebServerFactory
的Bean,并添加自定义的Connector配置。
@Configuration
public class TomcatConfig {
@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.addConnectorCustomizers(connector -> {
connector.setProperty("relaxedPathChars", "|{}[]");
connector.setProperty("relaxedQueryChars", "|{}[]");
});
return factory;
}
}
两种方法任选一种即可,笔者照葫芦画瓢,直接在配置文件中增加一条配置:
server.tomcat.relaxed-path-chars=|
重启应用后,400错误果然消失了,问题成功解决。此时看看时间,已然是凌晨2点多😂
问题虽然解决了,但还是有一个问题笔者没有找到原因,那就是同一个Docker镜像,跑在测试机群上可以正常处理特殊字符,跑在线上就GG了。照理说都已经用Docker镜像了,同样版本的Tomcat为何会有不一样的表现?这真是个大大的懵
字。
如果有大牛知道原因,还望不吝赐教!
原创不易,如果觉得此文对你有帮助,不妨点赞+收藏+关注,你的鼓励是我持续创作的动力!