PHP urlencode 方法可以算是使用率比较高的一个方法了。特别是在 API 接口设计的领域或使用其他第三方 API 的时候,经常会碰到使用 urlencode 的场景。
在几年前设计过一套 API 接口提供给 App 客户端调用。Android 客户端使用 Java 开发,他们会把参与按照文档定义的规则把参数名与值进行拼接之后再 urlencode 编码,然后再拼接上一个密钥 KEY 再 MD5 再转换为大写得到一个签名。服务器端收到这个请求之后,也会按照这个规则进行签名生成与客户端提交的签名进行判断。
结果问题就出来了。
// Android 客户端提交的信息如下:
$params = [
'method' => 'user.register', // 注册接口。
'v' => '1.0.0', // APP 版本号。
'mobile' => '14812345678', // 注册手机号。
'code' => '123456', // 短信验证码。
'password' => 'abcde fg' // 账号密码。注意这里带了一个空格。
];
// 按照键名自然排序(升序)。
ksort($params);
// 把键名与值拼接。
$str = '';
foreach ($params as $key => $value) {
$str .= "{$key}{$value}";
}
$key = 'abckey'; // 密钥 KEY。
$str = urlencode($str); // URL encode 转码。
$sign = md5($str . $key); // 生成签名。
$sign = strtoupper($sign); // 签名转成大写。
echo $sign; // 输出结果:80ADC8A5878776D971E4ED444FB8386B
上面这一套规则其实非常简单。可问题出就出在上面的 password 参数存在一个空格。而我们使用 urlencode 转码之后,这个空格会被转码成 + 号。
$str = 'xxx xxx';
echo urlencode($str); // 输出:xxx+xxx
而 Java 或者其他的语言,它们的 urlencode 会把空格转码为 %20。很显然 + 与 %20 是完全不相同的字符串。在进行 MD5 签名生成的时候,很显然签名是不对等。
我们查阅 PHP urlencode 的文档获知,它存在历史的原因,并未遵循 RFC3986 标准导致。所以,PHP 提供了另一个遵守 RFC3986 标准的 urlencode 方法:rawurlencode。
该方法与其他语言的 urlencode 方法保持一致的转码规则。
结论:接口设计的时候不要使用 urlencode,而要使用 rawurlencode 方法。
以上是最近对接多家 API 接口之后,重新唤醒了我几年前接口设计时的一些踩坑回忆。于是,今天特意整理并分享给大家。