URI参数签名算法
简介
应用基于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×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<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参数签名算法的更多相关文章
- PHP、Java、C#实现URI参数签名算法,确保应用与REST服务器之间的安全通信,防止Secret Key盗用、数据篡改等恶意攻击行为
简介 应用基于HTTP POST或HTTP GET请求发送Open API调用请求时,为了确保应用与REST服务器之间的安全通信,防止Secret Key盗用.数据篡改等恶意攻击行为,REST服务器使 ...
- redirect uri 参数错误 怎么办
这种情况,多数是因为请求地址不合法所致. 去公众号中添加合法的地址. 这种地址需要满足一些条件. 设置地址 满足的条件 保证可以访问到安全文件 如果访问不到的话,将无法保存 这里是文件存放位置 经过这 ...
- 【Win 10 应用开发】分析 URI 中的查询字符串
分析URI中的字符有K种方法(K >= 2),如果查询字符串中的参数比较简单,可以通过子字符串查找的方式来处理:如果查询字符串相对复杂,你可以使用正则表达式来匹配 key1=value1 , ...
- 【Win10 UWP】URI Scheme(一):Windows Store协议的解析和使用
协议是Windows Phone和Windows Store应用的一个重要特点,可以做到在不同应用之间进行互相呼起调用.小小协议,学问大着呢.我打算写几篇关于协议在UWP中使用的文章. 这一讲的主要对 ...
- CodeIgniter框架——访问方式 URI 分配变量 数据库操作
1.访问方式: CodeIgniter 的访问URL使用的是pathinfo,入口文件/控制器/方法(/参数列表) eg:localhost/index.php/welcome/index/id 第一 ...
- ElasticSearch URI 查询
公号:码农充电站pro 主页:https://codeshellme.github.io 1,URI 查询格式 URI 查询的一般格式如下: GET /index_name/_search?q=key ...
- HMAC-SHA256 签名方法各个语音的实现方式之前端JavaScriptes6
sha256和16进制输出,网上很多种后端的验证方法,几乎没有前端的,所以自己写了个,希望给类似需求的人一个帮助,适用场景 腾讯云接口鉴权 v3签名 npm install sha256npm ins ...
- 【UWP开源】图片编辑器,带贴图、滤镜、涂鸦等功能
目录 说明 功能 实现原理 使用方法 效果截图 说明 最近空余时间研究了一下Win2D,它能为我们在UWP中提供一种类似GDI那样的绘图方法.就像传统Winform.MFC中那样重写OnPaint相关 ...
- BaaS API 设计规范
上个月写了一个团队中的 BaaS API 的设计规范,给大家分享下: 目录 1. 引言... 4 1.1. 概要... 4 1.2. 参考资料... 4 1.3. 阅读对象... 4 1.4. 术语解 ...
随机推荐
- Codeforces Round #309 (Div. 2) -D. Kyoya and Permutation
Kyoya and Permutation 这题想了好久才写出来,没看题解写出来的感觉真的好爽啊!!! 题目大意:题意我看了好久才懂,就是给你一个序列,比如[4, 1, 6, 2, 5, 3],第一个 ...
- echarts 折线统计笔记
效果案例图 需要引入的js文件可以直接去官网下载 下面是代码 <!--第一步: 引入 ECharts 文件 --> <script src="static/js/myjs/ ...
- Java中九大内置对象
1.Request对象 该对象封装了用户提交的信息,通过调用该对象相应的方法可以获取封装的信息,即使用该对象可以获取用户提交的信息. 当Request对象获取客户提交的汉字字符时,会出现乱码问题 ...
- tensorflow相关API的学习
学习目录 1.tensorflow相关函数理解 (1)tf.nn.conv2d (2)tf.nn.relu (3)tf.nn.max_pool (4)tf.nn.dropout (5)tf.nn.si ...
- 生产环境中tomcat的配置
生产环境中要以daemon方式运行tomcat 通常在开发环境中,我们使用$CATALINA_HOME/bin/startup.sh来启动tomcat, 使用$CATALINA_HOME/bin/sh ...
- 5288: [Hnoi2018]游戏
5288: [Hnoi2018]游戏 链接 分析: 考虑y<=x的怎么做,那么只能从左边走到右边.我们可以从最右边的点开始,一次确定每个点往右边可以走多少. L[x],R[x]分别是x向左向右最 ...
- POJ.1704.Georgia and Bob(博弈论 Nim)
题目链接 \(Description\) 一个1~INF的坐标轴上有n个棋子,给定坐标Pi.棋子只能向左走,不能跨越棋子,且不能越界(<1).两人每次可以将任意一个可移动的棋子向左移动一个单位. ...
- Python3练习题系列(01)
2018-06-13 题目: 根据用户回答做出相应的判断,完成一个“回答-判断”的小游戏 Python3知识点: if, else, elif 实例代码: print("You enter ...
- Postman使用记录
1. 情况: 当 本地需要测试,线上的项目也需要同时测试时,地址了输入还需要打开多个窗口,麻烦 点击圆圈部分 testForm是我新建的 输入请求的action名字 点击齿轮按钮: 地址栏填入方式: ...
- SpringMVC页面传值
public ModelAndView query(){ ModelAndView modelAndView = new ModelAndView(); List list = new ArrayLi ...