[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"content-work-with-urlencode":3,"$f5qp2psz9bbvl":26},{"id":4,"type":5,"slug":6,"title":7,"date":8,"category":9,"tags":10,"body_markdown":17,"permalink":18,"excerpt_src":19,"media_type":19,"media_title":19,"media_author":19,"media_url":19,"rating":19,"layout":19,"pv":20,"admin_only":20,"created_at":21,"updated_at":21,"deleted_at":19,"status":22,"html":23,"excerpt":24,"cover":25},190,"article","work-with-urlencode","Urlencode踩坑日记","2020-03-31 13:37","web",[11,12,13,14,15,16],"JavaScript","PHP","Python","Urlencode","编码","签名","\nUrlencode又称百分号编码，是一种很常用的编码方式，作为前端工程师，少不了与它要打交道。不管是GET请求发送参数，还是POST请求发送body，都少不了要使用Urlencode来编码。\n\n而Urlencode的编码规则又特别简单：取出字符的ASCII码，转成16进制，然后前面加上百分号即可。如果是多字节的字符，则取出每一字节，按照同样的规则进行转换即可。例如问号`?`的ASCII码为`63`，转换为16进制为`3F`，所以`%3F`即为`?`进行Urlencode编码的结果。\n\n![urlencode](\u002Fassets\u002Fwork-with-urlencode\u002F1.png)\n\n## 背景\n\n项目需要对外提供HTTP API接口，因此接口鉴权成为一个很重要的内容。为了确保安全，防止中间人篡改数据或进行重放攻击，双方约定的私钥不可以直接出现在请求中，因此采用请求签名的方式来鉴权。\n\n双方约定appKey和appSecret，其中appKey用于识别请求对象，appSecret用于请求签名。具体的方案如下：\n\n1. 客户端按照当前时间生成时间戳`timestamp`和随机数`nonce`\n2. 客户端按照指定的规则将HTTP请求的queryString和POST的body进行编码，得到一个字符串`data`\n3. 将`timestamp`、`nonce`和`data`按规则拼接，然后使用`appSecret`计算签名\n4. 将`appKey`、`timestamp`、`nonce`和签名一起随请求发出\n\n服务端在接到请求后将使用获得的数据和`appSecret`重新计算签名，然后判断与客户端给出的签名值是否一致，如果不一致则鉴权不通过。\n\n这一套鉴权机制可以有效防御一些攻击手段：\n\n- 使用了时间戳，可以避免过期请求被重发\n- 使用了随机数，可以避免请求被短时间重复发送\n- 签名数据包含了完整的时间戳、随机数和请求数据，保证服务端收到的确实是客户端发送的数据，避免被拦截修改\n- 签名的密钥是双方协商好的，避免请求伪造\n\n## 踩坑\n\n在上面的鉴权过程中，一个非常重要的点就是第2点，即将请求的queryString和POST的body进行编码，得到一个字符串。\n\n因为GET和POST请求中，数据都会被Urlencode编码，因此很容易想到，我们也使用Urlencode来进行这个鉴权前的编码过程。\n\n于是坑就这么不期而遇了。\n\n\u003C!-- more -->\n\n由于服务端和客户端都使用JavaScript编写，因此都使用了`encodeURIComponent`来进行Urlencode编码，并且过程相当愉快。\n\n但既然是开放接口，就早晚会面临各种各样的客户端。于是在我自己编写的PHP客户端上，踩坑了：有时候请求一切正常，有时候却鉴权无法通过。经过反复的调试分析，最终发现，PHP获取的待签名的字符串和JS获取的不一样，而问题就出在对`*`的转义上。\n\n在JS中，`encodeURIComponent`并不会对`*`进行转义，而PHP中`rawurlencode`却会将`*`转义为`%2A`。因此，同样的数据在不同的客户端中就产生了不同的字符串，最终导致计算出的签名值不同，鉴权失败。\n\n## 爬坑失败\n\n如果一个接口一直使用都没有问题，突然来了一个新的客户端就鉴权失败，那么必然是这个客户端有问题了。在这种想法的驱使下，对PHP这个世界上最好的语言好感度再次-1，然后硬着头皮去查资料。发现确实有很多人碰到了PHP在使用Urlencode编码的时候星号被编码的问题。还有人给出了解决问题的代码，即在`rawurlencode`之后再将`%2A`替换成`*`。\n\n于是就这么更新上线了，一切又恢复了正常。\n\n然而好景不长，才刚正常几天，又出现了诡异的鉴权失败的问题。而这一次，请求的内容是`[链接](https:\u002F\u002Fwww.qq.com)`。再次对比后，发现括号`(`、`)`在Urlencode后又不一致了：JS没有对括号进行转义，而PHP对它们进行了转义，于是再次出现签名不一致的问题。\n\n直觉告诉我，当一个问题第一次出现时，也许可以绕得过去，但是当它再一次出现的时候，就必须得挖到底了，否则未来一定会有更严重的问题出现。\n\n## 认真审视Urlencode\n\n回到问题本质：兼容性问题。这可是前端工程师最擅长的领域，于是很自然地想到——规范。urlencode的规范是[RFC3986](https:\u002F\u002Ftools.ietf.org\u002Fhtml\u002Frfc3986)，但是看完规范之后，并没能解决这个兼容性的问题，反而解释了兼容性的来源：很多字符是否进行编辑取决于具体的场景和实现……\n\n> 下面这一段有点烧脑，如果不是特别有兴趣，建议跳过。\n\n规范将保留字符分为`gen-delims`和`sub-delims`两部分：\n\n- `gen-delims = \":\" \u002F \"\u002F\" \u002F \"?\" \u002F \"#\" \u002F \"[\" \u002F \"]\" \u002F \"@\"`\n- `sub-delims = \"!\" \u002F \"$\" \u002F \"&\" \u002F \"'\" \u002F \"(\" \u002F \")\" \u002F \"*\" \u002F \"+\" \u002F \",\" \u002F \";\" \u002F \"=\"`\n\n然后定义了`pchar`（`unreserved`指除了保留字符之外的字符）\n\n- `pchar = unreserved \u002F pct-encoded \u002F sub-delims \u002F \":\" \u002F \"@\"`\n\n以URL中出现的`path`和`query`为例，它们的规则分别是\n\n```\npath          = path-abempty    ; begins with \"\u002F\" or is empty\n                    \u002F path-absolute   ; begins with \"\u002F\" but not \"\u002F\u002F\"\n                    \u002F path-noscheme   ; begins with a non-colon segment\n                    \u002F path-rootless   ; begins with a segment\n                    \u002F path-empty      ; zero characters\n\n      path-abempty  = *( \"\u002F\" segment )\n      path-absolute = \"\u002F\" [ segment-nz *( \"\u002F\" segment ) ]\n      path-noscheme = segment-nz-nc *( \"\u002F\" segment )\n      path-rootless = segment-nz *( \"\u002F\" segment )\n      path-empty    = 0\u003Cpchar>\n\n      segment       = *pchar\n      segment-nz    = 1*pchar\n      segment-nz-nc = 1*( unreserved \u002F pct-encoded \u002F sub-delims \u002F \"@\" )\n                    ; non-zero-length segment without any colon \":\"\n```\n\n```\nquery       = *( pchar \u002F \"\u002F\" \u002F \"?\" )\n```\n\n可以看到，它们都有引用`pchar`作为规则（或规则的一部分），除此之外，还有各自允许的字符。这中间的细节要弄明白需要花非常多的时间，我们也可以先不纠结，虽然规范中写了每个部分可以包含哪些字符，却并没有明确写出这些字符是否需要进行`urlencode`（例如`sub-delims`）。\n\n规范中唯一能给我们一些比较明确指引的只有对`unreserved`非保留字符的描述，明确定义了它们是`字母 \u002F 数字 \u002F \"-\" \u002F \".\" \u002F \"_\" \u002F \"~\"`这几个字符。\n\n## 回到现实\n\n既然规范无法给出足够明确的指引，就只能看看现实世界是怎么运作的了。在搜索urlencode规范的时候，发现有很多文档都是这么写：\n\n> 按照rfc3986，除字母、数字、`-`、`.`、`_`、`~`字符外，其它字符均需要进行百分号编码。\n\n也即，大家在实际应用时，会把除非保留字符之外的其他字符全部进行编码。\n\n那编程语言又是如何处理的呢？于是拿JavaScript、PHP、Python分别跑了一下。由于JS中有`encodeURI`\u002F`encodeURIComponent`两个方法，PHP有`urlencode`\u002F`rawurlencode`两个方法，因此一共有5组结果。\n\n|ASCII|char              |python|js encodeURI|js encodeURIComponent|php urlencode|php rawurlencode|\n|-----|------------------|------|------------|---------------------|-------------|----------------|\n|0    |NUT 空字符（Null）|%00   |%00          |%00                   |%00           |%00              |\n|1    |SOH 标题开始      |%01   |%01          |%01                   |%01           |%01              |\n|2    |STX 本文开始      |%02   |%02          |%02                   |%02           |%02              |\n|3    |ETX 本文结束      |%03   |%03          |%03                   |%03           |%03              |\n|4    |EOT 传输结束      |%04   |%04          |%04                   |%04           |%04              |\n|5    |ENQ 请求          |%05   |%05          |%05                   |%05           |%05              |\n|6    |ACK 确认回应      |%06   |%06          |%06                   |%06           |%06              |\n|7    |BEL 响铃          |%07   |%07          |%07                   |%07           |%07              |\n|8    |BS 退格           |%08   |%08          |%08                   |%08           |%08              |\n|9    |HT 水平定位符号TAB|%09   |%09          |%09                   |%09           |%09              |\n|10   |LF 换行键         |%0A   |%0A          |%0A                   |%0A           |%0A              |\n|11   |VT 垂直定位符号   |%0B   |%0B          |%0B                   |%0B           |%0B              |\n|12   |FF 换页键         |%0C   |%0C          |%0C                   |%0C           |%0C              |\n|13   |CR Enter回车键    |%0D   |%0D          |%0D                   |%0D           |%0D              |\n|14   |SO 取消变换       |%0E   |%0E          |%0E                   |%0E           |%0E              |\n|15   |SI 启用变换       |%0F   |%0F          |%0F                   |%0F           |%0F              |\n|16   |DLE 跳出数据通讯  |%10   |%10          |%10                   |%10           |%10              |\n|17   |DC1 设备控制一    |%11   |%11          |%11                   |%11           |%11              |\n|18   |DC2 设备控制二    |%12   |%12          |%12                   |%12           |%12              |\n|19   |DC3 设备控制三    |%13   |%13          |%13                   |%13           |%13              |\n|20   |DC4 设备控制四    |%14   |%14          |%14                   |%14           |%14              |\n|21   |NAK 确认失败回应  |%15   |%15          |%15                   |%15           |%15              |\n|22   |SYN 同步用暂停    |%16   |%16          |%16                   |%16           |%16              |\n|23   |TB 区块传输结束   |%17   |%17          |%17                   |%17           |%17              |\n|24   |CAN 取消          |%18   |%18          |%18                   |%18           |%18              |\n|25   |EM 连接介质中断   |%19   |%19          |%19                   |%19           |%19              |\n|26   |SUB 替换          |%1A   |%1A          |%1A                   |%1A           |%1A              |\n|27   |ESC 退出键        |%1B   |%1B          |%1B                   |%1B           |%1B              |\n|28   |FS 文件分区符     |%1C   |%1C          |%1C                   |%1C           |%1C              |\n|29   |GS 组群分隔符     |%1D   |%1D          |%1D                   |%1D           |%1D              |\n|30   |RS 记录分隔符     |%1E   |%1E          |%1E                   |%1E           |%1E              |\n|31   |US 单元分隔符     |%1F   |%1F          |%1F                   |%1F           |%1F              |\n|32   |空格              |%20   |%20          |%20                   |+             |%20              |\n|33   |!                 |%21   |!            |!                     |%21           |%21              |\n|34   |\"                 |%22   |%22          |%22                   |%22           |%22              |\n|35   |#                 |%23   |#            |%23                   |%23           |%23              |\n|36   |$                 |%24   |$            |%24                   |%24           |%24              |\n|37   |%                 |%25   |%25          |%25                   |%25           |%25              |\n|38   |&                 |%26   |&            |%26                   |%26           |%26              |\n|39   |'                 |%27   |'            |'                     |%27           |%27              |\n|40   |(                 |%28   |(            |(                     |%28           |%28              |\n|41   |)                 |%29   |)            |)                     |%29           |%29              |\n|42   |\\*                 |%2A   |\\*            |*                     |%2A           |%2A              |\n|43   |+                 |%2B   |+            |%2B                   |%2B           |%2B              |\n|44   |,                 |%2C   |,            |%2C                   |%2C           |%2C              |\n|45   |-                 |-     |-            |-                     |-             |-                |\n|46   |.                 |.     |.            |.                     |.             |.                |\n|47   |\u002F                 |\u002F     |\u002F            |%2F                   |%2F           |%2F              |\n|48   |0                 |0     |0            |0                     |0             |0                |\n|49   |1                 |1     |1            |1                     |1             |1                |\n|50   |2                 |2     |2            |2                     |2             |2                |\n|51   |3                 |3     |3            |3                     |3             |3                |\n|52   |4                 |4     |4            |4                     |4             |4                |\n|53   |5                 |5     |5            |5                     |5             |5                |\n|54   |6                 |6     |6            |6                     |6             |6                |\n|55   |7                 |7     |7            |7                     |7             |7                |\n|56   |8                 |8     |8            |8                     |8             |8                |\n|57   |9                 |9     |9            |9                     |9             |9                |\n|58   |:                 |%3A   |:            |%3A                   |%3A           |%3A              |\n|59   |;                 |%3B   |;            |%3B                   |%3B           |%3B              |\n|60   |\u003C                 |%3C   |%3C          |%3C                   |%3C           |%3C              |\n|61   |=                 |%3D   |=            |%3D                   |%3D           |%3D              |\n|62   |>                 |%3E   |%3E          |%3E                   |%3E           |%3E              |\n|63   |?                 |%3F   |?            |%3F                   |%3F           |%3F              |\n|64   |@                 |%40   |@            |%40                   |%40           |%40              |\n|65   |A                 |A     |A            |A                     |A             |A                |\n|66   |B                 |B     |B            |B                     |B             |B                |\n|67   |C                 |C     |C            |C                     |C             |C                |\n|68   |D                 |D     |D            |D                     |D             |D                |\n|69   |E                 |E     |E            |E                     |E             |E                |\n|70   |F                 |F     |F            |F                     |F             |F                |\n|71   |G                 |G     |G            |G                     |G             |G                |\n|72   |H                 |H     |H            |H                     |H             |H                |\n|73   |I                 |I     |I            |I                     |I             |I                |\n|74   |J                 |J     |J            |J                     |J             |J                |\n|75   |K                 |K     |K            |K                     |K             |K                |\n|76   |L                 |L     |L            |L                     |L             |L                |\n|77   |M                 |M     |M            |M                     |M             |M                |\n|78   |N                 |N     |N            |N                     |N             |N                |\n|79   |O                 |O     |O            |O                     |O             |O                |\n|80   |P                 |P     |P            |P                     |P             |P                |\n|81   |Q                 |Q     |Q            |Q                     |Q             |Q                |\n|82   |R                 |R     |R            |R                     |R             |R                |\n|83   |S                 |S     |S            |S                     |S             |S                |\n|84   |T                 |T     |T            |T                     |T             |T                |\n|85   |U                 |U     |U            |U                     |U             |U                |\n|86   |V                 |V     |V            |V                     |V             |V                |\n|87   |W                 |W     |W            |W                     |W             |W                |\n|88   |X                 |X     |X            |X                     |X             |X                |\n|89   |Y                 |Y     |Y            |Y                     |Y             |Y                |\n|90   |Z                 |Z     |Z            |Z                     |Z             |Z                |\n|91   |[                 |%5B   |%5B          |%5B                   |%5B           |%5B              |\n|92   |\\                 |%5C   |%5C          |%5C                   |%5C           |%5C              |\n|93   |]                 |%5D   |%5D          |%5D                   |%5D           |%5D              |\n|94   |^                 |%5E   |%5E          |%5E                   |%5E           |%5E              |\n|95   |_                 |_     |_            |_                     |_             |_                |\n|96   |`                 |%60   |%60          |%60                   |%60           |%60              |\n|97   |a                 |a     |a            |a                     |a             |a                |\n|98   |b                 |b     |b            |b                     |b             |b                |\n|99   |c                 |c     |c            |c                     |c             |c                |\n|100  |d                 |d     |d            |d                     |d             |d                |\n|101  |e                 |e     |e            |e                     |e             |e                |\n|102  |f                 |f     |f            |f                     |f             |f                |\n|103  |g                 |g     |g            |g                     |g             |g                |\n|104  |h                 |h     |h            |h                     |h             |h                |\n|105  |i                 |i     |i            |i                     |i             |i                |\n|106  |j                 |j     |j            |j                     |j             |j                |\n|107  |k                 |k     |k            |k                     |k             |k                |\n|108  |l                 |l     |l            |l                     |l             |l                |\n|109  |m                 |m     |m            |m                     |m             |m                |\n|110  |n                 |n     |n            |n                     |n             |n                |\n|111  |o                 |o     |o            |o                     |o             |o                |\n|112  |p                 |p     |p            |p                     |p             |p                |\n|113  |q                 |q     |q            |q                     |q             |q                |\n|114  |r                 |r     |r            |r                     |r             |r                |\n|115  |s                 |s     |s            |s                     |s             |s                |\n|116  |t                 |t     |t            |t                     |t             |t                |\n|117  |u                 |u     |u            |u                     |u             |u                |\n|118  |v                 |v     |v            |v                     |v             |v                |\n|119  |w                 |w     |w            |w                     |w             |w                |\n|120  |x                 |x     |x            |x                     |x             |x                |\n|121  |y                 |y     |y            |y                     |y             |y                |\n|122  |z                 |z     |z            |z                     |z             |z                |\n|123  |{                 |%7B   |%7B          |%7B                   |%7B           |%7B              |\n|124  |&#124;            |%7C   |%7C          |%7C                   |%7C           |%7C              |\n|125  |}                 |%7D   |%7D          |%7D                   |%7D           |%7D              |\n|126  |~                 |~     |~            |~                     |%7E           |~                |\n|127  |删除              |%7F   |%7F          |%7F                   |%7F           |%7F              |\n\n总结一下：\n\n- 非保留字符的处理上，非常一致（除了`~`）\n- JS的`encodeURI`方法保留了很多符号，这些符号没有进行编码\n- JS的`encodeURIComponent`方法相比PHP和Python，少了`!`、`'`、`（`、`）`、`*`这5个字符的编码\n- PHP的`urlencode`方法将空格编码成了加号（`+`），且对`~`进行不必要的编码\n- Python默认没有对`\u002F`进行编码，需要显式指定`safe=''`才会进行编码（`urllib.parse.quote(str, safe='')`）\n\n如果按照业界“非保留字符一律进行编码”的实践规则来看，那么Python（指定`safe=''`）和PHP（`rawurlencode`）是符合要求的，而JS的`encodeURIComponent`则需要针对额外的5个字符打补丁。\n\n```javascript\nfunction fixedEncodeURIComponent (str) {\n  return encodeURIComponent(str).replace(\u002F[!'()*]\u002Fg, function(c) {\n    return '%' + c.charCodeAt(0).toString(16);\n  });\n}\n```\n\n## 善后\n\n因为结论比较明显，业界和主流语言都采用了比较一致的规则，因此最终这个项目的鉴权部分也进行了修改，除了非保留字符外，其他的字符都需要进行百分号编码。\n\nurlencode由于规范中没有规定得非常细致，将很多细节交给了实现，因此导致各语言的处理并不一致。当然如果能提前预知有这样的问题，有可能在选择方案的时候就不会选择urlencode这么一种“不太确定”的编码规则。\n\n> 如果你认为JSON大法好，恭喜你即将进行另一个坑：不同语言在JSON编码的处理上也不一致，例如中文要不要变成unicode格式、斜杠要不要编码等等。\n\n大概一位前端工程师也没想到有一天需要在后端处理“兼容性问题”。好在处理兼容性问题的原则是一致的：找到差异点、抹平它。\n","\u002Farticle\u002Fwork-with-urlencode.html",null,0,"2026-08-28 04:37:17","published","\u003Cp>Urlencode又称百分号编码，是一种很常用的编码方式，作为前端工程师，少不了与它要打交道。不管是GET请求发送参数，还是POST请求发送body，都少不了要使用Urlencode来编码。\u003C\u002Fp>\n\u003Cp>而Urlencode的编码规则又特别简单：取出字符的ASCII码，转成16进制，然后前面加上百分号即可。如果是多字节的字符，则取出每一字节，按照同样的规则进行转换即可。例如问号\u003Ccode>?\u003C\u002Fcode>的ASCII码为\u003Ccode>63\u003C\u002Fcode>，转换为16进制为\u003Ccode>3F\u003C\u002Fcode>，所以\u003Ccode>%3F\u003C\u002Fcode>即为\u003Ccode>?\u003C\u002Fcode>进行Urlencode编码的结果。\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fassets.toobug.net\u002Fassets\u002Fwork-with-urlencode\u002F1.png\" alt=\"urlencode\">\u003C\u002Fp>\n\u003Ch2>背景\u003C\u002Fh2>\n\u003Cp>项目需要对外提供HTTP API接口，因此接口鉴权成为一个很重要的内容。为了确保安全，防止中间人篡改数据或进行重放攻击，双方约定的私钥不可以直接出现在请求中，因此采用请求签名的方式来鉴权。\u003C\u002Fp>\n\u003Cp>双方约定appKey和appSecret，其中appKey用于识别请求对象，appSecret用于请求签名。具体的方案如下：\u003C\u002Fp>\n\u003Col>\n\u003Cli>客户端按照当前时间生成时间戳\u003Ccode>timestamp\u003C\u002Fcode>和随机数\u003Ccode>nonce\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>客户端按照指定的规则将HTTP请求的queryString和POST的body进行编码，得到一个字符串\u003Ccode>data\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>将\u003Ccode>timestamp\u003C\u002Fcode>、\u003Ccode>nonce\u003C\u002Fcode>和\u003Ccode>data\u003C\u002Fcode>按规则拼接，然后使用\u003Ccode>appSecret\u003C\u002Fcode>计算签名\u003C\u002Fli>\n\u003Cli>将\u003Ccode>appKey\u003C\u002Fcode>、\u003Ccode>timestamp\u003C\u002Fcode>、\u003Ccode>nonce\u003C\u002Fcode>和签名一起随请求发出\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>服务端在接到请求后将使用获得的数据和\u003Ccode>appSecret\u003C\u002Fcode>重新计算签名，然后判断与客户端给出的签名值是否一致，如果不一致则鉴权不通过。\u003C\u002Fp>\n\u003Cp>这一套鉴权机制可以有效防御一些攻击手段：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>使用了时间戳，可以避免过期请求被重发\u003C\u002Fli>\n\u003Cli>使用了随机数，可以避免请求被短时间重复发送\u003C\u002Fli>\n\u003Cli>签名数据包含了完整的时间戳、随机数和请求数据，保证服务端收到的确实是客户端发送的数据，避免被拦截修改\u003C\u002Fli>\n\u003Cli>签名的密钥是双方协商好的，避免请求伪造\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>踩坑\u003C\u002Fh2>\n\u003Cp>在上面的鉴权过程中，一个非常重要的点就是第2点，即将请求的queryString和POST的body进行编码，得到一个字符串。\u003C\u002Fp>\n\u003Cp>因为GET和POST请求中，数据都会被Urlencode编码，因此很容易想到，我们也使用Urlencode来进行这个鉴权前的编码过程。\u003C\u002Fp>\n\u003Cp>于是坑就这么不期而遇了。\u003C\u002Fp>\n\u003Cp>由于服务端和客户端都使用JavaScript编写，因此都使用了\u003Ccode>encodeURIComponent\u003C\u002Fcode>来进行Urlencode编码，并且过程相当愉快。\u003C\u002Fp>\n\u003Cp>但既然是开放接口，就早晚会面临各种各样的客户端。于是在我自己编写的PHP客户端上，踩坑了：有时候请求一切正常，有时候却鉴权无法通过。经过反复的调试分析，最终发现，PHP获取的待签名的字符串和JS获取的不一样，而问题就出在对\u003Ccode>*\u003C\u002Fcode>的转义上。\u003C\u002Fp>\n\u003Cp>在JS中，\u003Ccode>encodeURIComponent\u003C\u002Fcode>并不会对\u003Ccode>*\u003C\u002Fcode>进行转义，而PHP中\u003Ccode>rawurlencode\u003C\u002Fcode>却会将\u003Ccode>*\u003C\u002Fcode>转义为\u003Ccode>%2A\u003C\u002Fcode>。因此，同样的数据在不同的客户端中就产生了不同的字符串，最终导致计算出的签名值不同，鉴权失败。\u003C\u002Fp>\n\u003Ch2>爬坑失败\u003C\u002Fh2>\n\u003Cp>如果一个接口一直使用都没有问题，突然来了一个新的客户端就鉴权失败，那么必然是这个客户端有问题了。在这种想法的驱使下，对PHP这个世界上最好的语言好感度再次-1，然后硬着头皮去查资料。发现确实有很多人碰到了PHP在使用Urlencode编码的时候星号被编码的问题。还有人给出了解决问题的代码，即在\u003Ccode>rawurlencode\u003C\u002Fcode>之后再将\u003Ccode>%2A\u003C\u002Fcode>替换成\u003Ccode>*\u003C\u002Fcode>。\u003C\u002Fp>\n\u003Cp>于是就这么更新上线了，一切又恢复了正常。\u003C\u002Fp>\n\u003Cp>然而好景不长，才刚正常几天，又出现了诡异的鉴权失败的问题。而这一次，请求的内容是\u003Ccode>[链接](https:\u002F\u002Fwww.qq.com)\u003C\u002Fcode>。再次对比后，发现括号\u003Ccode>(\u003C\u002Fcode>、\u003Ccode>)\u003C\u002Fcode>在Urlencode后又不一致了：JS没有对括号进行转义，而PHP对它们进行了转义，于是再次出现签名不一致的问题。\u003C\u002Fp>\n\u003Cp>直觉告诉我，当一个问题第一次出现时，也许可以绕得过去，但是当它再一次出现的时候，就必须得挖到底了，否则未来一定会有更严重的问题出现。\u003C\u002Fp>\n\u003Ch2>认真审视Urlencode\u003C\u002Fh2>\n\u003Cp>回到问题本质：兼容性问题。这可是前端工程师最擅长的领域，于是很自然地想到——规范。urlencode的规范是\u003Ca href=\"https:\u002F\u002Ftools.ietf.org\u002Fhtml\u002Frfc3986\">RFC3986\u003C\u002Fa>，但是看完规范之后，并没能解决这个兼容性的问题，反而解释了兼容性的来源：很多字符是否进行编辑取决于具体的场景和实现……\u003C\u002Fp>\n\u003Cblockquote>\n\u003Cp>下面这一段有点烧脑，如果不是特别有兴趣，建议跳过。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>规范将保留字符分为\u003Ccode>gen-delims\u003C\u002Fcode>和\u003Ccode>sub-delims\u003C\u002Fcode>两部分：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Ccode>gen-delims = &quot;:&quot; \u002F &quot;\u002F&quot; \u002F &quot;?&quot; \u002F &quot;#&quot; \u002F &quot;[&quot; \u002F &quot;]&quot; \u002F &quot;@&quot;\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Ccode>sub-delims = &quot;!&quot; \u002F &quot;$&quot; \u002F &quot;&amp;&quot; \u002F &quot;'&quot; \u002F &quot;(&quot; \u002F &quot;)&quot; \u002F &quot;*&quot; \u002F &quot;+&quot; \u002F &quot;,&quot; \u002F &quot;;&quot; \u002F &quot;=&quot;\u003C\u002Fcode>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>然后定义了\u003Ccode>pchar\u003C\u002Fcode>（\u003Ccode>unreserved\u003C\u002Fcode>指除了保留字符之外的字符）\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Ccode>pchar = unreserved \u002F pct-encoded \u002F sub-delims \u002F &quot;:&quot; \u002F &quot;@&quot;\u003C\u002Fcode>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>以URL中出现的\u003Ccode>path\u003C\u002Fcode>和\u003Ccode>query\u003C\u002Fcode>为例，它们的规则分别是\u003C\u002Fp>\n\u003Cpre class=\"shiki\" style=\"background-color:#121212;color:#dbd7caee\" tabindex=\"0\">\u003Ccode>path          = path-abempty    ; begins with &quot;\u002F&quot; or is empty\n                    \u002F path-absolute   ; begins with &quot;\u002F&quot; but not &quot;\u002F\u002F&quot;\n                    \u002F path-noscheme   ; begins with a non-colon segment\n                    \u002F path-rootless   ; begins with a segment\n                    \u002F path-empty      ; zero characters\n\n      path-abempty  = *( &quot;\u002F&quot; segment )\n      path-absolute = &quot;\u002F&quot; [ segment-nz *( &quot;\u002F&quot; segment ) ]\n      path-noscheme = segment-nz-nc *( &quot;\u002F&quot; segment )\n      path-rootless = segment-nz *( &quot;\u002F&quot; segment )\n      path-empty    = 0&lt;pchar&gt;\n\n      segment       = *pchar\n      segment-nz    = 1*pchar\n      segment-nz-nc = 1*( unreserved \u002F pct-encoded \u002F sub-delims \u002F &quot;@&quot; )\n                    ; non-zero-length segment without any colon &quot;:&quot;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre class=\"shiki\" style=\"background-color:#121212;color:#dbd7caee\" tabindex=\"0\">\u003Ccode>query       = *( pchar \u002F &quot;\u002F&quot; \u002F &quot;?&quot; )\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>可以看到，它们都有引用\u003Ccode>pchar\u003C\u002Fcode>作为规则（或规则的一部分），除此之外，还有各自允许的字符。这中间的细节要弄明白需要花非常多的时间，我们也可以先不纠结，虽然规范中写了每个部分可以包含哪些字符，却并没有明确写出这些字符是否需要进行\u003Ccode>urlencode\u003C\u002Fcode>（例如\u003Ccode>sub-delims\u003C\u002Fcode>）。\u003C\u002Fp>\n\u003Cp>规范中唯一能给我们一些比较明确指引的只有对\u003Ccode>unreserved\u003C\u002Fcode>非保留字符的描述，明确定义了它们是\u003Ccode>字母 \u002F 数字 \u002F &quot;-&quot; \u002F &quot;.&quot; \u002F &quot;_&quot; \u002F &quot;~&quot;\u003C\u002Fcode>这几个字符。\u003C\u002Fp>\n\u003Ch2>回到现实\u003C\u002Fh2>\n\u003Cp>既然规范无法给出足够明确的指引，就只能看看现实世界是怎么运作的了。在搜索urlencode规范的时候，发现有很多文档都是这么写：\u003C\u002Fp>\n\u003Cblockquote>\n\u003Cp>按照rfc3986，除字母、数字、\u003Ccode>-\u003C\u002Fcode>、\u003Ccode>.\u003C\u002Fcode>、\u003Ccode>_\u003C\u002Fcode>、\u003Ccode>~\u003C\u002Fcode>字符外，其它字符均需要进行百分号编码。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>也即，大家在实际应用时，会把除非保留字符之外的其他字符全部进行编码。\u003C\u002Fp>\n\u003Cp>那编程语言又是如何处理的呢？于是拿JavaScript、PHP、Python分别跑了一下。由于JS中有\u003Ccode>encodeURI\u003C\u002Fcode>\u002F\u003Ccode>encodeURIComponent\u003C\u002Fcode>两个方法，PHP有\u003Ccode>urlencode\u003C\u002Fcode>\u002F\u003Ccode>rawurlencode\u003C\u002Fcode>两个方法，因此一共有5组结果。\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>ASCII\u003C\u002Fth>\n\u003Cth>char\u003C\u002Fth>\n\u003Cth>python\u003C\u002Fth>\n\u003Cth>js encodeURI\u003C\u002Fth>\n\u003Cth>js encodeURIComponent\u003C\u002Fth>\n\u003Cth>php urlencode\u003C\u002Fth>\n\u003Cth>php rawurlencode\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>NUT 空字符（Null）\u003C\u002Ftd>\n\u003Ctd>%00\u003C\u002Ftd>\n\u003Ctd>%00\u003C\u002Ftd>\n\u003Ctd>%00\u003C\u002Ftd>\n\u003Ctd>%00\u003C\u002Ftd>\n\u003Ctd>%00\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>1\u003C\u002Ftd>\n\u003Ctd>SOH 标题开始\u003C\u002Ftd>\n\u003Ctd>%01\u003C\u002Ftd>\n\u003Ctd>%01\u003C\u002Ftd>\n\u003Ctd>%01\u003C\u002Ftd>\n\u003Ctd>%01\u003C\u002Ftd>\n\u003Ctd>%01\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>2\u003C\u002Ftd>\n\u003Ctd>STX 本文开始\u003C\u002Ftd>\n\u003Ctd>%02\u003C\u002Ftd>\n\u003Ctd>%02\u003C\u002Ftd>\n\u003Ctd>%02\u003C\u002Ftd>\n\u003Ctd>%02\u003C\u002Ftd>\n\u003Ctd>%02\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>3\u003C\u002Ftd>\n\u003Ctd>ETX 本文结束\u003C\u002Ftd>\n\u003Ctd>%03\u003C\u002Ftd>\n\u003Ctd>%03\u003C\u002Ftd>\n\u003Ctd>%03\u003C\u002Ftd>\n\u003Ctd>%03\u003C\u002Ftd>\n\u003Ctd>%03\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>4\u003C\u002Ftd>\n\u003Ctd>EOT 传输结束\u003C\u002Ftd>\n\u003Ctd>%04\u003C\u002Ftd>\n\u003Ctd>%04\u003C\u002Ftd>\n\u003Ctd>%04\u003C\u002Ftd>\n\u003Ctd>%04\u003C\u002Ftd>\n\u003Ctd>%04\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>5\u003C\u002Ftd>\n\u003Ctd>ENQ 请求\u003C\u002Ftd>\n\u003Ctd>%05\u003C\u002Ftd>\n\u003Ctd>%05\u003C\u002Ftd>\n\u003Ctd>%05\u003C\u002Ftd>\n\u003Ctd>%05\u003C\u002Ftd>\n\u003Ctd>%05\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>6\u003C\u002Ftd>\n\u003Ctd>ACK 确认回应\u003C\u002Ftd>\n\u003Ctd>%06\u003C\u002Ftd>\n\u003Ctd>%06\u003C\u002Ftd>\n\u003Ctd>%06\u003C\u002Ftd>\n\u003Ctd>%06\u003C\u002Ftd>\n\u003Ctd>%06\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>7\u003C\u002Ftd>\n\u003Ctd>BEL 响铃\u003C\u002Ftd>\n\u003Ctd>%07\u003C\u002Ftd>\n\u003Ctd>%07\u003C\u002Ftd>\n\u003Ctd>%07\u003C\u002Ftd>\n\u003Ctd>%07\u003C\u002Ftd>\n\u003Ctd>%07\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>8\u003C\u002Ftd>\n\u003Ctd>BS 退格\u003C\u002Ftd>\n\u003Ctd>%08\u003C\u002Ftd>\n\u003Ctd>%08\u003C\u002Ftd>\n\u003Ctd>%08\u003C\u002Ftd>\n\u003Ctd>%08\u003C\u002Ftd>\n\u003Ctd>%08\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>9\u003C\u002Ftd>\n\u003Ctd>HT 水平定位符号TAB\u003C\u002Ftd>\n\u003Ctd>%09\u003C\u002Ftd>\n\u003Ctd>%09\u003C\u002Ftd>\n\u003Ctd>%09\u003C\u002Ftd>\n\u003Ctd>%09\u003C\u002Ftd>\n\u003Ctd>%09\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>10\u003C\u002Ftd>\n\u003Ctd>LF 换行键\u003C\u002Ftd>\n\u003Ctd>%0A\u003C\u002Ftd>\n\u003Ctd>%0A\u003C\u002Ftd>\n\u003Ctd>%0A\u003C\u002Ftd>\n\u003Ctd>%0A\u003C\u002Ftd>\n\u003Ctd>%0A\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>11\u003C\u002Ftd>\n\u003Ctd>VT 垂直定位符号\u003C\u002Ftd>\n\u003Ctd>%0B\u003C\u002Ftd>\n\u003Ctd>%0B\u003C\u002Ftd>\n\u003Ctd>%0B\u003C\u002Ftd>\n\u003Ctd>%0B\u003C\u002Ftd>\n\u003Ctd>%0B\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>12\u003C\u002Ftd>\n\u003Ctd>FF 换页键\u003C\u002Ftd>\n\u003Ctd>%0C\u003C\u002Ftd>\n\u003Ctd>%0C\u003C\u002Ftd>\n\u003Ctd>%0C\u003C\u002Ftd>\n\u003Ctd>%0C\u003C\u002Ftd>\n\u003Ctd>%0C\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>13\u003C\u002Ftd>\n\u003Ctd>CR Enter回车键\u003C\u002Ftd>\n\u003Ctd>%0D\u003C\u002Ftd>\n\u003Ctd>%0D\u003C\u002Ftd>\n\u003Ctd>%0D\u003C\u002Ftd>\n\u003Ctd>%0D\u003C\u002Ftd>\n\u003Ctd>%0D\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>14\u003C\u002Ftd>\n\u003Ctd>SO 取消变换\u003C\u002Ftd>\n\u003Ctd>%0E\u003C\u002Ftd>\n\u003Ctd>%0E\u003C\u002Ftd>\n\u003Ctd>%0E\u003C\u002Ftd>\n\u003Ctd>%0E\u003C\u002Ftd>\n\u003Ctd>%0E\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>15\u003C\u002Ftd>\n\u003Ctd>SI 启用变换\u003C\u002Ftd>\n\u003Ctd>%0F\u003C\u002Ftd>\n\u003Ctd>%0F\u003C\u002Ftd>\n\u003Ctd>%0F\u003C\u002Ftd>\n\u003Ctd>%0F\u003C\u002Ftd>\n\u003Ctd>%0F\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>16\u003C\u002Ftd>\n\u003Ctd>DLE 跳出数据通讯\u003C\u002Ftd>\n\u003Ctd>%10\u003C\u002Ftd>\n\u003Ctd>%10\u003C\u002Ftd>\n\u003Ctd>%10\u003C\u002Ftd>\n\u003Ctd>%10\u003C\u002Ftd>\n\u003Ctd>%10\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>17\u003C\u002Ftd>\n\u003Ctd>DC1 设备控制一\u003C\u002Ftd>\n\u003Ctd>%11\u003C\u002Ftd>\n\u003Ctd>%11\u003C\u002Ftd>\n\u003Ctd>%11\u003C\u002Ftd>\n\u003Ctd>%11\u003C\u002Ftd>\n\u003Ctd>%11\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>18\u003C\u002Ftd>\n\u003Ctd>DC2 设备控制二\u003C\u002Ftd>\n\u003Ctd>%12\u003C\u002Ftd>\n\u003Ctd>%12\u003C\u002Ftd>\n\u003Ctd>%12\u003C\u002Ftd>\n\u003Ctd>%12\u003C\u002Ftd>\n\u003Ctd>%12\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>19\u003C\u002Ftd>\n\u003Ctd>DC3 设备控制三\u003C\u002Ftd>\n\u003Ctd>%13\u003C\u002Ftd>\n\u003Ctd>%13\u003C\u002Ftd>\n\u003Ctd>%13\u003C\u002Ftd>\n\u003Ctd>%13\u003C\u002Ftd>\n\u003Ctd>%13\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>20\u003C\u002Ftd>\n\u003Ctd>DC4 设备控制四\u003C\u002Ftd>\n\u003Ctd>%14\u003C\u002Ftd>\n\u003Ctd>%14\u003C\u002Ftd>\n\u003Ctd>%14\u003C\u002Ftd>\n\u003Ctd>%14\u003C\u002Ftd>\n\u003Ctd>%14\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>21\u003C\u002Ftd>\n\u003Ctd>NAK 确认失败回应\u003C\u002Ftd>\n\u003Ctd>%15\u003C\u002Ftd>\n\u003Ctd>%15\u003C\u002Ftd>\n\u003Ctd>%15\u003C\u002Ftd>\n\u003Ctd>%15\u003C\u002Ftd>\n\u003Ctd>%15\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>22\u003C\u002Ftd>\n\u003Ctd>SYN 同步用暂停\u003C\u002Ftd>\n\u003Ctd>%16\u003C\u002Ftd>\n\u003Ctd>%16\u003C\u002Ftd>\n\u003Ctd>%16\u003C\u002Ftd>\n\u003Ctd>%16\u003C\u002Ftd>\n\u003Ctd>%16\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>23\u003C\u002Ftd>\n\u003Ctd>TB 区块传输结束\u003C\u002Ftd>\n\u003Ctd>%17\u003C\u002Ftd>\n\u003Ctd>%17\u003C\u002Ftd>\n\u003Ctd>%17\u003C\u002Ftd>\n\u003Ctd>%17\u003C\u002Ftd>\n\u003Ctd>%17\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>24\u003C\u002Ftd>\n\u003Ctd>CAN 取消\u003C\u002Ftd>\n\u003Ctd>%18\u003C\u002Ftd>\n\u003Ctd>%18\u003C\u002Ftd>\n\u003Ctd>%18\u003C\u002Ftd>\n\u003Ctd>%18\u003C\u002Ftd>\n\u003Ctd>%18\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>25\u003C\u002Ftd>\n\u003Ctd>EM 连接介质中断\u003C\u002Ftd>\n\u003Ctd>%19\u003C\u002Ftd>\n\u003Ctd>%19\u003C\u002Ftd>\n\u003Ctd>%19\u003C\u002Ftd>\n\u003Ctd>%19\u003C\u002Ftd>\n\u003Ctd>%19\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>26\u003C\u002Ftd>\n\u003Ctd>SUB 替换\u003C\u002Ftd>\n\u003Ctd>%1A\u003C\u002Ftd>\n\u003Ctd>%1A\u003C\u002Ftd>\n\u003Ctd>%1A\u003C\u002Ftd>\n\u003Ctd>%1A\u003C\u002Ftd>\n\u003Ctd>%1A\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>27\u003C\u002Ftd>\n\u003Ctd>ESC 退出键\u003C\u002Ftd>\n\u003Ctd>%1B\u003C\u002Ftd>\n\u003Ctd>%1B\u003C\u002Ftd>\n\u003Ctd>%1B\u003C\u002Ftd>\n\u003Ctd>%1B\u003C\u002Ftd>\n\u003Ctd>%1B\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>28\u003C\u002Ftd>\n\u003Ctd>FS 文件分区符\u003C\u002Ftd>\n\u003Ctd>%1C\u003C\u002Ftd>\n\u003Ctd>%1C\u003C\u002Ftd>\n\u003Ctd>%1C\u003C\u002Ftd>\n\u003Ctd>%1C\u003C\u002Ftd>\n\u003Ctd>%1C\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>29\u003C\u002Ftd>\n\u003Ctd>GS 组群分隔符\u003C\u002Ftd>\n\u003Ctd>%1D\u003C\u002Ftd>\n\u003Ctd>%1D\u003C\u002Ftd>\n\u003Ctd>%1D\u003C\u002Ftd>\n\u003Ctd>%1D\u003C\u002Ftd>\n\u003Ctd>%1D\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>30\u003C\u002Ftd>\n\u003Ctd>RS 记录分隔符\u003C\u002Ftd>\n\u003Ctd>%1E\u003C\u002Ftd>\n\u003Ctd>%1E\u003C\u002Ftd>\n\u003Ctd>%1E\u003C\u002Ftd>\n\u003Ctd>%1E\u003C\u002Ftd>\n\u003Ctd>%1E\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>31\u003C\u002Ftd>\n\u003Ctd>US 单元分隔符\u003C\u002Ftd>\n\u003Ctd>%1F\u003C\u002Ftd>\n\u003Ctd>%1F\u003C\u002Ftd>\n\u003Ctd>%1F\u003C\u002Ftd>\n\u003Ctd>%1F\u003C\u002Ftd>\n\u003Ctd>%1F\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>32\u003C\u002Ftd>\n\u003Ctd>空格\u003C\u002Ftd>\n\u003Ctd>%20\u003C\u002Ftd>\n\u003Ctd>%20\u003C\u002Ftd>\n\u003Ctd>%20\u003C\u002Ftd>\n\u003Ctd>+\u003C\u002Ftd>\n\u003Ctd>%20\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>33\u003C\u002Ftd>\n\u003Ctd>!\u003C\u002Ftd>\n\u003Ctd>%21\u003C\u002Ftd>\n\u003Ctd>!\u003C\u002Ftd>\n\u003Ctd>!\u003C\u002Ftd>\n\u003Ctd>%21\u003C\u002Ftd>\n\u003Ctd>%21\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>34\u003C\u002Ftd>\n\u003Ctd>&quot;\u003C\u002Ftd>\n\u003Ctd>%22\u003C\u002Ftd>\n\u003Ctd>%22\u003C\u002Ftd>\n\u003Ctd>%22\u003C\u002Ftd>\n\u003Ctd>%22\u003C\u002Ftd>\n\u003Ctd>%22\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>35\u003C\u002Ftd>\n\u003Ctd>#\u003C\u002Ftd>\n\u003Ctd>%23\u003C\u002Ftd>\n\u003Ctd>#\u003C\u002Ftd>\n\u003Ctd>%23\u003C\u002Ftd>\n\u003Ctd>%23\u003C\u002Ftd>\n\u003Ctd>%23\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>36\u003C\u002Ftd>\n\u003Ctd>$\u003C\u002Ftd>\n\u003Ctd>%24\u003C\u002Ftd>\n\u003Ctd>$\u003C\u002Ftd>\n\u003Ctd>%24\u003C\u002Ftd>\n\u003Ctd>%24\u003C\u002Ftd>\n\u003Ctd>%24\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>37\u003C\u002Ftd>\n\u003Ctd>%\u003C\u002Ftd>\n\u003Ctd>%25\u003C\u002Ftd>\n\u003Ctd>%25\u003C\u002Ftd>\n\u003Ctd>%25\u003C\u002Ftd>\n\u003Ctd>%25\u003C\u002Ftd>\n\u003Ctd>%25\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>38\u003C\u002Ftd>\n\u003Ctd>&amp;\u003C\u002Ftd>\n\u003Ctd>%26\u003C\u002Ftd>\n\u003Ctd>&amp;\u003C\u002Ftd>\n\u003Ctd>%26\u003C\u002Ftd>\n\u003Ctd>%26\u003C\u002Ftd>\n\u003Ctd>%26\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>39\u003C\u002Ftd>\n\u003Ctd>'\u003C\u002Ftd>\n\u003Ctd>%27\u003C\u002Ftd>\n\u003Ctd>'\u003C\u002Ftd>\n\u003Ctd>'\u003C\u002Ftd>\n\u003Ctd>%27\u003C\u002Ftd>\n\u003Ctd>%27\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>40\u003C\u002Ftd>\n\u003Ctd>(\u003C\u002Ftd>\n\u003Ctd>%28\u003C\u002Ftd>\n\u003Ctd>(\u003C\u002Ftd>\n\u003Ctd>(\u003C\u002Ftd>\n\u003Ctd>%28\u003C\u002Ftd>\n\u003Ctd>%28\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>41\u003C\u002Ftd>\n\u003Ctd>)\u003C\u002Ftd>\n\u003Ctd>%29\u003C\u002Ftd>\n\u003Ctd>)\u003C\u002Ftd>\n\u003Ctd>)\u003C\u002Ftd>\n\u003Ctd>%29\u003C\u002Ftd>\n\u003Ctd>%29\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>42\u003C\u002Ftd>\n\u003Ctd>*\u003C\u002Ftd>\n\u003Ctd>%2A\u003C\u002Ftd>\n\u003Ctd>*\u003C\u002Ftd>\n\u003Ctd>*\u003C\u002Ftd>\n\u003Ctd>%2A\u003C\u002Ftd>\n\u003Ctd>%2A\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>43\u003C\u002Ftd>\n\u003Ctd>+\u003C\u002Ftd>\n\u003Ctd>%2B\u003C\u002Ftd>\n\u003Ctd>+\u003C\u002Ftd>\n\u003Ctd>%2B\u003C\u002Ftd>\n\u003Ctd>%2B\u003C\u002Ftd>\n\u003Ctd>%2B\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>44\u003C\u002Ftd>\n\u003Ctd>,\u003C\u002Ftd>\n\u003Ctd>%2C\u003C\u002Ftd>\n\u003Ctd>,\u003C\u002Ftd>\n\u003Ctd>%2C\u003C\u002Ftd>\n\u003Ctd>%2C\u003C\u002Ftd>\n\u003Ctd>%2C\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>45\u003C\u002Ftd>\n\u003Ctd>-\u003C\u002Ftd>\n\u003Ctd>-\u003C\u002Ftd>\n\u003Ctd>-\u003C\u002Ftd>\n\u003Ctd>-\u003C\u002Ftd>\n\u003Ctd>-\u003C\u002Ftd>\n\u003Ctd>-\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>46\u003C\u002Ftd>\n\u003Ctd>.\u003C\u002Ftd>\n\u003Ctd>.\u003C\u002Ftd>\n\u003Ctd>.\u003C\u002Ftd>\n\u003Ctd>.\u003C\u002Ftd>\n\u003Ctd>.\u003C\u002Ftd>\n\u003Ctd>.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>47\u003C\u002Ftd>\n\u003Ctd>\u002F\u003C\u002Ftd>\n\u003Ctd>\u002F\u003C\u002Ftd>\n\u003Ctd>\u002F\u003C\u002Ftd>\n\u003Ctd>%2F\u003C\u002Ftd>\n\u003Ctd>%2F\u003C\u002Ftd>\n\u003Ctd>%2F\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>48\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003Ctd>0\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>49\u003C\u002Ftd>\n\u003Ctd>1\u003C\u002Ftd>\n\u003Ctd>1\u003C\u002Ftd>\n\u003Ctd>1\u003C\u002Ftd>\n\u003Ctd>1\u003C\u002Ftd>\n\u003Ctd>1\u003C\u002Ftd>\n\u003Ctd>1\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>50\u003C\u002Ftd>\n\u003Ctd>2\u003C\u002Ftd>\n\u003Ctd>2\u003C\u002Ftd>\n\u003Ctd>2\u003C\u002Ftd>\n\u003Ctd>2\u003C\u002Ftd>\n\u003Ctd>2\u003C\u002Ftd>\n\u003Ctd>2\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>51\u003C\u002Ftd>\n\u003Ctd>3\u003C\u002Ftd>\n\u003Ctd>3\u003C\u002Ftd>\n\u003Ctd>3\u003C\u002Ftd>\n\u003Ctd>3\u003C\u002Ftd>\n\u003Ctd>3\u003C\u002Ftd>\n\u003Ctd>3\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>52\u003C\u002Ftd>\n\u003Ctd>4\u003C\u002Ftd>\n\u003Ctd>4\u003C\u002Ftd>\n\u003Ctd>4\u003C\u002Ftd>\n\u003Ctd>4\u003C\u002Ftd>\n\u003Ctd>4\u003C\u002Ftd>\n\u003Ctd>4\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>53\u003C\u002Ftd>\n\u003Ctd>5\u003C\u002Ftd>\n\u003Ctd>5\u003C\u002Ftd>\n\u003Ctd>5\u003C\u002Ftd>\n\u003Ctd>5\u003C\u002Ftd>\n\u003Ctd>5\u003C\u002Ftd>\n\u003Ctd>5\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>54\u003C\u002Ftd>\n\u003Ctd>6\u003C\u002Ftd>\n\u003Ctd>6\u003C\u002Ftd>\n\u003Ctd>6\u003C\u002Ftd>\n\u003Ctd>6\u003C\u002Ftd>\n\u003Ctd>6\u003C\u002Ftd>\n\u003Ctd>6\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>55\u003C\u002Ftd>\n\u003Ctd>7\u003C\u002Ftd>\n\u003Ctd>7\u003C\u002Ftd>\n\u003Ctd>7\u003C\u002Ftd>\n\u003Ctd>7\u003C\u002Ftd>\n\u003Ctd>7\u003C\u002Ftd>\n\u003Ctd>7\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>56\u003C\u002Ftd>\n\u003Ctd>8\u003C\u002Ftd>\n\u003Ctd>8\u003C\u002Ftd>\n\u003Ctd>8\u003C\u002Ftd>\n\u003Ctd>8\u003C\u002Ftd>\n\u003Ctd>8\u003C\u002Ftd>\n\u003Ctd>8\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>57\u003C\u002Ftd>\n\u003Ctd>9\u003C\u002Ftd>\n\u003Ctd>9\u003C\u002Ftd>\n\u003Ctd>9\u003C\u002Ftd>\n\u003Ctd>9\u003C\u002Ftd>\n\u003Ctd>9\u003C\u002Ftd>\n\u003Ctd>9\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>58\u003C\u002Ftd>\n\u003Ctd>:\u003C\u002Ftd>\n\u003Ctd>%3A\u003C\u002Ftd>\n\u003Ctd>:\u003C\u002Ftd>\n\u003Ctd>%3A\u003C\u002Ftd>\n\u003Ctd>%3A\u003C\u002Ftd>\n\u003Ctd>%3A\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>59\u003C\u002Ftd>\n\u003Ctd>;\u003C\u002Ftd>\n\u003Ctd>%3B\u003C\u002Ftd>\n\u003Ctd>;\u003C\u002Ftd>\n\u003Ctd>%3B\u003C\u002Ftd>\n\u003Ctd>%3B\u003C\u002Ftd>\n\u003Ctd>%3B\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>60\u003C\u002Ftd>\n\u003Ctd>&lt;\u003C\u002Ftd>\n\u003Ctd>%3C\u003C\u002Ftd>\n\u003Ctd>%3C\u003C\u002Ftd>\n\u003Ctd>%3C\u003C\u002Ftd>\n\u003Ctd>%3C\u003C\u002Ftd>\n\u003Ctd>%3C\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>61\u003C\u002Ftd>\n\u003Ctd>=\u003C\u002Ftd>\n\u003Ctd>%3D\u003C\u002Ftd>\n\u003Ctd>=\u003C\u002Ftd>\n\u003Ctd>%3D\u003C\u002Ftd>\n\u003Ctd>%3D\u003C\u002Ftd>\n\u003Ctd>%3D\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>62\u003C\u002Ftd>\n\u003Ctd>&gt;\u003C\u002Ftd>\n\u003Ctd>%3E\u003C\u002Ftd>\n\u003Ctd>%3E\u003C\u002Ftd>\n\u003Ctd>%3E\u003C\u002Ftd>\n\u003Ctd>%3E\u003C\u002Ftd>\n\u003Ctd>%3E\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>63\u003C\u002Ftd>\n\u003Ctd>?\u003C\u002Ftd>\n\u003Ctd>%3F\u003C\u002Ftd>\n\u003Ctd>?\u003C\u002Ftd>\n\u003Ctd>%3F\u003C\u002Ftd>\n\u003Ctd>%3F\u003C\u002Ftd>\n\u003Ctd>%3F\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>64\u003C\u002Ftd>\n\u003Ctd>@\u003C\u002Ftd>\n\u003Ctd>%40\u003C\u002Ftd>\n\u003Ctd>@\u003C\u002Ftd>\n\u003Ctd>%40\u003C\u002Ftd>\n\u003Ctd>%40\u003C\u002Ftd>\n\u003Ctd>%40\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>65\u003C\u002Ftd>\n\u003Ctd>A\u003C\u002Ftd>\n\u003Ctd>A\u003C\u002Ftd>\n\u003Ctd>A\u003C\u002Ftd>\n\u003Ctd>A\u003C\u002Ftd>\n\u003Ctd>A\u003C\u002Ftd>\n\u003Ctd>A\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>66\u003C\u002Ftd>\n\u003Ctd>B\u003C\u002Ftd>\n\u003Ctd>B\u003C\u002Ftd>\n\u003Ctd>B\u003C\u002Ftd>\n\u003Ctd>B\u003C\u002Ftd>\n\u003Ctd>B\u003C\u002Ftd>\n\u003Ctd>B\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>67\u003C\u002Ftd>\n\u003Ctd>C\u003C\u002Ftd>\n\u003Ctd>C\u003C\u002Ftd>\n\u003Ctd>C\u003C\u002Ftd>\n\u003Ctd>C\u003C\u002Ftd>\n\u003Ctd>C\u003C\u002Ftd>\n\u003Ctd>C\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>68\u003C\u002Ftd>\n\u003Ctd>D\u003C\u002Ftd>\n\u003Ctd>D\u003C\u002Ftd>\n\u003Ctd>D\u003C\u002Ftd>\n\u003Ctd>D\u003C\u002Ftd>\n\u003Ctd>D\u003C\u002Ftd>\n\u003Ctd>D\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>69\u003C\u002Ftd>\n\u003Ctd>E\u003C\u002Ftd>\n\u003Ctd>E\u003C\u002Ftd>\n\u003Ctd>E\u003C\u002Ftd>\n\u003Ctd>E\u003C\u002Ftd>\n\u003Ctd>E\u003C\u002Ftd>\n\u003Ctd>E\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>70\u003C\u002Ftd>\n\u003Ctd>F\u003C\u002Ftd>\n\u003Ctd>F\u003C\u002Ftd>\n\u003Ctd>F\u003C\u002Ftd>\n\u003Ctd>F\u003C\u002Ftd>\n\u003Ctd>F\u003C\u002Ftd>\n\u003Ctd>F\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>71\u003C\u002Ftd>\n\u003Ctd>G\u003C\u002Ftd>\n\u003Ctd>G\u003C\u002Ftd>\n\u003Ctd>G\u003C\u002Ftd>\n\u003Ctd>G\u003C\u002Ftd>\n\u003Ctd>G\u003C\u002Ftd>\n\u003Ctd>G\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>72\u003C\u002Ftd>\n\u003Ctd>H\u003C\u002Ftd>\n\u003Ctd>H\u003C\u002Ftd>\n\u003Ctd>H\u003C\u002Ftd>\n\u003Ctd>H\u003C\u002Ftd>\n\u003Ctd>H\u003C\u002Ftd>\n\u003Ctd>H\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>73\u003C\u002Ftd>\n\u003Ctd>I\u003C\u002Ftd>\n\u003Ctd>I\u003C\u002Ftd>\n\u003Ctd>I\u003C\u002Ftd>\n\u003Ctd>I\u003C\u002Ftd>\n\u003Ctd>I\u003C\u002Ftd>\n\u003Ctd>I\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>74\u003C\u002Ftd>\n\u003Ctd>J\u003C\u002Ftd>\n\u003Ctd>J\u003C\u002Ftd>\n\u003Ctd>J\u003C\u002Ftd>\n\u003Ctd>J\u003C\u002Ftd>\n\u003Ctd>J\u003C\u002Ftd>\n\u003Ctd>J\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>75\u003C\u002Ftd>\n\u003Ctd>K\u003C\u002Ftd>\n\u003Ctd>K\u003C\u002Ftd>\n\u003Ctd>K\u003C\u002Ftd>\n\u003Ctd>K\u003C\u002Ftd>\n\u003Ctd>K\u003C\u002Ftd>\n\u003Ctd>K\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>76\u003C\u002Ftd>\n\u003Ctd>L\u003C\u002Ftd>\n\u003Ctd>L\u003C\u002Ftd>\n\u003Ctd>L\u003C\u002Ftd>\n\u003Ctd>L\u003C\u002Ftd>\n\u003Ctd>L\u003C\u002Ftd>\n\u003Ctd>L\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>77\u003C\u002Ftd>\n\u003Ctd>M\u003C\u002Ftd>\n\u003Ctd>M\u003C\u002Ftd>\n\u003Ctd>M\u003C\u002Ftd>\n\u003Ctd>M\u003C\u002Ftd>\n\u003Ctd>M\u003C\u002Ftd>\n\u003Ctd>M\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>78\u003C\u002Ftd>\n\u003Ctd>N\u003C\u002Ftd>\n\u003Ctd>N\u003C\u002Ftd>\n\u003Ctd>N\u003C\u002Ftd>\n\u003Ctd>N\u003C\u002Ftd>\n\u003Ctd>N\u003C\u002Ftd>\n\u003Ctd>N\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>79\u003C\u002Ftd>\n\u003Ctd>O\u003C\u002Ftd>\n\u003Ctd>O\u003C\u002Ftd>\n\u003Ctd>O\u003C\u002Ftd>\n\u003Ctd>O\u003C\u002Ftd>\n\u003Ctd>O\u003C\u002Ftd>\n\u003Ctd>O\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>80\u003C\u002Ftd>\n\u003Ctd>P\u003C\u002Ftd>\n\u003Ctd>P\u003C\u002Ftd>\n\u003Ctd>P\u003C\u002Ftd>\n\u003Ctd>P\u003C\u002Ftd>\n\u003Ctd>P\u003C\u002Ftd>\n\u003Ctd>P\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>81\u003C\u002Ftd>\n\u003Ctd>Q\u003C\u002Ftd>\n\u003Ctd>Q\u003C\u002Ftd>\n\u003Ctd>Q\u003C\u002Ftd>\n\u003Ctd>Q\u003C\u002Ftd>\n\u003Ctd>Q\u003C\u002Ftd>\n\u003Ctd>Q\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>82\u003C\u002Ftd>\n\u003Ctd>R\u003C\u002Ftd>\n\u003Ctd>R\u003C\u002Ftd>\n\u003Ctd>R\u003C\u002Ftd>\n\u003Ctd>R\u003C\u002Ftd>\n\u003Ctd>R\u003C\u002Ftd>\n\u003Ctd>R\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>83\u003C\u002Ftd>\n\u003Ctd>S\u003C\u002Ftd>\n\u003Ctd>S\u003C\u002Ftd>\n\u003Ctd>S\u003C\u002Ftd>\n\u003Ctd>S\u003C\u002Ftd>\n\u003Ctd>S\u003C\u002Ftd>\n\u003Ctd>S\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>84\u003C\u002Ftd>\n\u003Ctd>T\u003C\u002Ftd>\n\u003Ctd>T\u003C\u002Ftd>\n\u003Ctd>T\u003C\u002Ftd>\n\u003Ctd>T\u003C\u002Ftd>\n\u003Ctd>T\u003C\u002Ftd>\n\u003Ctd>T\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>85\u003C\u002Ftd>\n\u003Ctd>U\u003C\u002Ftd>\n\u003Ctd>U\u003C\u002Ftd>\n\u003Ctd>U\u003C\u002Ftd>\n\u003Ctd>U\u003C\u002Ftd>\n\u003Ctd>U\u003C\u002Ftd>\n\u003Ctd>U\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>86\u003C\u002Ftd>\n\u003Ctd>V\u003C\u002Ftd>\n\u003Ctd>V\u003C\u002Ftd>\n\u003Ctd>V\u003C\u002Ftd>\n\u003Ctd>V\u003C\u002Ftd>\n\u003Ctd>V\u003C\u002Ftd>\n\u003Ctd>V\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>87\u003C\u002Ftd>\n\u003Ctd>W\u003C\u002Ftd>\n\u003Ctd>W\u003C\u002Ftd>\n\u003Ctd>W\u003C\u002Ftd>\n\u003Ctd>W\u003C\u002Ftd>\n\u003Ctd>W\u003C\u002Ftd>\n\u003Ctd>W\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>88\u003C\u002Ftd>\n\u003Ctd>X\u003C\u002Ftd>\n\u003Ctd>X\u003C\u002Ftd>\n\u003Ctd>X\u003C\u002Ftd>\n\u003Ctd>X\u003C\u002Ftd>\n\u003Ctd>X\u003C\u002Ftd>\n\u003Ctd>X\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>89\u003C\u002Ftd>\n\u003Ctd>Y\u003C\u002Ftd>\n\u003Ctd>Y\u003C\u002Ftd>\n\u003Ctd>Y\u003C\u002Ftd>\n\u003Ctd>Y\u003C\u002Ftd>\n\u003Ctd>Y\u003C\u002Ftd>\n\u003Ctd>Y\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>90\u003C\u002Ftd>\n\u003Ctd>Z\u003C\u002Ftd>\n\u003Ctd>Z\u003C\u002Ftd>\n\u003Ctd>Z\u003C\u002Ftd>\n\u003Ctd>Z\u003C\u002Ftd>\n\u003Ctd>Z\u003C\u002Ftd>\n\u003Ctd>Z\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>91\u003C\u002Ftd>\n\u003Ctd>[\u003C\u002Ftd>\n\u003Ctd>%5B\u003C\u002Ftd>\n\u003Ctd>%5B\u003C\u002Ftd>\n\u003Ctd>%5B\u003C\u002Ftd>\n\u003Ctd>%5B\u003C\u002Ftd>\n\u003Ctd>%5B\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>92\u003C\u002Ftd>\n\u003Ctd>\\\u003C\u002Ftd>\n\u003Ctd>%5C\u003C\u002Ftd>\n\u003Ctd>%5C\u003C\u002Ftd>\n\u003Ctd>%5C\u003C\u002Ftd>\n\u003Ctd>%5C\u003C\u002Ftd>\n\u003Ctd>%5C\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>93\u003C\u002Ftd>\n\u003Ctd>]\u003C\u002Ftd>\n\u003Ctd>%5D\u003C\u002Ftd>\n\u003Ctd>%5D\u003C\u002Ftd>\n\u003Ctd>%5D\u003C\u002Ftd>\n\u003Ctd>%5D\u003C\u002Ftd>\n\u003Ctd>%5D\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>94\u003C\u002Ftd>\n\u003Ctd>^\u003C\u002Ftd>\n\u003Ctd>%5E\u003C\u002Ftd>\n\u003Ctd>%5E\u003C\u002Ftd>\n\u003Ctd>%5E\u003C\u002Ftd>\n\u003Ctd>%5E\u003C\u002Ftd>\n\u003Ctd>%5E\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>95\u003C\u002Ftd>\n\u003Ctd>_\u003C\u002Ftd>\n\u003Ctd>_\u003C\u002Ftd>\n\u003Ctd>_\u003C\u002Ftd>\n\u003Ctd>_\u003C\u002Ftd>\n\u003Ctd>_\u003C\u002Ftd>\n\u003Ctd>_\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>96\u003C\u002Ftd>\n\u003Ctd>`\u003C\u002Ftd>\n\u003Ctd>%60\u003C\u002Ftd>\n\u003Ctd>%60\u003C\u002Ftd>\n\u003Ctd>%60\u003C\u002Ftd>\n\u003Ctd>%60\u003C\u002Ftd>\n\u003Ctd>%60\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>97\u003C\u002Ftd>\n\u003Ctd>a\u003C\u002Ftd>\n\u003Ctd>a\u003C\u002Ftd>\n\u003Ctd>a\u003C\u002Ftd>\n\u003Ctd>a\u003C\u002Ftd>\n\u003Ctd>a\u003C\u002Ftd>\n\u003Ctd>a\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>98\u003C\u002Ftd>\n\u003Ctd>b\u003C\u002Ftd>\n\u003Ctd>b\u003C\u002Ftd>\n\u003Ctd>b\u003C\u002Ftd>\n\u003Ctd>b\u003C\u002Ftd>\n\u003Ctd>b\u003C\u002Ftd>\n\u003Ctd>b\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>99\u003C\u002Ftd>\n\u003Ctd>c\u003C\u002Ftd>\n\u003Ctd>c\u003C\u002Ftd>\n\u003Ctd>c\u003C\u002Ftd>\n\u003Ctd>c\u003C\u002Ftd>\n\u003Ctd>c\u003C\u002Ftd>\n\u003Ctd>c\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>100\u003C\u002Ftd>\n\u003Ctd>d\u003C\u002Ftd>\n\u003Ctd>d\u003C\u002Ftd>\n\u003Ctd>d\u003C\u002Ftd>\n\u003Ctd>d\u003C\u002Ftd>\n\u003Ctd>d\u003C\u002Ftd>\n\u003Ctd>d\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>101\u003C\u002Ftd>\n\u003Ctd>e\u003C\u002Ftd>\n\u003Ctd>e\u003C\u002Ftd>\n\u003Ctd>e\u003C\u002Ftd>\n\u003Ctd>e\u003C\u002Ftd>\n\u003Ctd>e\u003C\u002Ftd>\n\u003Ctd>e\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>102\u003C\u002Ftd>\n\u003Ctd>f\u003C\u002Ftd>\n\u003Ctd>f\u003C\u002Ftd>\n\u003Ctd>f\u003C\u002Ftd>\n\u003Ctd>f\u003C\u002Ftd>\n\u003Ctd>f\u003C\u002Ftd>\n\u003Ctd>f\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>103\u003C\u002Ftd>\n\u003Ctd>g\u003C\u002Ftd>\n\u003Ctd>g\u003C\u002Ftd>\n\u003Ctd>g\u003C\u002Ftd>\n\u003Ctd>g\u003C\u002Ftd>\n\u003Ctd>g\u003C\u002Ftd>\n\u003Ctd>g\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>104\u003C\u002Ftd>\n\u003Ctd>h\u003C\u002Ftd>\n\u003Ctd>h\u003C\u002Ftd>\n\u003Ctd>h\u003C\u002Ftd>\n\u003Ctd>h\u003C\u002Ftd>\n\u003Ctd>h\u003C\u002Ftd>\n\u003Ctd>h\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>105\u003C\u002Ftd>\n\u003Ctd>i\u003C\u002Ftd>\n\u003Ctd>i\u003C\u002Ftd>\n\u003Ctd>i\u003C\u002Ftd>\n\u003Ctd>i\u003C\u002Ftd>\n\u003Ctd>i\u003C\u002Ftd>\n\u003Ctd>i\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>106\u003C\u002Ftd>\n\u003Ctd>j\u003C\u002Ftd>\n\u003Ctd>j\u003C\u002Ftd>\n\u003Ctd>j\u003C\u002Ftd>\n\u003Ctd>j\u003C\u002Ftd>\n\u003Ctd>j\u003C\u002Ftd>\n\u003Ctd>j\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>107\u003C\u002Ftd>\n\u003Ctd>k\u003C\u002Ftd>\n\u003Ctd>k\u003C\u002Ftd>\n\u003Ctd>k\u003C\u002Ftd>\n\u003Ctd>k\u003C\u002Ftd>\n\u003Ctd>k\u003C\u002Ftd>\n\u003Ctd>k\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>108\u003C\u002Ftd>\n\u003Ctd>l\u003C\u002Ftd>\n\u003Ctd>l\u003C\u002Ftd>\n\u003Ctd>l\u003C\u002Ftd>\n\u003Ctd>l\u003C\u002Ftd>\n\u003Ctd>l\u003C\u002Ftd>\n\u003Ctd>l\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>109\u003C\u002Ftd>\n\u003Ctd>m\u003C\u002Ftd>\n\u003Ctd>m\u003C\u002Ftd>\n\u003Ctd>m\u003C\u002Ftd>\n\u003Ctd>m\u003C\u002Ftd>\n\u003Ctd>m\u003C\u002Ftd>\n\u003Ctd>m\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>110\u003C\u002Ftd>\n\u003Ctd>n\u003C\u002Ftd>\n\u003Ctd>n\u003C\u002Ftd>\n\u003Ctd>n\u003C\u002Ftd>\n\u003Ctd>n\u003C\u002Ftd>\n\u003Ctd>n\u003C\u002Ftd>\n\u003Ctd>n\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>111\u003C\u002Ftd>\n\u003Ctd>o\u003C\u002Ftd>\n\u003Ctd>o\u003C\u002Ftd>\n\u003Ctd>o\u003C\u002Ftd>\n\u003Ctd>o\u003C\u002Ftd>\n\u003Ctd>o\u003C\u002Ftd>\n\u003Ctd>o\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>112\u003C\u002Ftd>\n\u003Ctd>p\u003C\u002Ftd>\n\u003Ctd>p\u003C\u002Ftd>\n\u003Ctd>p\u003C\u002Ftd>\n\u003Ctd>p\u003C\u002Ftd>\n\u003Ctd>p\u003C\u002Ftd>\n\u003Ctd>p\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>113\u003C\u002Ftd>\n\u003Ctd>q\u003C\u002Ftd>\n\u003Ctd>q\u003C\u002Ftd>\n\u003Ctd>q\u003C\u002Ftd>\n\u003Ctd>q\u003C\u002Ftd>\n\u003Ctd>q\u003C\u002Ftd>\n\u003Ctd>q\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>114\u003C\u002Ftd>\n\u003Ctd>r\u003C\u002Ftd>\n\u003Ctd>r\u003C\u002Ftd>\n\u003Ctd>r\u003C\u002Ftd>\n\u003Ctd>r\u003C\u002Ftd>\n\u003Ctd>r\u003C\u002Ftd>\n\u003Ctd>r\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>115\u003C\u002Ftd>\n\u003Ctd>s\u003C\u002Ftd>\n\u003Ctd>s\u003C\u002Ftd>\n\u003Ctd>s\u003C\u002Ftd>\n\u003Ctd>s\u003C\u002Ftd>\n\u003Ctd>s\u003C\u002Ftd>\n\u003Ctd>s\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>116\u003C\u002Ftd>\n\u003Ctd>t\u003C\u002Ftd>\n\u003Ctd>t\u003C\u002Ftd>\n\u003Ctd>t\u003C\u002Ftd>\n\u003Ctd>t\u003C\u002Ftd>\n\u003Ctd>t\u003C\u002Ftd>\n\u003Ctd>t\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>117\u003C\u002Ftd>\n\u003Ctd>u\u003C\u002Ftd>\n\u003Ctd>u\u003C\u002Ftd>\n\u003Ctd>u\u003C\u002Ftd>\n\u003Ctd>u\u003C\u002Ftd>\n\u003Ctd>u\u003C\u002Ftd>\n\u003Ctd>u\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>118\u003C\u002Ftd>\n\u003Ctd>v\u003C\u002Ftd>\n\u003Ctd>v\u003C\u002Ftd>\n\u003Ctd>v\u003C\u002Ftd>\n\u003Ctd>v\u003C\u002Ftd>\n\u003Ctd>v\u003C\u002Ftd>\n\u003Ctd>v\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>119\u003C\u002Ftd>\n\u003Ctd>w\u003C\u002Ftd>\n\u003Ctd>w\u003C\u002Ftd>\n\u003Ctd>w\u003C\u002Ftd>\n\u003Ctd>w\u003C\u002Ftd>\n\u003Ctd>w\u003C\u002Ftd>\n\u003Ctd>w\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>120\u003C\u002Ftd>\n\u003Ctd>x\u003C\u002Ftd>\n\u003Ctd>x\u003C\u002Ftd>\n\u003Ctd>x\u003C\u002Ftd>\n\u003Ctd>x\u003C\u002Ftd>\n\u003Ctd>x\u003C\u002Ftd>\n\u003Ctd>x\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>121\u003C\u002Ftd>\n\u003Ctd>y\u003C\u002Ftd>\n\u003Ctd>y\u003C\u002Ftd>\n\u003Ctd>y\u003C\u002Ftd>\n\u003Ctd>y\u003C\u002Ftd>\n\u003Ctd>y\u003C\u002Ftd>\n\u003Ctd>y\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>122\u003C\u002Ftd>\n\u003Ctd>z\u003C\u002Ftd>\n\u003Ctd>z\u003C\u002Ftd>\n\u003Ctd>z\u003C\u002Ftd>\n\u003Ctd>z\u003C\u002Ftd>\n\u003Ctd>z\u003C\u002Ftd>\n\u003Ctd>z\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>123\u003C\u002Ftd>\n\u003Ctd>{\u003C\u002Ftd>\n\u003Ctd>%7B\u003C\u002Ftd>\n\u003Ctd>%7B\u003C\u002Ftd>\n\u003Ctd>%7B\u003C\u002Ftd>\n\u003Ctd>%7B\u003C\u002Ftd>\n\u003Ctd>%7B\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>124\u003C\u002Ftd>\n\u003Ctd>|\u003C\u002Ftd>\n\u003Ctd>%7C\u003C\u002Ftd>\n\u003Ctd>%7C\u003C\u002Ftd>\n\u003Ctd>%7C\u003C\u002Ftd>\n\u003Ctd>%7C\u003C\u002Ftd>\n\u003Ctd>%7C\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>125\u003C\u002Ftd>\n\u003Ctd>}\u003C\u002Ftd>\n\u003Ctd>%7D\u003C\u002Ftd>\n\u003Ctd>%7D\u003C\u002Ftd>\n\u003Ctd>%7D\u003C\u002Ftd>\n\u003Ctd>%7D\u003C\u002Ftd>\n\u003Ctd>%7D\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>126\u003C\u002Ftd>\n\u003Ctd>~\u003C\u002Ftd>\n\u003Ctd>~\u003C\u002Ftd>\n\u003Ctd>~\u003C\u002Ftd>\n\u003Ctd>~\u003C\u002Ftd>\n\u003Ctd>%7E\u003C\u002Ftd>\n\u003Ctd>~\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>127\u003C\u002Ftd>\n\u003Ctd>删除\u003C\u002Ftd>\n\u003Ctd>%7F\u003C\u002Ftd>\n\u003Ctd>%7F\u003C\u002Ftd>\n\u003Ctd>%7F\u003C\u002Ftd>\n\u003Ctd>%7F\u003C\u002Ftd>\n\u003Ctd>%7F\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>总结一下：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>非保留字符的处理上，非常一致（除了\u003Ccode>~\u003C\u002Fcode>）\u003C\u002Fli>\n\u003Cli>JS的\u003Ccode>encodeURI\u003C\u002Fcode>方法保留了很多符号，这些符号没有进行编码\u003C\u002Fli>\n\u003Cli>JS的\u003Ccode>encodeURIComponent\u003C\u002Fcode>方法相比PHP和Python，少了\u003Ccode>!\u003C\u002Fcode>、\u003Ccode>'\u003C\u002Fcode>、\u003Ccode>（\u003C\u002Fcode>、\u003Ccode>）\u003C\u002Fcode>、\u003Ccode>*\u003C\u002Fcode>这5个字符的编码\u003C\u002Fli>\n\u003Cli>PHP的\u003Ccode>urlencode\u003C\u002Fcode>方法将空格编码成了加号（\u003Ccode>+\u003C\u002Fcode>），且对\u003Ccode>~\u003C\u002Fcode>进行不必要的编码\u003C\u002Fli>\n\u003Cli>Python默认没有对\u003Ccode>\u002F\u003C\u002Fcode>进行编码，需要显式指定\u003Ccode>safe=''\u003C\u002Fcode>才会进行编码（\u003Ccode>urllib.parse.quote(str, safe='')\u003C\u002Fcode>）\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>如果按照业界“非保留字符一律进行编码”的实践规则来看，那么Python（指定\u003Ccode>safe=''\u003C\u002Fcode>）和PHP（\u003Ccode>rawurlencode\u003C\u002Fcode>）是符合要求的，而JS的\u003Ccode>encodeURIComponent\u003C\u002Fcode>则需要针对额外的5个字符打补丁。\u003C\u002Fp>\n\u003Cpre>\u003Ccode>function fixedEncodeURIComponent (str) {\n  return encodeURIComponent(str).replace(\u002F[!'()*]\u002Fg, function(c) {\n    return '%' + c.charCodeAt(0).toString(16);\n  });\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2>善后\u003C\u002Fh2>\n\u003Cp>因为结论比较明显，业界和主流语言都采用了比较一致的规则，因此最终这个项目的鉴权部分也进行了修改，除了非保留字符外，其他的字符都需要进行百分号编码。\u003C\u002Fp>\n\u003Cp>urlencode由于规范中没有规定得非常细致，将很多细节交给了实现，因此导致各语言的处理并不一致。当然如果能提前预知有这样的问题，有可能在选择方案的时候就不会选择urlencode这么一种“不太确定”的编码规则。\u003C\u002Fp>\n\u003Cblockquote>\n\u003Cp>如果你认为JSON大法好，恭喜你即将进行另一个坑：不同语言在JSON编码的处理上也不一致，例如中文要不要变成unicode格式、斜杠要不要编码等等。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>大概一位前端工程师也没想到有一天需要在后端处理“兼容性问题”。好在处理兼容性问题的原则是一致的：找到差异点、抹平它。\u003C\u002Fp>\n","\u003Cp>Urlencode又称百分号编码，是一种很常用的编码方式，作为前端工程师，少不了与它要打交道。不管是GET请求发送参数，还是POST请求发送body，都少不了要使用Urlencode来编码。\u003C\u002Fp>\n\u003Cp>而Urlencode的编码规则又特别简单：取出字符的ASCII码，转成16进制，然后前面加上百分号即可。如果是多字节的字符，则取出每一字节，按照同样的规则进行转换即可。例如问号\u003Ccode>?\u003C\u002Fcode>的ASCII码为\u003Ccode>63\u003C\u002Fcode>，转换为16进制为\u003Ccode>3F\u003C\u002Fcode>，所以\u003Ccode>%3F\u003C\u002Fcode>即为\u003Ccode>?\u003C\u002Fcode>进行Urlencode编码的结果。\u003C\u002Fp>\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fassets.toobug.net\u002Fassets\u002Fwork-with-urlencode\u002F1.png\" alt=\"urlencode\">\u003C\u002Fp>\n\u003Ch2>背景\u003C\u002Fh2>\n\u003Cp>项目需要对外提供HTTP API接口，因此接口鉴权成为一个很重要的内容。为了确保安全，防止中间人篡改数据或进行重放攻击，双方约定的私钥不可以直接出现在请求中，因此采用请求签名的方式来鉴权。\u003C\u002Fp>\n\u003Cp>双方约定appKey和appSecret，其中appKey用于识别请求对象，appSecret用于请求签名。具体的方案如下：\u003C\u002Fp>\n\u003Col>\n\u003Cli>客户端按照当前时间生成时间戳\u003Ccode>timestamp\u003C\u002Fcode>和随机数\u003Ccode>nonce\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>客户端按照指定的规则将HTTP请求的queryString和POST的body进行编码，得到一个字符串\u003Ccode>data\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>将\u003Ccode>timestamp\u003C\u002Fcode>、\u003Ccode>nonce\u003C\u002Fcode>和\u003Ccode>data\u003C\u002Fcode>按规则拼接，然后使用\u003Ccode>appSecret\u003C\u002Fcode>计算签名\u003C\u002Fli>\n\u003Cli>将\u003Ccode>appKey\u003C\u002Fcode>、\u003Ccode>timestamp\u003C\u002Fcode>、\u003Ccode>nonce\u003C\u002Fcode>和签名一起随请求发出\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>服务端在接到请求后将使用获得的数据和\u003Ccode>appSecret\u003C\u002Fcode>重新计算签名，然后判断与客户端给出的签名值是否一致，如果不一致则鉴权不通过。\u003C\u002Fp>\n\u003Cp>这一套鉴权机制可以有效防御一些攻击手段：\u003C\u002Fp>\n\u003Cul>\n\u003Cli>使用了时间戳，可以避免过期请求被重发\u003C\u002Fli>\n\u003Cli>使用了随机数，可以避免请求被短时间重复发送\u003C\u002Fli>\n\u003Cli>签名数据包含了完整的时间戳、随机数和请求数据，保证服务端收到的确实是客户端发送的数据，避免被拦截修改\u003C\u002Fli>\n\u003Cli>签名的密钥是双方协商好的，避免请求伪造\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>踩坑\u003C\u002Fh2>\n\u003Cp>在上面的鉴权过程中，一个非常重要的点就是第2点，即将请求的queryString和POST的body进行编码，得到一个字符串。\u003C\u002Fp>\n\u003Cp>因为GET和POST请求中，数据都会被Urlencode编码，因此很容易想到，我们也使用Urlencode来进行这个鉴权前的编码过程。\u003C\u002Fp>\n\u003Cp>于是坑就这么不期而遇了。\u003C\u002Fp>\n","https:\u002F\u002Fassets.toobug.net\u002Fassets\u002Fwork-with-urlencode\u002F1.png",{"total":20,"totalRoots":20,"comments":27,"pv":20},[]]