服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - 解决springboot URL带有斜杠的转义字符百分之2F导致的400错误

解决springboot URL带有斜杠的转义字符百分之2F导致的400错误

2021-12-09 10:51ColdFireMan Java教程

这篇文章主要介绍了解决springboot URL带有斜杠的转义字符百分之2F导致的400错误问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

springboot URL带有斜杠转义字符百分之2F导致的400错误

今天项目上出现一个问题,是前端的GET请求url中带有路径参数,这个参数中有/这个特殊字符,前端已经转移成了%2F,后端用的是springboot,并没有收到这个请求,直接返回了400的错误

原因

据说是tomcat默认是不支持转义的,需要手动设置一下转化,这个搜索tomcat的设置可以找到,但是这个是springboot,有内置的tomcat,但是在yml中找不到相关的配置。

解决方式

修改一下启动类,加一个系统参数,重写WebMvcConfigurerAdapter的configurePathMatch方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
@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

现象

正常访问一个get请求,页面返回400: 解决springboot URL带有斜杠的转义字符百分之2F导致的400错误

后台日志报错:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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规范进行访问解析,而 RFC 3986规范规定Url中只允许包含英文字母(a-zA-Z)、数字(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 “(),/:;<=>?@[]{}”).

所以这个问题特别容易出现在升级spring boot版本的时候,spring boot内嵌的tomcat也会升级,老版的tomcat运行正常,新版的tomcat就会出错。而深究特殊字符来源,一般是get请求中包含json字符串、搜索特殊字符关键字等。

解决方案

如果是在开发新业务过程中出现这个问题,可以选择新的方案,避免在GET请求中使用! * ' ( ) ; : @ & = + $ , / ? # [ ])等字符,毕竟符合规范是最好的出路。

如果是升级,可以使用下面的方式来解决:

sprintboot 1.x(1.5.21测试有效)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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测试有效)

?
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.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;
    }
}

总结

这次问题出现的原因是升级springboot导致的,因为之前使用的较低版本的springboot(1.5.10.RELEASE),升级到1.5.21.RELEASE后出现了该问题。因为之前在springboot 2.x上遇到过这个问题,因此知道问题所在,但springboot 1.x和2.x的解决方案有一点差异,这里记录一下。

后续

后面再做了一次Tomcat升级,从9.0.21升级到9.0.31,突然又出现这个问题,问题原因是一样的,tomcat对非法字符的控制更加严格了,严格遵循最新的RFC7230,我们除了把所有的非法字符全部加到relaxedQueryChars以外,还添加了另一项配置rejectIllegalHeader:

?
1
2
3
4
5
6
7
8
9
10
11
12
@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项,比如升级过程中我们发现有API在header里传输中文,导致服务启报错,加了rejectIllegalHeader=false后,不报400,但程序找不到对应的Header,最后不得不删除这些不规范的header。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家.

原文链接:https://blog.csdn.net/ColdFireMan/article/details/86552242

延伸 · 阅读

精彩推荐
  • Java教程小米推送Java代码

    小米推送Java代码

    今天小编就为大家分享一篇关于小米推送Java代码,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧...

    富贵稳中求8032021-07-12
  • Java教程Java8中Stream使用的一个注意事项

    Java8中Stream使用的一个注意事项

    最近在工作中发现了对于集合操作转换的神器,java8新特性 stream,但在使用中遇到了一个非常重要的注意点,所以这篇文章主要给大家介绍了关于Java8中S...

    阿杜7482021-02-04
  • Java教程Java实现抢红包功能

    Java实现抢红包功能

    这篇文章主要为大家详细介绍了Java实现抢红包功能,采用多线程模拟多人同时抢红包,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙...

    littleschemer13532021-05-16
  • Java教程20个非常实用的Java程序代码片段

    20个非常实用的Java程序代码片段

    这篇文章主要为大家分享了20个非常实用的Java程序片段,对java开发项目有所帮助,感兴趣的小伙伴们可以参考一下 ...

    lijiao5352020-04-06
  • Java教程升级IDEA后Lombok不能使用的解决方法

    升级IDEA后Lombok不能使用的解决方法

    最近看到提示IDEA提示升级,寻思已经有好久没有升过级了。升级完毕重启之后,突然发现好多错误,本文就来介绍一下如何解决,感兴趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java BufferWriter写文件写不进去或缺失数据的解决

    Java BufferWriter写文件写不进去或缺失数据的解决

    这篇文章主要介绍了Java BufferWriter写文件写不进去或缺失数据的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望...

    spcoder14552021-10-18
  • Java教程xml与Java对象的转换详解

    xml与Java对象的转换详解

    这篇文章主要介绍了xml与Java对象的转换详解的相关资料,需要的朋友可以参考下...

    Java教程网2942020-09-17
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    这篇文章主要介绍了Java使用SAX解析xml的示例,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下...

    大行者10067412021-08-30