首页 > 解决方案 > springboot decode % error 当它在 url 中并且已经被转义

问题描述

版本信息:

服务器版本:Apache Tomcat/9.0.17
服务器构建:2019 年 3 月 13 日 15:55:27 UTC
服务器编号:9.0.17.0
操作系统名称:Windows 8.1
操作系统版本:6.3
架构:amd64
JVM 版本:1.8.0_40-b25

spring-boot-2.1.4
spring-core-5.1.6

我有一个这样的 html 页面: localhost/example/37%.html
(对不起,我不能用 http// 开始 url,因为编辑页面会自动将其更改为链接,并将在 % 后添加 25)
当我访问此 url ,我得到一个错误:HTTP Status 400。
应该是,因为 % 是一个转义字符。
所以我将网址更改为:localhost/example/37%25.html。
这一次,我得到了一个新的错误:

Whitelabel 错误页面
此应用程序没有针对 /error 的显式映射,因此您将其视为后备。
Thu Sep 03 09:25:13 CST 2020
出现意外错误(类型=内部服务器错误,状态=500)。
URLDecoder:转义 (%) 模式中的非法十六进制字符 - 对于输入字符串:“.h”

我检查了tomcat控制台,它输出错误消息:

2020-09-03 09:25:12 [http-nio-8080-exec-3] ERROR 
o.s.b.w.s.support.ErrorPageFilter - Forwarding to error page from request [/37%.html] due to exception [URLDecoder: Illegal hex characters in escape (%) pattern - For input string: ".h"]
java.lang.IllegalArgumentException: URLDecoder: Illegal hex characters in escape(%) pattern - For input string: ".h" 
at java.net.URLDecoder.decode(URLDecoder.java:194)  
at org.springframework.web.servlet.resource.PathResourceResolver.isInvalidEncodedPath(PathResourceResolver.java:285)
at org.springframework.web.servlet.resource.PathResourceResolver.isResourceUnderLocation(PathResourceResolver.java:254)

似乎tomcat(或浏览器)已将%25解码为%,但springboot仍要再次解码%(PathResourceResolver.java)

我不知道这是一个错误问题还是这是 springboot 不允许 % in url 的正确方法

标签: spring-bootescapingdecodepercentage

解决方案


我发现了一些有趣的东西。

现在我有两个网页,一个名为 37%.html,另一个名为 37%25.html

按照springboot的逻辑,它会解码%两次,我访问了这个url:

本地主机/示例/37%2525.html

它会显示 37%.html,但是,它会显示 37%25.html。

那么,springboot解码多少次或如何解码%???

然后我在springboot中找到了两个类文件:
ResourceHttpRequestHandler.java
PathResourceResolver.java

它们都有一个名为:isInvalidEncodedPath() 的函数

代码在这里:

ResourceHttpRequestHandler.isInvalidEncodedPath(path)

private boolean isInvalidEncodedPath(String path) {
    if(path.contains("%")) {
        try {
            String decodedPath = URLDecoder.decode(path, "UTF-8");
            if(this.isInvalidPath(decodedPath)) {
                return true;
            }

            decodedPath = this.processPath(decodedPath);
            if(this.isInvalidPath(decodedPath)) {
                return true;
            }
        } catch (UnsupportedEncodingException | IllegalArgumentException var3) {
            ;
        }
    }

    return false;
}

PathResourceResolver.isInvalidEncodedPath(resourcePath)

private boolean isInvalidEncodedPath(String resourcePath) {
    if(resourcePath.contains("%")) {
        try {
            String decodedPath = URLDecoder.decode(resourcePath, "UTF-8");
            if(decodedPath.contains("../") || decodedPath.contains("..\\")) {
                this.logger.warn("Resolved resource path contains encoded \"../\" or \"..\\\": " + resourcePath);
                return true;
            }
        } catch (UnsupportedEncodingException var3) {
            ;
        }
    }

    return false;
}   

看到不一样的东西??他们都做 URLDecoder.decode(resourcePath, "UTF-8"),但捕获不同的异常。

当您访问 url 时,springboot 将按以下顺序调用该函数:

1 ResourceHttpRequestHandler.isInvalidEncodedPath(路径)
2 PathResourceResolver.isInvalidEncodedPath(resourcePath)

所以当点击 /37%25.html 时,在 ResourceHttpRequestHandler.isInvalidEncodedPath(path) 中,它会得到 /37%.html,因为 tomcat(或浏览器)将 %25 解码为 %。然后 URLDecoder.decode("/37%.html", "UTF-8"),触发 IllegalArgumentException,被捕获,但是什么也不做,返回 false。在PathResourceResolver.isInvalidEncodedPath(resourcePath),URLDecoder.decode("/37%.html", "UTF-8")中,触发IllegalArgumentException,没有catced,抛出异常。

当点击 /37%2525.html 时,在 ResourceHttpRequestHandler.isInvalidEncodedPath(path) 中,它得到 /37%25.html, URLDecoder.decode("/37%25.html", "UTF-8"),没问题。在PathResourceResolver.isInvalidEncodedPath(resourcePath),URLDecoder.decode("/37%25.html", "UTF-8"),也没有问题,然后显示37%25.html

如果你只输入 /37%.html,你会得到 400 错误,无效的 URI:isHexDigit。

因此,不幸的是,无法正确访问具有 % char 的 url。


推荐阅读