侧边栏壁纸
博主头像
孔子说JAVA博主等级

成功只是一只沦落在鸡窝里的鹰,成功永远属于自信且有毅力的人!

  • 累计撰写 352 篇文章
  • 累计创建 135 个标签
  • 累计收到 10 条评论

目 录CONTENT

文章目录

Tomcat异常(The valid characters are defined in RFC 7230 and RFC 3986)解决方案

孔子说JAVA
2022-10-01 / 0 评论 / 0 点赞 / 118 阅读 / 3,461 字 / 正在检测是否收录...
广告 广告

今天在使用Tomcat8部署项目做测试的时候,发现有的接口会报错400,后端提示在请求目标中找到无效字符(Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986)。这是因为tomcat高版本中(8.0以上版本)严格按照 RFC 3986 规范解析地址,该规范只允许包含一些指定的字符,而我们请求的参数中部分字符在指定范围之外。

  • RFC 3986规范在tomcat7.0.73版本中就已经提出了,RFC 7230也是对前者的一些补充或者说是完善,所以在tomcat7.0.73及以上版本都会有这种问题。
  • Tomcat在 7.0.73, 8.0.39, 8.5.7 版本后,添加了对于http头的验证。

1、问题描述

今天在测试接口的是否使用tomcat启动服务,然后通过get请求去测试接口,页面返回报的错误是400。页面错误如下图所示:

image-1664333395664

tomcat中错误如下图所示:

image-1664333757145

日志中有如下报错信息:

Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986

这句话的大致意思就是说请求头中包含了 RFC 7230 and RFC 3986规范中定义的非法字符(地址被tomcat认为存在不合法字符)。在这种情况下 tomcat 返回 HTTP 400 错误响应。

在org.apache.tomcat.util.http.parser.HttpParser#IS_NOT_REQUEST_TARGET[]中定义了一堆not request target

if(IS_CONTROL[i] || i > 127 || i == 32 || i == 34 || i == 35 || i == 60 || i == 62 || i == 92 || i == 94 || i == 96 || i == 123 || i == 124 || i == 125) {
    IS_NOT_REQUEST_TARGET[i] = true;
}

转换过来就是以下字符(对应10进制ASCII看):

键盘上那些控制键  (<32或者=127)
非英文字符 (>127)
空格 (32)
双引号 (34)
# (35)
< (60)
> (62)
反斜杠 (92)
^ (94)
` (96) 指TAB上面那个键
{ (123)
} (124)
| (125)

2、原因分析

这个问题是高版本tomcat中的新特性:就是严格按照 RFC 3986规范进行访问解析,而 RFC 3986规范定义了Url中只允许包含英文字母 a-zA-Z、数字 0-9- _ .~ 等特殊字符以及所有保留字符(RFC3986中指定了以下字符为保留字符:! * ’ ( ) ; : @ & = + $ , / ? # [ ])。而我们的系统在通过地址传参时,在url中传了一段json,传入的参数中有"{"不在RFC3986中的保留字段中,所以会报这个错。

  • RFC 3986规范在tomcat7.0.73版本中就已经提出了,RFC 7230也是对前者的一些补充或者说是完善,所以在tomcat7.0.73及以上版本都会有这种问题。

3、解决方案

3.1 方法一:降低tomcat版本

下载tomcat7.0.61,完美正常运行项目,控制台不再报错,网页可以正常打开了。

image-1664334655093

Tomcat 64位下载地址:https://archive.apache.org/dist/tomcat/tomcat-7/v7.0.61/bin/apache-tomcat-7.0.61-windows-x64.zip

Tomcat 32位下载地址:https://archive.apache.org/dist/tomcat/tomcat-7/v7.0.61/bin/apache-tomcat-7.0.61-windows-x86.zip

3.2 方法二:将get请求改为post请求(推荐使用)

示例代码略。

3.3 方法三:get请求(问号传参)加密

使用URIEncoder()函数,可以将中文等字符进行编码。

var _url=path+"/dmaction/getGsList?jsr="+jsr+"&&v=1";
	_url=encodeURI(encodeURI(_url));
	$.ajax({
		type : "Post",
		url : _url,
		sync: false,//同步上传
		cache: false,//上传文件无需缓存
		processData: false,  // 不处理数据
		contentType: false, // 不设置内容类型
		dataType : "json",
		success : function(data) {
				 
		}
	});

可以看到示例代码中对url进行了编码操作: _url=encodeURI(encodeURI(_url));

在Controller中进行解码操作:

@RequestMapping("/getGsList")
	@ResponseBody
	public List<StudentBean> getGsList(HttpServletRequest request, @RequestParam String jsr)throws Exception{
		//解决IE浏览器中文  RFC 7230 and RFC 3986
		jsr=URLDecoder.decode(jsr,"UTF-8");
}

3.4 方法四:配置tomcat/conf下的catalina.properties(推荐)

在tomcat/conf下的catalina.properties文件中,找到最后注释掉的一行代码:#tomcat.util.http.parser.HttpParser.requestTargetAllow=| , 改成:tomcat.util.http.parser.HttpParser.requestTargetAllow=|{} ,表示把 {} 放行。

image-1664335318209

3.5 方法五:配置tomcat/conf下的server.xml(推荐)

在tomcat/conf下的server.xml文件中做出以下配置:

image-1664338911590

加上红色框中的代码,问题解决。

relaxedPathChars="|{}[],%" relaxedQueryChars="|{}[],%"

3.6 springboot内置tomcat解决方案

在Application启动类里面添加以下代码(当然也可以添加在一个@Configuration注解的类里):

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

image-1664339116653

因为springboot内置Tomcat,这样就等于修改了它的配置,解除字符限制,并且亲测可行,不用改其它东西。

注:我使用的 springboot 版本是 2.0.9。

如果你的springboot版本与我的相差太多,如1.X,如果上面的无效,可以试试改用下面这段:

@Bean
public EmbeddedServletContainerFactory webServerFactory() {
        TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
        factory.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> connector.setProperty("relaxedQueryChars", "|{}[]"));
        return factory;
}
0

评论区