简介

应用基于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=-- ::09uid=
  • 在拼接好的字符串末尾追加上应用通过百度OAuth2.0协议获取Access Token时所获取到的session_secret参数值,得到如下字符串:
format=jsonsession_key=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A=timestamp=-- ::09uid=6741116727e1be4fdcaa83d7f61c489994ff6ed6
  • 对前面得到的字符串求MD5签名,得到的d24dd357a95a2579c410b3a92495f009就是调用API时所需要的sign参数值。

接下来便可以通过HTTP POST方法或HTTP GET方法请求百度Open API的REST服务器,进行接口调用了,如:

GET /rest/2.0/passport/users/getInfo?session_key=9XNNXe66zOlSassjSKD5gry9BiN61IUEi8IpJmjBwvU07RXP0J3c4GnhZR3GKhMHa1A%3D&timestamp=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&timestamp=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<String,String> params 请求参数集,所有参数必须已转换为字符串类型
* @param String secret 签名密钥
* @return 签名
* @throws IOException
*/
public static String getSignature(HashMap<String,String> params, String secret) throws IOException
{
// 先将参数以其参数名的字典序升序进行排序
Map<String, String> sortedParams = new TreeMap<String, String>(params);
Set<Entry<String, String>> entrys = sortedParams.entrySet(); // 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起
StringBuilder basestring = new StringBuilder();
for (Entry<String, String> 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 < bytes.length; 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#代码实现方式如下所示:


/// <summary>
/// 计算参数签名
/// </summary>
/// <param name="params">请求参数集,所有参数必须已转换为字符串类型</param>
/// <param name="secret">签名密钥</param>
/// <returns>签名</returns>
public static string getSignature(IDictionary<string, string> parameters, string secret)
{
// 先将参数以其参数名的字典序升序进行排序
IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters);
IEnumerator<KeyValuePair<string, 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 = ; i < bytes.Length; i++) {
string hex = bytes[i].ToString("x");
if (hex.Length == ) {
result.Append("");
}
result.Append(hex);
} return result.ToString();
}


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

URI参数签名算法的更多相关文章

  1. PHP、Java、C#实现URI参数签名算法,确保应用与REST服务器之间的安全通信,防止Secret Key盗用、数据篡改等恶意攻击行为

    简介 应用基于HTTP POST或HTTP GET请求发送Open API调用请求时,为了确保应用与REST服务器之间的安全通信,防止Secret Key盗用.数据篡改等恶意攻击行为,REST服务器使 ...

  2. redirect uri 参数错误 怎么办

    这种情况,多数是因为请求地址不合法所致. 去公众号中添加合法的地址. 这种地址需要满足一些条件. 设置地址 满足的条件 保证可以访问到安全文件 如果访问不到的话,将无法保存 这里是文件存放位置 经过这 ...

  3. 【Win 10 应用开发】分析 URI 中的查询字符串

    分析URI中的字符有K种方法(K >= 2),如果查询字符串中的参数比较简单,可以通过子字符串查找的方式来处理:如果查询字符串相对复杂,你可以使用正则表达式来匹配 key1=value1 ,  ...

  4. 【Win10 UWP】URI Scheme(一):Windows Store协议的解析和使用

    协议是Windows Phone和Windows Store应用的一个重要特点,可以做到在不同应用之间进行互相呼起调用.小小协议,学问大着呢.我打算写几篇关于协议在UWP中使用的文章. 这一讲的主要对 ...

  5. CodeIgniter框架——访问方式 URI 分配变量 数据库操作

    1.访问方式: CodeIgniter 的访问URL使用的是pathinfo,入口文件/控制器/方法(/参数列表) eg:localhost/index.php/welcome/index/id 第一 ...

  6. ElasticSearch URI 查询

    公号:码农充电站pro 主页:https://codeshellme.github.io 1,URI 查询格式 URI 查询的一般格式如下: GET /index_name/_search?q=key ...

  7. HMAC-SHA256 签名方法各个语音的实现方式之前端JavaScriptes6

    sha256和16进制输出,网上很多种后端的验证方法,几乎没有前端的,所以自己写了个,希望给类似需求的人一个帮助,适用场景 腾讯云接口鉴权 v3签名 npm install sha256npm ins ...

  8. 【UWP开源】图片编辑器,带贴图、滤镜、涂鸦等功能

    目录 说明 功能 实现原理 使用方法 效果截图 说明 最近空余时间研究了一下Win2D,它能为我们在UWP中提供一种类似GDI那样的绘图方法.就像传统Winform.MFC中那样重写OnPaint相关 ...

  9. BaaS API 设计规范

    上个月写了一个团队中的 BaaS API 的设计规范,给大家分享下: 目录 1. 引言... 4 1.1. 概要... 4 1.2. 参考资料... 4 1.3. 阅读对象... 4 1.4. 术语解 ...

随机推荐

  1. (第8篇)实时可靠的开源分布式实时计算系统——Storm

    摘要: 在Hadoop生态圈中,针对大数据进行批量计算时,通常需要一个或者多个MapReduce作业来完成,但这种批量计算方式是满足不了对实时性要求高的场景.那Storm是怎么做到的呢? 博主福利 给 ...

  2. java:给你一个数组和两个索引,交换下标为这两个索引的数字

    给你一个数组和两个索引,交换下标为这两个索引的数字 import java.util.Arrays; public class Solution { public static void main(S ...

  3. JdbcType类型和Java类型的对应关系

    在Oracle中有些字段不是必填时在用户使用的时候会出现数据null的情况.这个时候在Oracle中是无法进行插入的. JDBC Type Java Type CHAR String VARCHAR ...

  4. Ubuntu (虚拟机同样) 更换内核?

    1:查看当前安装的内核dpkg -l|grep linux-image 2:查看可以更新的内核版本:sudo apt-cache search linux-image 3:安装新内核sudo apt- ...

  5. Python - 经典程序示例

    列表排序 def que6(): # 6.输入三个整数x, y, z,形成一个列表,请把这n个数由小到大输出. # 程序分析:列表有sort方法,所以把他们组成列表即可. li = np.random ...

  6. P1007 独木桥

    P1007 独木桥最少的话是同时朝一个方向,最多可以这样考虑,碰头时不是转向吗?它等价于穿过另一个人,也就是两个人交换,最后取最大值就好了. #include<iostream> usin ...

  7. conda 与 pip 加速

    conda和pip默认使用国外站点来下载软件,我们可以配置国内镜像来加速下载(国外用户无须此操作). 配置清华conda镜像 conda config --add channels https://m ...

  8. Springboot 2.0.x 集成基于Centos7的Redis集群安装及配置

    Redis简介 Redis是一个基于C语言开发的开源(BSD许可),开源高性能的高级内存数据结构存储,用作数据库.缓存和消息代理.它支持数据结构,如 字符串.散列.列表.集合,带有范围查询的排序集,位 ...

  9. window10 还原精灵 破解版 冰点

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha=======

  10. Microsoft Corporation 去掉 windows 修改 启动加载 版权

    windows 修改 开机界面 boot启动界面 windows 修改 启动加载 版权 windows 系统如何修改开机画面的版权文字“Microsoft Corporation ... ◎Micro ...