生成认证TokenV1(不推荐)
概述
智能外呼开放平台接口,每次调用均需进行Token认证鉴权。
使用前提
开通智能外呼产品
生成公式
认证字符串的格式:
cc-api-auth-v{version}/{accessKeyId}/{timestamp}/{expirationPeriodInSeconds}/{signedHeaders}/{signature}。
主要包含了3部分,即前缀字符串(authStringPrefix)、签名头域(signedHeaders)和签名摘要(signature),其中: 前缀字符串:cc-api-auth-v1/{AK}/{timestamp}/{expirationPeriodInSeconds } 签名头域:即签名算法中涉及到的HTTP头域列表 签名摘要:为通过HMAC SHA256算法计算所得
编码生成步骤
创建前缀字符串
将已知参数拼接为如下形式: cc-api-auth-v1/{AK}/{timestamp}/{expirationPeriodInSeconds}
- AK 可通过在配置台 系统管理-> API配置页面获取到
- timestamp 为token 生成的时间,格式为2014-06-01T23:00:10Z表示UTC时间2014年6月1日23点0分10秒
- expirationPeriodInSeconds 签名有效期限,从timestamp所指定的时间开始计算,单位为秒。
创建规范请求(canonicalRequest),确定签名头域(signedHeaders)
CanonicalRequest的计算公式为:
CanonicalRequest = HTTP Method + "\n" + CanonicalURI + "\n" + CanonicalQueryString + "\n" + CanonicalHeaders。
HTTP Method
指HTTP协议中定义的GET、PUT、POST等请求方法,必须使用全大写的形式
GET
POST
PUT
DELETE
HEAD
CanonicalURI
CanonicalURI是对URL中的绝对路径进行编码后的结果,即CanonicalURI = UriEncodeExceptSlash(Path)。要求绝对路径Path必须以“/”开头,不以“/”开头的需要补充上,空路径为“/”。函数UriEncodeExceptSlash的具体含义及功能请参考相关函数。
示例: 若URL为 https: //bos.cn-n1.baidubce.com/example/测试 ,则其URL的Path为/example/测试,将之规范化得到CanonicalURI =UriEncodeExceptSlash(/example/测试)= /example/%E6%B5%8B%E8%AF%95。
CanonicalQueryString
CanonicalQueryString是对于URL中的Query String(Query String即URL中“?”后面的“key1 = valve1 & key2 = valve2 ”字符串)进行编码后的结果。
编码步骤如下:
- 提取URL中的Query String项,即URL中“?”后面的“key1 = valve1 & key2 = valve2 ”字符串。
- 将Query String根据&分隔符拆开成若干项,每一项是key=value或者只有key的形式。
- 对拆开后的每一项进行编码处理,分以下三种情况。
- 当该项的key是authorization时,直接忽略该项。
- 当该项只有key时,转换公式为UriEncode(key) + "="的形式。
- 当该项是key=value的形式时,转换公式为 UriEncode(key) + "=" + UriEncode(value) 的形式。这里value可以是空字符串。
4.将每一项转换后的字符串按照字典顺序(ASCII码由小到大)排序,并使用& 符号连接起来,生成相应的CanonicalQueryString。
编码示例:
获取URL为https: //bos.cn-n1.baidubce.com/example?text&text1=测试&text10=test的CanonicalQueryString。
1.提取URL中的Query String,得到 text&text1=测试&text10=test。 2.根据&对Query String进行拆分,得到text 、text1=测试 、 text10=test三项。 3.对拆分的每一项进行编码。
- 对text项进行编码:UriEncode("text") + "=",得到"text="
- 对text1=测试项进行编码:UriEncode("text1") + "=" + UriEncode("测试"),得到"text1=%E6%B5%8B%E8%AF%95"
- 对text10=test项进行编码:UriEncode("text10") + "=" + UriEncode("test"),得到"text10=test"
4.对上面三项编码后的字符串进行(按照ASCII码由小到大)排序,得到结果是text10=test 、text1=%E6%B5%8B%E8%AF%95 、text= ,然后用&连接起来,得到CanonicalQueryString为text10=test&text1=%E6%B5%8B%E8%AF%95&text=。
CanonicalHeaders
CanonicalHeaders是对HTTP请求中的Header部分进行选择性编码的结果。 在这个步骤中,可根据Header部分确定签名头域(signedHeaders)。签名头域是指签名算法中涉及到的HTTP头域列表。HTTP头域名字一律要求小写且头域名字之间用分号(;)分隔,如host;range;x-bce-date。列表按照字典序排列。当它为空时表示系统默认取Host.
对Header进行编码获取CanonicalHeaders,编码步骤如下。
- 将Header的名字变成全小写,注意仅改名字。
- 将Header的值去掉开头和结尾的空白字符。
- 经过上一步之后值为空字符串的Header忽略,其余的转换为 UriEncodeExceptSlash(name) + ":" + UriEncodeExceptSlash(value) 的形式。 把上面转换后的所有字符串按照字典序进行排序。
- 将排序后的字符串按顺序用\n符号连接起来得到最终的CanonicalHeaders。
编码示例: 如下是发送请求的Header:
Host: bj.bcebos.com
Date: Mon, 27 Apr 2015 16:23:49 +0800
Content-Type: text/plain
Content-Length: 8
Content-Md5: NFzcPqhviddjRNnSOGo4rw==
x-bce-date: 2015-04-27T08:23:49Z
- 选择需要编码的Header,然后把所有名字都改为小写。
host: bj.bcebos.com
date: Mon, 27 Apr 2015 16:23:49 +0800
content-type: text/plain
content-length: 8
content-md5: NFzcPqhviddjRNnSOGo4rw==
- 将Header的值去掉开头和结尾的空白字符。
host:bj.bcebos.com
date:Mon, 27 Apr 2015 16:23:49 +0800
content-type:text/plain
content-length:8
content-md5:NFzcPqhviddjRNnSOGo4rw==
3.对每个Header进行UriEncodeExceptSlash转换。
host:bj.bcebos.com
date:Mon%2C%2027%20Apr%202015%2016%3A23%3A49%20%2B0800
content-type:text/plain
content-length:8
content-md5:NFzcPqhviddjRNnSOGo4rw%3D%3D?
4.将步骤3中转换后的所有字符串按照字典序进行排序。
content-length:8
content-md5:NFzcPqhviddjRNnSOGo4rw%3D%3D?
content-type:text/plain
date:Mon%2C%2027%20Apr%202015%2016%3A23%3A49%20%2B0800
host:bj.bcebos.com
5.将排序后的字符串按顺序用\n符号连接起来得到最终结果。
content-length:8
content-md5:NFzcPqhviddjRNnSOGo4rw%3D%3D?
content-type:text/plain
date:Mon%2C%2027%20Apr%202015%2016%3A23%3A49%20%2B0800
host:bj.bcebos.com
同时获得认证字符串的signedHeaders内容应该是content-length;content-md5;content-type;date;host
生成派生密钥(signingKey)
SigningKey = HMAC-SHA256-HEX(sk, authStringPrefix)
其中:
- HMAC-SHA256-HEX是HMAC SHA256算法函数
- sk为生成信息摘要时所使用的密钥,即用户的Secret Access Key,可通过在配置台 系统管理-> API配置页面获取到
- authStringPrefix,进行哈希运算的消息,代表认证字符串的前缀部分,即:cc-api-auth-v1/{accessKeyId}/{timestamp}/{expirationPeriodInSeconds}。
生成签名摘要及认证字符串(authorization)
通过上面的计算得到的SigningKey和CanonicalRequest按照下面公式可以得到签名。
Signature = HMAC-SHA256-HEX(SigningKey, CanonicalRequest)
最后由公式cc-api-auth-v1/{accessKeyId}/{timestamp}/{expirationPeriodInSeconds }/{signedHeaders}/{signature}得到认证字符串。
相关函数说明
HMAC-SHA256-HEX()
调用HMAC SHA256算法,根据开发者提供的密钥(key)和密文(message)输出密文摘要,并把结果转换为小写形式的十六进制字符串。
Lowercase()
将字符串全部变成小写。
Trim()
去掉字符串开头和结尾的空白字符。
UriEncode()
RFC 3986规定,"URI非保留字符"包括以下字符:字母(A-Z,a-z)、数字(0-9)、连字号(-)、点号(.)、下划线(_)、波浪线(~),算法实现如下:
- 将字符串转换成UTF-8编码的字节流
- 保留所有“URI非保留字符”原样不变
- 对其余字节做一次RFC 3986中规定的百分号编码(Percent-encoding),即一个“%”后面跟着两个表示该字节值的十六进制字母,字母一律采用大写形式。
UriEncodeExceptSlash()
与UriEncode() 类似,区别是斜杠(/)不做编码。一个简单的实现方式是先调用UriEncode(),然后把结果中所有的%2F都替换为/ UriEncode()函数参考代码如下:
public static String uri-encode(CharSequence input, boolean encodeSlash) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < input.length(); i++) {
char ch = input.charAt(i);
if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '-' || ch == '~' || ch == '.') {
result.append(ch);
} else if (ch == '/') {
result.append(encodeSlash ? "%2F" : ch);
} else {
result.append(toHexUTF8(ch));
}
}
return result.toString();
}
代码示例
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import hashlib
import hmac
import urllib
import time
# 1.AK/SK、host、method、URL绝对路径、querystring
AK = "07b2391cee0f40bd806c6318a572b3ef"
SK = "83195d93149b4692821cfb424482b961"
host = "localhost:8080" # 接口的域名,或者ip端口
method = "POST"
query = "" # 通过url param传参时,将参数填这
URI = "/api/test" # 接口path
# 2.x-bce-date
x_bce_date = time.gmtime()
x_bce_date = time.strftime('%Y-%m-%dT%H:%M:%SZ',x_bce_date)
# 3.header和signedHeaders
header = {
"Host":host,
}
signedHeaders = "Host"
# 4.认证字符串前缀
authStringPrefix = "cc-api-auth-v1" + "/" +AK + "/" +x_bce_date + "/" +"1800"
print("authStringPrefix=" + authStringPrefix)
# 5.生成CanonicalRequest
#5.1生成CanonicalURI
CanonicalURI = urllib.quote(URI) # windows下为urllib.parse.quote,Linux下为urllib.quote
#5.2生成CanonicalQueryString
CanonicalQueryString = query # 如果您调用的接口的query比较复杂的话,需要做额外处理
#5.3生成CanonicalHeaders
result = []
for key,value in header.items():
tempStr = str(urllib.quote(key.lower(),safe="")) + ":" + str(urllib.quote(value,safe=""))
result.append(tempStr)
result.sort()
CanonicalHeaders = "\n".join(result)
#5.4拼接得到CanonicalRequest
CanonicalRequest = method + "\n" + CanonicalURI + "\n" + CanonicalQueryString +"\n" + CanonicalHeaders
print("CanonicalRequest=" + CanonicalRequest)
# 6.生成signingKey
signingKey = hmac.new(SK.encode('utf-8'),authStringPrefix.encode('utf-8'),hashlib.sha256)
print("signingKey=" + signingKey.hexdigest())
# 7.生成Signature
Signature = hmac.new((signingKey.hexdigest()).encode('utf-8'),CanonicalRequest.encode('utf-8'),hashlib.sha256)
print("Signature=" + Signature.hexdigest())
# 8.生成Authorization并放到header里
header['Authorization'] = authStringPrefix + "/" +signedHeaders + "/" +Signature.hexdigest()
print("token=" + authStringPrefix + "/" +signedHeaders + "/" +Signature.hexdigest())
# 9.发送API请求并接受响应
#import requests
#import json
#body={
# "name" : "QQQQQQ"
# }
#url = "http://"+host + URI
#r = requests.put(url,headers = header,data=json.dumps(body))
#print(r.text)
java SDK使用示例
平台提供Token认证的java sdk,支持开发者直接调用获取Token。
sdk jar包地址:下载
Maven POM文件 引入SDK jar包
<dependencies>
<dependency>
<groupId>com.baidu.contact-center</groupId>
<artifactId>cf-auth-api-token</artifactId>
<version>5.1.5-SNAPSHOT</version>
<scope>system</scope>
<systemPath>/Users/lihuiyao01/Desktop/cf-auth-api-token-5.1.5-SNAPSHOT-jar-with-dependencies.jar</systemPath>
</dependency>
</dependencies>
SDK 示例代码
public class TokenGenerate {
public static void main(String[] args) {
ApiTokenV1.ApiAuthTokenV1Builder builder = new ApiTokenV1.ApiAuthTokenV1Builder();
// 必填参数 ak,sk,uri,host,http method, 过期时间
builder.ak("ac92a217bb4f43ebbfe31cda859f647e"); // ak
builder.sk("c5432f291e2d472395348d399a8d8640"); // sk
builder.uri("/api/v1/robot/list"); // 接口路径
builder.host("aicc.bce.baidu.com"); // host
builder.httpMethod("GET"); // http method
builder.expireInSeconds(1800); // 过期时间
// 以下是非必须参数
// 参与生成token的请求头, 此处若使用了x-bce-date,则调用外呼api接口时,请求头内必需带上x-bce-date
HashMap<String, String> headerMap = new HashMap<String, String>();
headerMap.put("x-bce-date", "2021-10-12T10:02:14Z");
builder.httpHeaderMap(headerMap);
// builder.httpHeader("Content-Type", "application/json");
// url里?带的参数,如/api/v1/robot/list?robotName=test, 没有?参数可以不调用此方法
builder.queryStr("robotName=test&pn=1");
System.out.println(builder.build().getToken());
}
}