The valid characters are defined in RFC 7230 and RFC 3986

The valid characters are defined in RFC 7230 and RFC 3986

1.异常截图

The valid characters are defined in RFC 7230 and RFC 3986

2.原因说明

​ 导致上述问题是因为tomcat自8.5.x系列的:8.5.12 之后版本、8.0.x系列的:8.0.42 之后版本、7.0.x系列的:7.0.76 之后版本对URL参数做了比较规范的限制,必须遵循RFC 7230 and RFC 3986规范,对于非保留字字符(json格式的请求参数)必须做转义操作,否则会抛出Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986错误信息。就是严格按照 RFC 3986规范进行访问解析,而 RFC 3986规范定义了Url中只允许包含英文字母(a-zA-Z)、数字(0-9)、-_.~4个特殊字符以及所有保留字符(RFC3986中指定了以下字符为保留字符:! * ‘ ( ) ; : @ & = + $ , / ? # [ ])。

3.源码分析

①降低tomcat版本,不推荐(因为可能有兼容性问题)

②SpringBoot项目,使用内嵌Tomcat

@Configuration
public class TomcatConfigurer {
    @Bean
    public TomcatServletWebServerFactory webServerFactory() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.addConnectorCustomizers((Connector connector) -> {
            connector.setProperty("relaxedPathChars", "\"<>[\\]^{|}");
            connector.setProperty("relaxedQueryChars", "\"<>[\\]^{|}");
        });
        return factory;
    }
}

③使用独立Tomcat(包括远古的Servlet+Jsp也是一样)

public Http11Processor(AbstractHttp11Protocol protocol, AbstractEndpoint endpoint) {
    super(endpoint);
    this.protocol = protocol;

    // 一、这两个参数就是配置请求参数或路径中需要释放的字符
    httpParser = new HttpParser(protocol.getRelaxedPathChars(), protocol.getRelaxedQueryChars());
    // ...省略...

}

HttpParser的静态代码块,初始化boolean[]

static {
    for (int i = 0; i < ARRAY_SIZE; i++) {
        // Control> 0-31, 127
        if (i < 32 || i == 127) {
            IS_CONTROL[i] = true;
        }

        // Separator
        if (    i == '(' || i == ')' || i == ''  || i == '@'  ||
                i == ',' || i == ';' || i == ':' || i == '\\' || i == '\"' ||
                i == '/' || i == '[' || i == ']' || i == '?'  || i == '='  ||
                i == '{' || i == '}' || i == ' ' || i == '\t') {
            IS_SEPARATOR[i] = true;
        }

        // Token: Anything 0-127 that is not a control and not a separator
        if (!IS_CONTROL[i] && !IS_SEPARATOR[i] && i < 128) {
            IS_TOKEN[i] = true;
        }

        // Hex: 0-9, a-f, A-F
        if ((i >= '0' && i = 'a' && i = 'A' && i = '0' && i = '0' && i = 'a' && i = 'A' && i ' || i == '[' || i == '\\' || i == ']' ||
                i == '^' || i == ''  || i == '{' || i == '|' || i == '}') {
            IS_RELAXABLE[i] = true;
        }
    }

    // 二、这里有个属性配置项,但是很明显只有三个字符可以配置|{}
    String prop = System.getProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow");
    if (prop != null) {
        for (int i = 0; i < prop.length(); i++) {
            char c = prop.charAt(i);
            if (c == '{' || c == '}' || c == '|') {
                REQUEST_TARGET_ALLOW[c] = true;
            } else {
                log.warn(sm.getString("http.invalidRequestTargetCharacter",
                        Character.valueOf(c)));
            }
        }
    }
    DEFAULT = new HttpParser(null, null);
}
</code></pre>
<pre><code class="language-java">/*
* HttpParser构造器
*/
public HttpParser(String relaxedPathChars, String relaxedQueryChars) {
    for (int i = 0; i < ARRAY_SIZE; i++) {
        // Not valid for request target.

        // Combination of multiple rules from RFC7230 and RFC 3986. Must be
        // ASCII, no controls plus a few additional characters excluded
        if (IS_CONTROL[i] ||
                i == ' ' || i == '\"' || i == '#' || i == '' || i == '\\' ||
                i == '^' || i == ''  || i == '{' || i == '|' || i == '}') {
            if (!REQUEST_TARGET_ALLOW[i]) {
                IS_NOT_REQUEST_TARGET[i] = true;
            }
        }

        /*
         * absolute-path  = 1*( "/" segment )
         * segment        = *pchar
         * pchar          = unreserved / pct-encoded / sub-delims / ":" / "@"
         *
         * Note pchar allows everything userinfo allows plus "@"
         */
        if (IS_USERINFO[i] || i == '@' || i == '/' || REQUEST_TARGET_ALLOW[i]) {
            IS_ABSOLUTEPATH_RELAXED[i] = true;
        }

        /*
         * query          = *( pchar / "/" / "?" )
         *
         * Note query allows everything absolute-path allows plus "?"
         */
        if (IS_ABSOLUTEPATH_RELAXED[i] || i == '?' || REQUEST_TARGET_ALLOW[i]) {
            IS_QUERY_RELAXED[i] = true;
        }
    }

    relax(IS_ABSOLUTEPATH_RELAXED, relaxedPathChars);
    relax(IS_QUERY_RELAXED, relaxedQueryChars);
}

/**
* 上面HttpParser构造器中最后两行代码调用relax方法
* 1.参数1是可释放的字符数组,参数2是待验证字符串
*/
private void relax(boolean[] flags, String relaxedChars) {
    if (relaxedChars != null && relaxedChars.length() > 0) {
        char[] chars = relaxedChars.toCharArray();
        for (char c : chars) {
            if (isRelaxable(c)) {
                flags[c] = true;
                // 不允许的请求字符数组
                IS_NOT_REQUEST_TARGET[c] = false;
            }
        }
    }
}

private static boolean isRelaxable(int c) {
    // Fast for valid user info characters, slower for some incorrect
    // ones
    try {
        // 在静态代码块中初始化
        return IS_RELAXABLE[c];
    } catch (ArrayIndexOutOfBoundsException ex) {
        return false;
    }
}

4.解决方案

从以上源码中可知有两处可以配置的地方:

The valid characters are defined in RFC 7230 and RFC 3986

①在tomcat/conf/server.xml中的节点(找到应用服务对应的协议和端口号)中添加两个属性


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

The valid characters are defined in RFC 7230 and RFC 3986

②在tomcat/conf/catalina.properties中的最后添加两行配置

可配置字符集: | { }
tomcat.util.http.parser.HttpParser.requestTargetAllow=|{}
org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true

Original: https://www.cnblogs.com/yushixin1024/p/16358216.html
Author: 飒沓流星
Title: The valid characters are defined in RFC 7230 and RFC 3986

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/589274/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球