背景介绍 链接到标题

在自研对象存储服务(类似 S3 协议)的接口开发中,我们使用Java HttpClient 发送文件上传请求,如果请求的 URL 包含特殊字符加号(如中文文件名有加号’+’),并且基于 URL 中的文件路径进行签名和鉴权,服务端会报鉴权失败。

过程分析 链接到标题

  1. 客户端分析

在发送 HTTP 请求时,已经对 URL 进行了 URLEncode 处理,在客户端打印出来的请求 URL 也显示是正确编码的。可以认为不是我们应用层的代码问题。

  1. 服务端分析

在服务端,我们使用 HttpServletRequest 的 getRequestURI() 方法获取请求的 URI,发现来自该客户端请求的 URI 中包含加号,也就是未做 URLEncode 处理。

同样地,我们通过另一个客户端发起同一个文件的上传请求,服务端日志打印出来的请求 URI 是正确的,加号已被正确编码为%2B。

由此说明,服务端也没有问题。

  1. httpclient 库源码分析

该服务依赖的 httpclient 库版本是4.5.7,我怀疑该问题应该是由于 httpclient 库的 BUG,在处理 URL 时没有对加号进行编码。

httpclient 当前版本

通过打断点调试,一路追踪 httpclient 库的相关方法调用。在以下方法中(部分源码截图),发现 httpclient 库会将已编码的 URL 解码,然后再次做编码。然而它会把加号(+)当成安全字符,从而不对其进行编码,导致了最终问题的发生。

httpclient-1

httpclient-2

httpclient-3

httpclient-4

httpclient-5

解决方案 链接到标题

解决方案很简单,升级 httpclient 库到最新版本,目前最新版本是4.5.13,该版本已经修复了该问题。

httpclient-4.5.13