AIOT云云推送鉴权
更新时间:2024-09-20
AIOT云云推送鉴权
概述
在整体服务接入后,若需要对设备交互过程进行干预,则需要进云云对接,百度侧将设备交互的消息载荷按照特定协议推送到目标服务,目标服务处理后返回,后续将下发给用户设备。
API签名认证机制
客户端建立连接时,需要在 header 传递 Timestamp, AccessKey, Authorization 参数。
名词 | 描述 |
---|---|
Timestamp | 系统时间戳(示例中时间需要在5分钟内,否则为失效) |
ACCESS_KEY | 百度侧提供访问key |
SECRET_KEY | 百度侧提供密钥key |
RequestBody | 请求体实际载荷 |
Authorization | Authorization参数签名过程:整个签名摘要使用HMAC-SHA256算法,将ACCESS_KEY + Timestamp + RequestBody 进行摘要计算并且进行Base64编码,点击下载完整示例代码 |
接口说明
1. 通信协议
数据交换格式为JSON,所有request/response body内容均采用UTF-8编码。
请求参数包括如下4种:
参数类型 | 说明 |
---|---|
URI | 通常用于指明操作实体,如:POST /v1/api/foo |
Query参数 | URL中携带的请求参数,通常用来指明要对实体进行的动作 |
HEADER | 通过HTTP头域传入,Timestamp, AccessKey, Authorization |
RequestBody | 通过JSON格式组织的请求数据体 |
2. 请求参数
与 ASR 结果回调接口的参数一致
Header 参数
参数 | 类型 | 说明 |
---|---|---|
Content-Type | string | application/json |
Timestamp | long | 时间戳(毫秒) |
AccessKey | string | access_key |
Authorization | string | 摘要信息 |
Body 参数
参数 | 类型 | 说明 |
---|---|---|
logld | string | 日志 ID |
device | obj | 百度三元组数据 |
device.fc | string | |
device.pk | string | |
device.ak | string | |
query | string | ASR 识别结果 |
nlulnfos | string | 请求语义 |
extlnfo | obj | 扩展信息 |
custom | string | 端上自定义信息 |
nluInfos 字段说明 //这里只是示例
Java
1 [{\"domain\":\"clean_bot\",\"intent\":\"start_clean\",\"slots\":{\"location\":[{\"slot_type\":\"STRING\",\"text\":\"卧室\",\"value\":\"卧室\"}]}}]
extInfo 字段说明 //这里只是示例
Java
1{
2 "memberId": 123456789, // 用户ID
3}
3. 响应参数
logId | string | 日志 ID |
errcode | int | 错误码,0为成功,其他为失败 |
errmsg | string | 错误信息 |
tts | obj | 播报信息 |
nlulnfos | string | 响应语义 |
ctrlparams | obj | 控制参数 |
custom | string | 客户自定义,透传到端上 |
tts 内容字段说明:
Java
1{
2 "flag": 0, // 0: 正常播报(默认),1:tts 不进行播报(content字段内容不生效)
3 "content":"tts text" // 用于tts播报的内容, 如果content为空,则会用百度侧的content进行播报
4}
ctrlParams 字段说明:
Java
1{
2 "mediaPlay": 0 // 0: 正常播报(默认),1:不进行播报
3}
custom字段说明:
类型为string,百度透传结果,客户进行端云协同
原始如json数据需要序列化成 string类型
举例:
Java
1 "custom": "
2 {\"otherKey1\":\"otherValue1\",\"otherKey2\":\"otherValue2\"}
3". //整个内容是 string,百度透传结果,客户进行端云协同
4. 状态码
errcode | 说明 |
---|---|
0 | 成功 |
1001 | 鉴权失败 |
1002 | 参数错误 |
1003 | 内部错误 |
示例代码
实例代码块为校验签名的合法性,实际调用为百度侧进行签名,客户侧校验百度侧签名是否合法。点击下载完整示例代码
签名校验核心逻辑
Java
1{
2
3import javax.crypto.Mac;
4import javax.crypto.spec.SecretKeySpec;
5import java.nio.charset.StandardCharsets;
6import java.security.NoSuchAlgorithmException;
7import java.util.Base64;
8
9import java.security.InvalidKeyException;
10
11
12/**
13 * 验签工具类. 验证签名是否正确
14 * 实现方需要实现以下策略
15 * <p>
16 * 1.时间有效性判断(建议上下5分钟之内认为有效)
17 * 2.防重攻击.
18 * 3.签名有效性判断
19 **/
20public class CloudSignature {
21
22
23 /**
24 * ak参数.
25 */
26 public static final String ACCESS_KEY = "QRtM5Mqc3Knn6duZd9xTjjfbSSlIDwln";
27
28 /**
29 * sk参数.由双方云共同拥有. 不进行网络传输.请严格保密.
30 */
31 public static final String SECRET_KEY = "dEtj0KZebuj6gadtg5zNdVmXBWryNUmDzbFOM7fqcKzT3Excx8lyIgsyOUPceB5nS3NlJ5LJ9TqsElx8KyC8UD3izVL0eGw5tCm44YxuV8SDPfhms5X862PtnG6MPDlf";
32
33
34 /**
35 * 验证签名
36 *
37 * @param timestamp 系统时间戳-毫秒
38 * @param signature 签名
39 * @param requestBody 请求内容.HTTP请求和HTTPS请求里的最原始的Body String 字符串
40 * @return 是否合法
41 */
42 public static boolean authenticateRequest(String timestamp, String signature, String requestBody) {
43
44 // 时间戳判断-时间有效性判断(建议上下5分钟之内认为有效)
45 long now = System.currentTimeMillis();
46 long currentTime = Long.parseLong(timestamp);
47 // 绝对值小于5分钟之内认为有效
48 long timeDiff = Math.abs(now - currentTime);
49 System.out.println(timeDiff);
50 if (timeDiff > 5 * 50 * 1000) {
51 return false; // 时间不在5分钟之内认为无效
52 }
53
54 // 防重判断. 这里暂时不实现
55 // 可以利用 requestBody 里的 logId 作为防重key
56
57
58 // 签名校验
59 String calculatedSignature = calculateSignature(timestamp, ACCESS_KEY, requestBody); // 根据具体算法计算请求签名
60 // 如果请求签名不匹配,说明请求未经授权,拒绝鉴权
61 return calculatedSignature == null || calculatedSignature.equals(signature);
62
63 // 鉴权通过
64 }
65
66 /**
67 * 计算签名
68 *
69 * @param timestamp 时间戳
70 * @param accessKey ak
71 * @param requestBody 请求内容.是HTTP请求和HTTPS请求里的最原始的Body String 字符串
72 * @return 签名
73 */
74 public static String calculateSignature(String timestamp, String accessKey, String requestBody) {
75 try {
76 // 使用HMAC-SHA256算法进行签名计算
77 Mac mac = Mac.getInstance("HmacSHA256");
78 SecretKeySpec secretKeySpec = new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
79 mac.init(secretKeySpec);
80 String toBeSigned = accessKey + timestamp + requestBody; // 将请求内容body加入签名计算
81 byte[] signatureBytes = mac.doFinal(toBeSigned.getBytes(StandardCharsets.UTF_8));
82 // 将签名结果进行Base64编码
83 return Base64.getEncoder().encodeToString(signatureBytes);
84 } catch (NoSuchAlgorithmException | InvalidKeyException e) {
85 e.printStackTrace();
86 return null;
87 }
88 }
89
90}
91}
测试代码
Java
1import org.junit.Assert;
2import org.junit.Test;
3
4public class CloudSignatureTest {
5
6 @Test
7 public void normalTest() {
8 // 示例使用:
9 String timestamp = System.currentTimeMillis() + ""; // 获取当前时间戳
10 String accessKey = CloudSignature.ACCESS_KEY; // 替换为实际的Access Key
11 String requestBody = "{\"logId\":\"123\",\"device\":{\"fc\":\"fc\",\"pk\":\"pk\",\"ak\":\"000000000019\"},\"query\":\"测试query\",\"nluInfos\":\"[{\\\"domain\\\":\\\"unknown\\\",\\\"intent\\\":\\\"unknown\\\",\\\"slots\\\":{}}]\"}"; // 替换为实际的请求内容body
12 String signature = CloudSignature.calculateSignature(timestamp, accessKey, requestBody); // 计算请求签名
13 // 将timestamp、accessKey、requestBody和signature kJcAI4FSIFvL6nY7uhjQ6A4THy+0sPLKd6LLcR90Ozk=
14 System.out.println(signature);
15 Assert.assertTrue(CloudSignature.authenticateRequest(timestamp, signature, requestBody));
16 }
17
18
19 @Test
20 public void fakerSignatureTest() {
21 // 示例使用:
22 String timestamp = System.currentTimeMillis() + ""; // 获取当前时间戳
23
24 String requestBody = "{\"logId\":\"123\",\"device\":{\"fc\":\"fc\",\"pk\":\"pk\",\"ak\":\"000000000019\"},\"query\":\"测试query\",\"nluInfos\":\"[{\\\"domain\\\":\\\"unknown\\\",\\\"intent\\\":\\\"unknown\\\",\\\"slots\\\":{}}]\"}"; // 替换为实际的请求内容body
25
26 // 异常的ak
27 String signature = CloudSignature.calculateSignature(timestamp, "faker_access_key", requestBody); // 计算请求签名
28 // 将timestamp、accessKey、requestBody和signature kJcAI4FSIFvL6nY7uhjQ6A4THy+0sPLKd6LLcR90Ozk=
29 System.out.println(signature);
30 Assert.assertFalse(CloudSignature.authenticateRequest(timestamp, signature, requestBody));
31 }
32
33
34 @Test
35 public void InvalidTimestampSignatureTest() {
36 // 示例使用:
37 String timestamp = (System.currentTimeMillis() + (60 * 1000 * 20)) + ""; // 获取当前时间戳
38 String accessKey = CloudSignature.ACCESS_KEY; // 替换为实际的Access Key
39 String requestBody = "{\"logId\":\"123\",\"device\":{\"fc\":\"fc\",\"pk\":\"pk\",\"ak\":\"000000000019\"},\"query\":\"测试query\",\"nluInfos\":\"[{\\\"domain\\\":\\\"unknown\\\",\\\"intent\\\":\\\"unknown\\\",\\\"slots\\\":{}}]\"}"; // 替换为实际的请求内容body
40 String signature = CloudSignature.calculateSignature(timestamp, accessKey, requestBody); // 计算请求签名
41 // 将timestamp、accessKey、requestBody和signature kJcAI4FSIFvL6nY7uhjQ6A4THy+0sPLKd6LLcR90Ozk=
42 System.out.println(signature);
43 Assert.assertFalse(CloudSignature.authenticateRequest(timestamp, signature, requestBody));
44 }
45
46}