南国薏米

南国不须收薏苡,百年终竟是芭蕉。

0%

Tomcat中URI不支持特殊字符解决方案

前情提要】最近开发过程中遇到一个TomcatIllegalArgumentException的报错,所以在这里记录一下。

壹、错误现象

在用Get请求是当URL中包含特殊字符,比如:<>(){}|等时,Tomcat会报出以下错误:

1
2
3
4
5
6
7
8
9
10
11
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:476) ~[tomcat-embed-core-8.5.28.jar:8.5.28]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:687) ~[tomcat-embed-core-8.5.28.jar:8.5.28]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.28.jar:8.5.28]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790) [tomcat-embed-core-8.5.28.jar:8.5.28]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459) [tomcat-embed-core-8.5.28.jar:8.5.28]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.28.jar:8.5.28]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_161]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_161]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.28.jar:8.5.28]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_161]

贰、故障原因

因为Tomcat严格按照 RFC 3986规范进行访问解析,而 RFC 3986规范定义了Url中只允许包含英文字母(a-zA-Z)、数字(0-9)、-_.~4个特殊字符以及所有保留字符(RFC3986中指定了以下字符为保留字符:! * ’ ( ) ; : @ & = + $ , / ? # [ ])。传入的参数中有”{“不在RFC3986中的保留字段中,所以会报参数异常错。而且这个错误你在应用中处理不到,因为根本都还没有进入应用,在Tomcat中就已经报错了,而且就连你在Tomcat中配置错误页面也没有用。

叁、解决方案

3.1、定义requestTargetAllow属性

Tomcat 7.0.76, 8.0.42, 8.5.12 这些版本之后可以定义requestTargetAllow 属性来允许禁止的字符。在tomcat的 catalina.properties文件中添加这一句:

1
tomcat.util.http.parser.HttpParser.requestTargetAllow=|{}

3.2、修复server.xml配置文件

如果某些版本的Tomcat已经参照3.1中的方法修改之后,还是不生效的话。从官网的文档中我们可以查看到如下提示:tomcat.util.http.parser.HttpParser. requestTargetAllow(This system property is deprecated. Use the relaxedPathChars and relaxedQueryChars attributes of the Connector instead)

所有我们在Tomcat配置文件中:$CATALINA_HOME/conf/server.xml添加relaxedQueryChars属性添加到Connector元素:

1
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" URIEncoding="UTF-8" relaxedQueryChars="[]|{}^&#x5c;&#x60;&quot;&lt;&gt;" redirectPort="8443" />

3.3、Springboot修改方法

在SpringBootApplication的的main方法中增加

1
System.setProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow","|{}");

另外在Springboot 2.0 之后的版本,可以自定义WebServerFactoryCustomizer,添加特殊字符的支持:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;

/**
* Created on 2019/2/18 17:41.
*
* @author Ethan
* <p>
* java.lang.IllegalArgumentException:
* Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986.
* </p>
*/
@Component
public class PortalTomcatWebServerCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

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

薏米笔记