PHP、Java、C#实现URI参数签名算法,确保应用与REST服务器之间的
简介 应用基于HTTP POST或HTTP GET请求发送Open API调用请求时,为了确保应用与REST服务器之间的安全通信,防止Secret Key盗用、数据篡改等恶意攻击行为,REST服务器使用了参数签名机制。应用在调用Open API之前,需要为其所有请求参数计算一个MD5签名,并追
简介
应用基于HTTP POST或HTTP GET请求发送Open API调用请求时,为了确保应用与REST服务器之间的安全通信,防止Secret Key盗用、数据篡改等恶意攻击行为,REST服务器使用了参数签名机制。应用在调用Open API之前,需要为其所有请求参数计算一个MD5签名,并追加到请求参数中,参数名为“sign”。REST服务器在接收到请求时会重新计算签名,并判断其值是否与应用传递过来的sign参数值一致,以此判定当前Open API调用请求是否是被第三者伪造或篡改。
应用在调用Open API之前需要通过 OAuth2.0服务获得用户或平台的授权,获取到授权后将会拿到以下3个重要参数:
- access_token:基于https调用Open API时所需要的访问授权码;
- session_key:基于http调用Open API时所需要的访问授权码;
- session_secret:基于http调用Open API时计算参数签名用的签名密钥。
其中,session_secret这个参数就是做参数签名时所需要的签名密钥。这与Facebook、人人网等平台稍微有所区别,这两个平台在做参数签名时所用的签名密钥一般有2个:
- 如果是通过应用服务端调用Open API,则注册应用时所拿到的应用密钥(即API Key)就是参数签名密钥;
- 如果是通过JavaScript、ActionScript等客户端语言调用Open API,则应用获取到用户授权后所拿到的Session Secret就是参数签名密钥。当然,通过服务端调用Open API时也可以用Session Secret作为签名密钥。
签名算法
假设参与参数签名计算的请求参数分别是“k1”、“k2”、“k3”,它们的值分别是“v1”、“v2”、“v3”,则参数签名计算方法如下:
- 将请求参数格式化为“key=value”格式,即“k1=v1”、“k2=v2”、“k3=v3”;
- 将格式化好的参数键值对以字典序升序排列后,拼接在一起,即“k1=v1k2=v2k3=v3”;
- 在拼接好的字符串末尾追加上应用通过OAuth2.0协议获取Access Token时所获取到的session_secret参数值;
- 上述字符串的MD5值即为签名的值。
注意:计算签名时的请求参数中不要包含sign(签名)参数,因为sign参数的值此时还不知道,有待计算。
另外,计算签名的时候不需要对参数进行urlencode处理(“application/x-www-form-urlencoded”编码),但是发送请求的时候需要进行urlencode处理,这是很多开发者最容易犯错的地方。
签名过程示例
假设某个应用需要获取某个uid为67411167的用户的基本资料,应用在之前的通过OAuth2.0服务获取Access Token的过程中所拿到的session_key和session_secret参数值分别为:
- session_key: "9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A="
- session_secret: "27e1be4fdcaa83d7f61c489994ff6ed6"
调用Open API时的系统时间(PHP中可以通过date('Y-m-d H:i:s')来获取当前系统时间)为"2011-06-21 17:18:09",希望REST服务器以JSON格式返回调用结果,即相当于参与参数签名计算的请求参数集合为:
<span>[ </span>"session_key" => "9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A="<span>, </span>"timestamp" => "2011-06-21 17:18:09"<span>, </span>"format" => "json"<span>, </span>"uid" => 67411167<span> ]</span>
则计算签名的具体过程如下:
- 将请求参数格式化为“key=value”格式,格式化后的请求参数集合为:
<span> [ </span>"session_key=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A="<span>, </span>"timestamp=2011-06-21 17:18:09"<span>, </span>"format=json"<span>, </span>"uid=67411167"<span> ]</span>
- 将格式化好的参数键值对以字典序升序排列,得到如下参数集:
<span> [ </span>"format=json"<span>, </span>"session_key=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A="<span>, </span>"timestamp=2011-06-21 17:18:09"<span>, </span>"uid=67411167"<span> ]</span>
- 将前面排序好的参数集拼接在一起,得到如下字符串:
format<span>=</span>jsonsession_key<span>=</span>9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A<span>=</span>timestamp<span>=</span><span>2011</span><span>-</span><span>06</span><span>-</span><span>21</span> <span>17</span><span>:</span><span>18</span><span>:</span>09uid<span>=</span><span>67411167</span>
- 在拼接好的字符串末尾追加上应用通过OAuth2.0协议获取Access Token时所获取到的session_secret参数值,得到如下字符串:
format<span>=</span>jsonsession_key<span>=</span>9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A<span>=</span>timestamp<span>=</span><span>2011</span><span>-</span><span>06</span><span>-</span><span>21</span> <span>17</span><span>:</span><span>18</span><span>:</span>09uid<span>=</span>6741116727e1be4fdcaa83d7f61c489994ff6ed6
- 对前面得到的字符串求MD5签名,得到的d24dd357a95a2579c410b3a92495f009就是调用API时所需要的sign参数值。
接下来便可以通过HTTP POST方法或HTTP GET方法请求Open API的REST服务器,进行接口调用了,如:
GET /rest/2.0/passport/users/getInfo?session_key=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A%3D×tamp=2011-06-21+17%3A18%3A09&format=json&uid=67411167&sign=d24dd357a95a2579c410b3a92495f009 HTTP/1.1<span> Host: openapi.baidu.com User</span>-<span>Agent: Client of Baidu Open Platform Accept: </span>*<span>/*</span><span> Accept-Encoding: gzip,deflate Accept-Charset: utf-8 Connection: close 或 POST /rest/2.0/passport/users/getInfo HTTP/1.1 Host: openapi.baidu.com User-Agent: Client of Baidu Open Platform Accept: </span><span>*/</span>*<span> Accept</span>-<span>Encoding: gzip,deflate Accept</span>-Charset: utf-8<span> Content</span>-Length: 179<span> Connection: close session_key</span>=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A%3D×tamp=2011-06-21+17%3A18%3A09&format=json&uid=67411167&sign=d24dd357a95a2579c410b3a92495f009
签名算法实现代码
PHP代码实现
获取签名的PHP代码实现方式如下所示:
<span>/*</span><span>* * 签名生成算法 * @param array $params API调用的请求参数集合的关联数组,不包含sign参数 * @param string $secret 签名的密钥即获取access token时返回的session secret * @return string 返回参数签名值 </span><span>*/</span> <span>function</span> getSignature(<span>$params</span>, <span>$secret</span><span>) { </span><span>$str</span> = ''; <span>//</span><span>待签名字符串 //先将参数以其参数名的字典序升序进行排序</span> <span>ksort</span>(<span>$params</span><span>); </span><span>//</span><span>遍历排序后的参数数组中的每一个key/value对</span> <span>foreach</span> (<span>$params</span> <span>as</span> <span>$k</span> => <span>$v</span><span>) { </span><span>//</span><span>为key/value对生成一个key=value格式的字符串,并拼接到待签名字符串后面</span> <span>$str</span> .= "<span>$k</span>=<span>$v</span>"<span>; } </span><span>//</span><span>将签名密钥拼接到签名字符串最后面</span> <span>$str</span> .= <span>$secret</span><span>; </span><span>//</span><span>通过md5算法为签名字符串生成一个md5签名,该签名就是我们要追加的sign参数值</span> <span>return</span> <span>md5</span>(<span>$str</span><span>); }</span>
调用示例:
<span>$uid</span> = 67411167<span>; </span><span>$params</span> = <span>array</span><span>( </span>"session_key" => "9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A=", "timestamp" => "2011-06-21 17:18:09", "format" => "json", "uid" => <span>$uid</span>,<span> ); </span><span>$sign</span> = getSignature(<span>$params</span>, "27e1be4fdcaa83d7f61c489994ff6ed6");
Java代码实现
获取签名的java代码实现方式如下所示:
<span>/**</span><span> * 签名生成算法 * </span><span>@param</span><span> HashMap<string> params 请求参数集,所有参数必须已转换为字符串类型 * </string></span><span>@param</span><span> String secret 签名密钥 * </span><span>@return</span><span> 签名 * </span><span>@throws</span><span> IOException </span><span>*/</span> <span>public</span> <span>static</span> String getSignature(HashMap<string> params, String secret) <span>throws</span><span> IOException { </span><span>//</span><span> 先将参数以其参数名的字典序升序进行排序</span> Map<string string> sortedParams = <span>new</span> TreeMap<string string><span>(params); Set</span><entry string>> entrys =<span> sortedParams.entrySet(); </span><span>//</span><span> 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起</span> StringBuilder basestring = <span>new</span><span> StringBuilder(); </span><span>for</span> (Entry<string string><span> param : entrys) { basestring.append(param.getKey()).append(</span>"="<span>).append(param.getValue()); } basestring.append(secret); </span><span>//</span><span> 使用MD5对待签名串求签</span> <span>byte</span>[] bytes = <span>null</span><span>; </span><span>try</span><span> { MessageDigest md5 </span>= MessageDigest.getInstance("MD5"<span>); bytes </span>= md5.digest(basestring.toString().getBytes("UTF-8"<span>)); } </span><span>catch</span><span> (GeneralSecurityException ex) { </span><span>throw</span> <span>new</span><span> IOException(ex); } </span><span>//</span><span> 将MD5输出的二进制结果转换为小写的十六进制</span> StringBuilder sign = <span>new</span><span> StringBuilder(); </span><span>for</span> (<span>int</span> i = 0; i ) { String hex = Integer.toHexString(bytes[i] & 0xFF<span>); </span><span>if</span> (hex.length() == 1<span>) { sign.append(</span>"0"<span>); } sign.append(hex); } </span><span>return</span><span> sign.toString(); }</span></string></entry></string></string></string>
注意:计算签名时所有参数的key和value都必须先转换为对应的字符串类型,因为在HTTP请求中传递的内容都是字符串类型的,很多开发者都因为没注意到这点,直接将非字符串类型的参数的二进制值传递了进去,结果导致签名与服务端计算的不一致而出错。
C#代码实现
获取签名的C#代码实现方式如下所示:
<span>///</span> <span><summary></summary></span> <span>///</span><span> 计算参数签名 </span><span>///</span> <span></span> <span>///</span> <span><param name="params"></span><span>请求参数集,所有参数必须已转换为字符串类型</span><span></span> <span>///</span> <span><param name="secret"></span><span>签名密钥</span><span></span> <span>///</span> <span><returns></returns></span><span>签名</span><span></span> <span>public</span> <span>static</span> <span>string</span> getSignature(IDictionarystring, <span>string</span>> parameters, <span>string</span><span> secret) { </span><span>//</span><span> 先将参数以其参数名的字典序升序进行排序</span> IDictionarystring, <span>string</span>> sortedParams = <span>new</span> SortedDictionarystring, <span>string</span>><span>(parameters); IEnumerator</span><keyvaluepair>string, <span>string</span>>> iterator=<span> sortedParams.GetEnumerator(); </span><span>//</span><span> 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起</span> StringBuilder basestring= <span>new</span><span> StringBuilder(); </span><span>while</span><span> (iterator.MoveNext()) { </span><span>string</span> key =<span> iterator.Current.Key; </span><span>string</span> value =<span> iterator.Current.Value; </span><span>if</span> (!<span>string</span>.IsNullOrEmpty(key) && !<span>string</span><span>.IsNullOrEmpty(value)){ basestring.Append(key).Append(</span><span>"</span><span>=</span><span>"</span><span>).Append(value); } } basestring.Append(secret); </span><span>//</span><span> 使用MD5对待签名串求签</span> MD5 md5 =<span> MD5.Create(); </span><span>byte</span>[] bytes =<span> md5.ComputeHash(Encoding.UTF8.GetBytes(basestring.ToString())); </span><span>//</span><span> 将MD5输出的二进制结果转换为小写的十六进制</span> StringBuilder result = <span>new</span><span> StringBuilder(); </span><span>for</span> (<span>int</span> i = <span>0</span>; i ) { <span>string</span> hex = bytes[i].ToString(<span>"</span><span>x</span><span>"</span><span>); </span><span>if</span> (hex.Length == <span>1</span><span>) { result.Append(</span><span>"</span><span>0</span><span>"</span><span>); } result.Append(hex); } </span><span>return</span><span> result.ToString(); }</span></keyvaluepair>
服务器接受请求后,同样对参数进行签名,如果签名相同则数据没有被修改或者丢失。
注意:计算签名时所有参数的key和value都必须先转换为对应的字符串类型,因为在HTTP请求中传递的内容都是字符串类型的,很多开发者都因为没注意到这点,直接将非字符串类型的参数的二进制值传递了进去,结果导致签名与服务端计算的不一致而出错。

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

PHP 8.4 带来了多项新功能、安全性改进和性能改进,同时弃用和删除了大量功能。 本指南介绍了如何在 Ubuntu、Debian 或其衍生版本上安装 PHP 8.4 或升级到 PHP 8.4

Visual Studio Code,也称为 VS Code,是一个免费的源代码编辑器 - 或集成开发环境 (IDE) - 可用于所有主要操作系统。 VS Code 拥有针对多种编程语言的大量扩展,可以轻松编写

本教程演示了如何使用PHP有效地处理XML文档。 XML(可扩展的标记语言)是一种用于人类可读性和机器解析的多功能文本标记语言。它通常用于数据存储

字符串是由字符组成的序列,包括字母、数字和符号。本教程将学习如何使用不同的方法在PHP中计算给定字符串中元音的数量。英语中的元音是a、e、i、o、u,它们可以是大写或小写。 什么是元音? 元音是代表特定语音的字母字符。英语中共有五个元音,包括大写和小写: a, e, i, o, u 示例 1 输入:字符串 = "Tutorialspoint" 输出:6 解释 字符串 "Tutorialspoint" 中的元音是 u、o、i、a、o、i。总共有 6 个元

Java 8引入了Stream API,提供了一种强大且表达力丰富的处理数据集合的方式。然而,使用Stream时,一个常见问题是:如何从forEach操作中中断或返回? 传统循环允许提前中断或返回,但Stream的forEach方法并不直接支持这种方式。本文将解释原因,并探讨在Stream处理系统中实现提前终止的替代方法。 延伸阅读: Java Stream API改进 理解Stream forEach forEach方法是一个终端操作,它对Stream中的每个元素执行一个操作。它的设计意图是处

如果您是一位经验丰富的 PHP 开发人员,您可能会感觉您已经在那里并且已经完成了。您已经开发了大量的应用程序,调试了数百万行代码,并调整了一堆脚本来实现操作

Java是热门编程语言,适合初学者和经验丰富的开发者学习。本教程从基础概念出发,逐步深入讲解高级主题。安装Java开发工具包后,可通过创建简单的“Hello,World!”程序实践编程。理解代码后,使用命令提示符编译并运行程序,控制台上将输出“Hello,World!”。学习Java开启了编程之旅,随着掌握程度加深,可创建更复杂的应用程序。

JavaMadeSimple:ABeginner'sGuidetoProgrammingPower简介Java是一种强大的编程语言,广泛应用于从移动应用程序到企业级系统的各种领域。对于初学者来说,Java的语法简洁易懂,是学习编程的理想选择。基本语法Java使用基于类的面向对象编程范式。类是将相关数据和行为组织在一起的模板。以下是一个简单的Java类示例:publicclassPerson{privateStringname;privateintage;
