PHP、Java、C#实现URI参数签名算法,确保应用与REST服务器之间的

WBOY
發布: 2016-06-06 19:44:20
原創
1721 人瀏覽過

简介 应用基于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格式返回调用结果,即相当于参与参数签名计算的请求参数集合为:


              
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

[ "session_key" => "9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A=", "timestamp" => "2011-06-21 17:18:09", "format" => "json", "uid" => 67411167 ]
登入後複製

           
登入後複製
登入後複製
登入後複製
登入後複製

则计算签名的具体过程如下:

  • 将请求参数格式化为“key=value”格式,格式化后的请求参数集合为:

 [ "session_key=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A=", "timestamp=2011-06-21 17:18:09", "format=json", "uid=67411167" ]
登入後複製

           
登入後複製
登入後複製
登入後複製
登入後複製
  • 将格式化好的参数键值对以字典序升序排列,得到如下参数集:

 [ "format=json", "session_key=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A=", "timestamp=2011-06-21 17:18:09", "uid=67411167" ]
登入後複製

           
登入後複製
登入後複製
登入後複製
登入後複製
  • 将前面排序好的参数集拼接在一起,得到如下字符串:
format=jsonsession_key=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A=timestamp=2011-06-21 17:18:09uid=67411167
登入後複製
  • 在拼接好的字符串末尾追加上应用通过OAuth2.0协议获取Access Token时所获取到的session_secret参数值,得到如下字符串:
format=jsonsession_key=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A=timestamp=2011-06-21 17:18:09uid=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 Host: openapi.baidu.com User-Agent: Client of Baidu Open Platform Accept: */* 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: */* Accept-Encoding: gzip,deflate Accept-Charset: utf-8 Content-Length: 179 Connection: close session_key=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A%3D×tamp=2011-06-21+17%3A18%3A09&format=json&uid=67411167&sign=d24dd357a95a2579c410b3a92495f009
登入後複製

签名算法实现代码

PHP代码实现

获取签名的PHP代码实现方式如下所示:

/** * 签名生成算法 * @param array $params API调用的请求参数集合的关联数组,不包含sign参数 * @param string $secret 签名的密钥即获取access token时返回的session secret * @return string 返回参数签名值 */ function getSignature($params, $secret) { $str = ''; //待签名字符串 //先将参数以其参数名的字典序升序进行排序 ksort($params); //遍历排序后的参数数组中的每一个key/value对 foreach ($params as $k => $v) { //为key/value对生成一个key=value格式的字符串,并拼接到待签名字符串后面 $str .= "$k=$v"; } //将签名密钥拼接到签名字符串最后面 $str .= $secret; //通过md5算法为签名字符串生成一个md5签名,该签名就是我们要追加的sign参数值 return md5($str); }
登入後複製

调用示例:

$uid = 67411167; $params = array( "session_key" => "9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A=", "timestamp" => "2011-06-21 17:18:09", "format" => "json", "uid" => $uid, ); $sign = getSignature($params, "27e1be4fdcaa83d7f61c489994ff6ed6");
登入後複製

           
登入後複製
登入後複製
登入後複製
登入後複製

Java代码实现

获取签名的java代码实现方式如下所示:


              
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

/** * 签名生成算法 * @param HashMap params 请求参数集,所有参数必须已转换为字符串类型 * @param String secret 签名密钥 * @return 签名 * @throws IOException */ public static String getSignature(HashMap params, String secret) throws IOException { // 先将参数以其参数名的字典序升序进行排序 Map sortedParams = new TreeMap(params); Set> entrys = sortedParams.entrySet(); // 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起 StringBuilder basestring = new StringBuilder(); for (Entry param : entrys) { basestring.append(param.getKey()).append("=").append(param.getValue()); } basestring.append(secret); // 使用MD5对待签名串求签 byte[] bytes = null; try { MessageDigest md5 = MessageDigest.getInstance("MD5"); bytes = md5.digest(basestring.toString().getBytes("UTF-8")); } catch (GeneralSecurityException ex) { throw new IOException(ex); } // 将MD5输出的二进制结果转换为小写的十六进制 StringBuilder sign = new StringBuilder(); for (int i = 0; i ) { String hex = Integer.toHexString(bytes[i] & 0xFF); if (hex.length() == 1) { sign.append("0"); } sign.append(hex); } return sign.toString(); }
登入後複製

              
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製


              
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

注意:计算签名时所有参数的key和value都必须先转换为对应的字符串类型,因为在HTTP请求中传递的内容都是字符串类型的,很多开发者都因为没注意到这点,直接将非字符串类型的参数的二进制值传递了进去,结果导致签名与服务端计算的不一致而出错。

C#代码实现

获取签名的C#代码实现方式如下所示:


              
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

///  /// 计算参数签名 ///  /// 请求参数集,所有参数必须已转换为字符串类型 /// 签名密钥 /// 签名 public static string getSignature(IDictionarystring, string> parameters, string secret) { // 先将参数以其参数名的字典序升序进行排序 IDictionarystring, string> sortedParams = new SortedDictionarystring, string>(parameters); IEnumeratorstring, string>> iterator= sortedParams.GetEnumerator(); // 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起 StringBuilder basestring= new StringBuilder(); while (iterator.MoveNext()) { string key = iterator.Current.Key; string value = iterator.Current.Value; if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value)){ basestring.Append(key).Append("=").Append(value); } } basestring.Append(secret); // 使用MD5对待签名串求签 MD5 md5 = MD5.Create(); byte[] bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(basestring.ToString())); // 将MD5输出的二进制结果转换为小写的十六进制 StringBuilder result = new StringBuilder(); for (int i = 0; i ) { string hex = bytes[i].ToString("x"); if (hex.Length == 1) { result.Append("0"); } result.Append(hex); } return result.ToString(); }
登入後複製

              
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

服务器接受请求后,同样对参数进行签名,如果签名相同则数据没有被修改或者丢失。


              
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

注意:计算签名时所有参数的key和value都必须先转换为对应的字符串类型,因为在HTTP请求中传递的内容都是字符串类型的,很多开发者都因为没注意到这点,直接将非字符串类型的参数的二进制值传递了进去,结果导致签名与服务端计算的不一致而出错。

來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門推薦
    最新下載
    更多>
    網站特效
    網站源碼
    網站素材
    前端模板
    關於我們 免責聲明 Sitemap
    PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!