SpringBoot 之更改 Tomcat 的 URL 校验规则

痴心易碎 提交于 2020-02-28 13:05:25

原文地址:https://maiyikai.github.io/2020/02/27/1582786564/

Spring Boot 项目中都会嵌入 Tomcat, 在不同版本的 Spring Boot 下,也会嵌入不同版本的 Tomcat 。Tomcat 作用和用途这里就不用再赘述了…

在项目的迁移过程中,由原先的 Servlet 项目 改造成了 Sprong Boot 项目,导致了一系列的问题,目前让我觉得可以记录的就是当前的这个 Tomcat 的问题了。因为在正常情况下不会涉及到这种问题,但是进行服务版本改造的时候应该会出现类似的问题。

抛转引玉

访问地址:http://addression/ServerName/Web.jsp?json={“balibali”…}
访问后端时报错: Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
浏览器返回的结果是:400
一开始,还不知道是什么原因,因为我用 PostMan 访问是正常的,但是用浏览器就出问题。Servlet 服务中是可以正常访问的,但是在 SpringBoot 项目中就不行了。
于是乎开始针对这个这个问题排查,奇怪的是这个异常信息在多次访问的之后,只打印一次,一开始还不重视它,但是整个日志就没有其他的异常信息

网络搜寻

一直找不到原因,于是就尝试使用这个异常去查,结果知道了原因:说是因为 Tomcat 对 URL 字符作了限制,而我地址中就存在了这些被限制的字符,于是乎对访问地址进行多次测试:

  1. http://addression/ServerName/Web.jsp --> 没有报错
  2. http://addression/ServerName/Web.jsp?json=balabala…—> 没有报错
  3. http://addression/ServerName/Web.jsp?json={}–> 400
  4. http://addression/ServerName/Web.jsp?json=%7B %7D–> 正常(%7B %7D分别对应的是字符 { } )

综上可知,是参数 json 赋值为一个对象时报错误,但是步骤 3 和 步骤 4 在某种意义上是一样的,因为在浏览器地址栏输入步骤 4 ,回车之后你看到的就是步骤 3 的地址…
但是步骤 4 是正常的,步骤 3 是错的…

百度与谷歌

知道了是 Tomcat 对 URL 的字符限制,就想先试试更改一下 Tomcat 的配置,网上找了一堆的教程

Tomcat 系列
  1. 更改 Tomcat 版本
  2. 配置tomcat支持|{}等字符的方法是:在 catalina.properties中添加 tomcat.util.http.parser.HttpParser.requestTargetAllow=|{} 但是只支持7.0.76, 8.0.42, 8.5.12 之后的版本

看到了这个答案,感觉有戏,结果才想起来, SpringBoot 项目中的 Tomcat 是内嵌的啊,难不成要重新引入一个?未免太过麻烦,而且后期还不好维护…等等一堆问题,怎么办呢?应该还有 Spring Boot 解决方案,再找找…

SpringBoot 系列

因为是公司项目,所以选用的 SpringBoot 版本一般不会改变,这里使用是 1.5.6 版本的。

  1. SpringBoot 2.* 版本解决方案-工厂配置(未验证)
	//引入这个配置
	@Bean
    public ConfigurableServletWebServerFactory webServerFactory() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> connector.setProperty("relaxedQueryChars", "|{}[]\\"));
        return factory;
    }
  1. SpringBoot 1.* 版本解决方案-工厂配置(未生效)
	/**
     * 解决异常信息:
     *  java.lang.IllegalArgumentException:
     *      Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
     * @return
     */
    @Bean
    public EmbeddedServletContainerCustomizer webServerFactory() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
            @Override
            public void customize(Connector connector) {
                connector.setProperty("relaxedQueryChars", "|{}[]");
            }
        });
        return factory;
    }
  1. SpringBoot 1.* 版本解决方案-工厂配置(已有的配置–未生效)
	@Component
	public class PortalTomcatWebServerCustomizer implements EmbeddedServletContainerCustomizer {
	
	@Override
	public void customize(ConfigurableEmbeddedServletContainer container) {
	    if(container instanceof TomcatEmbeddedServletContainerFactory) {
	      TomcatEmbeddedServletContainerFactory containerFactory = (TomcatEmbeddedServletContainerFactory) container;
	      containerFactory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
	        @Override
	        public void customize(Connector connector) {
	          connector.setAttribute("relaxedQueryChars", "[]|{}:,^\`"<>");
	          connector.setAttribute("relaxedPathChars", "[]|:,");
	        }
	      });
	    }
	  }
	}
  1. SpringBoot 1.* 版本解决方案-属性配置(生效)
	@Configuration
	public class RfcConfig {
	    @Bean
	    public Integer setRfc()
	    {
	        // 指定jre系统属性,允许特殊符号, 如{} 做入参,其他符号按需添加。见 tomcat的HttpParser源码。
	        System.setProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow", "|{}");
	        return 0;
	    }
	}

根据查询结果尝试,在当前项目中只有第 4 个方案是有用的…

总结

根据网络查询的结果不一定是可行的,需要多次查找,多次尝试,才能知道哪些是可行的。
针对于技术型问题,谷歌查询的比较精准

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!