1. 问题描述
在使用HttpServletRequest.getParameter方法获取HTTP请求参数时,发现经过URL编码的参数被自动进行了解码,与预期不一致。
2. 问题分析
2.1 调试
使用IDEA对程序进行调试,对HttpServletRequest.getParameter方法设置断点,发送HTTP请求,进入断点后,使用“Step Info (F5)”,调用堆栈如下:
可以看到对应org.apache.catalina.connector.RequestFacade类的getParameter方法。
2.2 添加依赖包
IDEA默认无法找到org.apache.catalina.connector.RequestFacade类,根据包名,可知以上类应在Tomcat的jar包中。
将Tomcat的lib目录添加至当前工程的依赖目录中后,IDEA可以找到org.apache.catalina.connector.RequestFacade类。
2.3 添加依赖包后调试
2.3.1 请求参数解析
添加依赖包后,继续调试,进入断点后,使用“Step Info (F5)”,进入RequestFacade类的getParameter方法,代码如下:
public String getParameter(String name) { |
org.apache.catalina.Globals类的IS_SECURITY_ENABLED变量值为“System.getSecurityManager() != null”,在当前条件下即false。
RequestFacade类的getParameter方法中的this.request.getParameter方法,对应org.apache.catalina.connector.Request类的getParameter方法,代码如下:
|
在应用处理某次HTTP请求时,初始时parametersParsed参数为false(分析略),因此会执行parseParameters方法。
在Request类的parseParameters方法中,首先将this.parametersParsed设置为true(因此parseParameters方法在处理一次请求时只会执行一次解析请求参数的操作),后续调用parameters.processParameters(byte[] bytes, int start, int len)方法,对应org.apache.tomcat.util.http.Parameters类。
在Parameters类的processParameters(byte[] bytes, int start, int len)方法中,调用了processParameters(byte[] bytes, int start, int len, Charset charset)方法。
在processParameters(byte[] bytes, int start, int len, Charset charset)方法中,在while(pos < end)循环中,定义了parsingName、decodeName、decodeValue变量,分别用于标记当前在解析name还是value,当前是否需要对name进行解码,当前是否需要对value进行解码。
在while(pos < end)循环中,包含了do while循环,在其中的switch代码块中,对当前字节进行判断,若当前字符为“%+&=”等特殊字符时,对parsingName、decodeName、decodeValue变量值进行修改。可以看到当name或value中出现’%’或’+’时,会将需要解码的标志设置为真。相关代码如下所示:
|
以上涉及的十进制数如下
十进制数 | 十六进制数 | 字符 |
37 | 0x25 | '%' |
43 | 0x2B | '+' |
38 | 0x26 | '&' |
61 | 0x3D | '=' |
在后续代码中,当decodeName变量为真时,会调用this.urlDecode(this.tmpName)方法;当decodeValue变量为真时,会调用this.urlDecode(this.tmpValue)方法。在urlDecode方法中,会进行URL解码。
在后续代码中,定义了“String name = this.tmpName.toString();”与“String value”(值为“this.tmpValue.toString()”或“""”)变量,分别对应当前解析的HTTP请求参数的name与value。并调用了this.addParameter(name, value);方法
在Parameters类的addParameter方法中,调用了this.paramHashValues.put(key, values);方法,将解析的请求参数保存至Map<String, ArrayList<String>> paramHashValues变量中。
2.3.2 请求参数获取
在Request类的getParameter方法中,当parametersParsed为假,调用完parseParameters方法之后,会调用this.coyoteRequest.getParameters().getParameter(name)方法,对应Parameters类的getParameter方法。
在Parameters类的getParameter方法中,从paramHashValues变量中获取请求参数值并返回,如下所示:
|