# 点播资源选链
# 资源选链
HTTP Method:GET
请求地址:https://usk-uslink.hivoice.cn/unios-data-uslink/rest/v1/link/get_data_link
参数 | 类型 | 必填 | 描述 |
---|---|---|---|
appKey | String | Y | 应用KEY |
udid | String | Y | 设备唯一标识 |
deviceType | String | Y | 设备类型(android ,ios,chip ,web) |
dataType | String | Y | 数据类型(child/music/audio) |
dataSourceCode | String | Y | 数据源(child/xmly/kuwo) |
id | String | Y | 资源ID |
resourceType | Integer | Y | 资源类型(1:视频 2:音频 ) |
timestamp | Long | Y | 时间戳,有效性(10分钟内) |
encryptMethod | String | Y | 加密方式(MD5/AES/DES/SHA1/HMACSHA256),默认MD5 |
signature | String | Y | 数据签名 |
请求参数中数据类型dataType和对应的数据源dataSourceCode列表
数据类型code | 数据类型名称 | 数据源code | 数据源名称 |
---|---|---|---|
child | 儿童资源数据类型 | child | 儿童资源统一数据源 |
music | 音乐数据类型 | kuwo | 酷我音乐数据源 |
news | 新闻数据类型 | leting | 乐听新闻数据源 |
audio | 有声读物数据类型 | qingtingFM | 蜻蜓FM数据源 |
audio | 有声读物数据类型 | xmly | 喜马拉雅数据源 |
channel | 广播电台数据类型 | qingtingFM | 蜻蜓FM数据源 |
返回值
参数 | 类型 | 必填 | 描述 |
---|---|---|---|
errorCode | String | Y | 响应code,具体见错误码定义 |
errorMsg | String | Y | 响应描述 |
costTime | Integer | Y | 响应时间,单位:毫秒 |
result | Object | Y | 返回的结果数据 |
└data | String | Y | 儿童资源列表 |
└└id | String | Y | 资源ID,唯一标识 |
└└url | String | Y | 播放链接 |
└└totalContentPlayTime | Integer | Y | 播放时长,单位秒 |
└└expiryTime | Integer | Y | 音频地址过期时间,单位秒 |
返回结果示例:
{
"errorCode": "0",
"errorMsg": "请求成功",
"result": {
"data": {
"id": "2000130210",
"totalContentPlayTime": 161,
"url": "http://dcs-resources-oss.hivoice.cn/audio/44fff002ec33f4c5ada5af4c237af6d4.mp3"
}
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 签名规则
选链服务支持以下几种签名方式:MD5(签名方式为空默认是MD5)、SHA1、AES、DES、HMACSHA256
# MD5、HMACSHA256、AES、DES签名生成规则:
A)将所有请求参数(除encryptMethod)放入Map中(MD5签名将appSecret也一并放入Map),注意:计算signature时所有参数不应进行URLEncode。
B)将格式化后的参数以字典序升序排列,拼接在一起,注意字典序中大写字母在前,空值不参与签名。
C)将B形成字符串获取摘要,即为本次请求signature(签名)的值。
参数排序示例
private static final String SYMBOL_AND = "&";
/**
* 格式化并排序请求参数
* @param map
* @return
* @throws Exception
*/
private static String formatAndSortParams(Map<String, String> map) throws Exception {
if (null == map || map.size() == 0) {
return null;
}
ArrayList<String> list = new ArrayList<String>();
for (Map.Entry<String, String> entry : map.entrySet()) {
if (StringHandler.isNullOrEmpty(entry.getKey()) || StringHandler.isNullOrEmpty(entry.getValue())) {
continue;
}
list.add(entry.getKey().trim() + "=" + UrlCoder.encode(entry.getValue().trim()) + "&");
}
int size = list.size();
String[] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < size; i++) {
sb.append(arrayToSort[i]);
}
String params = sb.toString();
if (params.endsWith(SYMBOL_AND)) {
params = params.substring(0, params.length() - 1);
}
return params;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
MD5签名示例
/**
* 签名算法
*
* @param params
* 要参与签名的数据对象
* @return 签名
* @throws IllegalAccessException
*/
public static String getSign(Map<String, String> params) throws Exception {
if (null == params || params.isEmpty() ) {
return null;
}
params.remove("signature");
// 格式化并排序请求参数
String result = formatAndSortParams(params);
// Base64编码
result = CoderUtil.encryptBASE64(result).replace("\r\n", "");
// MD5运算
result = new String(DigestUtils.md5Hex(result));
return result;
}
public static void main(String[] args) {
Map<String, String> mapParams = new LinkedHashMap<>();
mapParams.put("appKey", "appKey");
mapParams.put("appSecret", "appSecret");
mapParams.put("deviceType", "android");
mapParams.put("dataType", "child");
mapParams.put("dataSourceCode", "child");
mapParams.put("id", "1000208060");
mapParams.put("resourceType", "1");
mapParams.put("timestamp", String.valueOf(1569831595));
mapParams.put("udid", "udid");
String encrypt = getSign(mapParams);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
HMACSHA256签名示例
public static String HMACSHA256(String data, String key) throws Exception {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
byte[] var6 = array;
int var7 = array.length;
for(int var8 = 0; var8 < var7; ++var8) {
byte item = var6[var8];
sb.append(Integer.toHexString(item & 255 | 256).substring(1, 3));
}
return sb.toString().toUpperCase();
}
public static void main(String[] args) {
Map<String, String> mapParams = new LinkedHashMap<>();
mapParams.put("appKey", "appKey");
mapParams.put("deviceType", "android");
mapParams.put("dataType", "child");
mapParams.put("dataSourceCode", "child");
mapParams.put("id", "2000130210");
mapParams.put("resourceType", "2");
mapParams.put("timestamp", String.valueOf(1569831488));
mapParams.put("udid", "uni_uid");
// key是appSecret值
String key = "appSecret";
// formatAndSortParams是参数排序方法
String encrypt = HMACSHA256(formatAndSortParams(mapParams), key);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
AES签名示例:
// sSrc:Map参数排序后的字符串;encodingFormat:UTF-8;sKey:appSecret的前16位;ivParameter:appSecret 从17位到最后
public static String cbc_encrypt(String sSrc, String encodingFormat, String sKey, String ivParameter) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] raw = sKey.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());// 使用CBC模式,需要一个向量iv,可增加加密算法的强度
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(sSrc.getBytes(encodingFormat));
String ret = new BASE64Encoder().encode(encrypted);// 此处使用BASE64做转码。
ret = ret.replaceAll("\r\n", "");
ret = ret.replaceAll("\n", "");
return ret;
}
public static void main(String[] args) {
Map<String, String> mapParams = new LinkedHashMap<>();
mapParams.put("appKey", "appKey");
mapParams.put("deviceType", "android");
mapParams.put("dataType", "child");
mapParams.put("dataSourceCode", "child");
mapParams.put("id", "2000130210");
mapParams.put("resourceType", "2");
mapParams.put("timestamp", String.valueOf(1569831488));
mapParams.put("udid", "uni_uid");
// formatAndSortParams是参数排序方法
String enString = cbc_encrypt(formatAndSortParams(mapParams), "utf-8", appSecret.substring(0,16), appSecret.substring(16));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
DES签名示例:
/**
* 3DESECB加密,key必须是长度大于等于 3*8 = 24 位
*
* @param src String
* @param key String
* @return String
*/
public static String encryptThreeDESECB(String src, String key) throws Exception {
if (src == null || key == null) {
return null;
}
final DESedeKeySpec dks = new DESedeKeySpec(key.getBytes("UTF-8"));
final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
final SecretKey securekey = keyFactory.generateSecret(dks);
final Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, securekey);
final byte[] b = cipher.doFinal(src.getBytes("UTF-8"));
final BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(b).replaceAll("\r", "").replaceAll("\n", "");
}
public static void main(String[] args) {
Map<String, String> mapParams = new LinkedHashMap<>();
mapParams.put("appKey", "appKey");
mapParams.put("deviceType", "android");
mapParams.put("dataType", "child");
mapParams.put("dataSourceCode", "child");
mapParams.put("id", "2000130210");
mapParams.put("resourceType", "2");
mapParams.put("timestamp", String.valueOf(1569831488));
mapParams.put("udid", "uni_uid");
// key是appSecret
String key = "appsecret";
// formatAndSortParams是参数排序方法
String enString = encryptThreeDESECB(formatAndSortParams(mapParams), key);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# SHA1签名生成规则:
A)将所有请求参数(除encryptMethod)和appSecret的值放入List中,注意:计算signature时所有参数不应进行URLEncode。
B)将格式化后的参数以字典序升序排列,拼接在一起,注意字典序中大写字母在前,空值(null)使用空字符串代替。
C)将B形成字符串获取SHA1摘要,形成一个40位的十六进制(字母大写)字符串,即为本次请求signature(签名)的值。
/**
* 对参数列表构造响应签名
*
* @param params
* @return
*/
public static String buildSignature(List<String> params) {
if (params == null || params.isEmpty()) {
return "";
}
// 升序排序参数值
Collections.sort(params);
StringBuilder sb = new StringBuilder();
for (String param : params) {
sb.append(param == null ? "" : param);
}
return getSHA1Digest(sb.toString());
}
/**
* 将字符串进行SHA1获取摘要,摘要为十六进制字符串
*
* @param data
* @return
* @throws Exception
*/
public static String getSHA1Digest(String data) {
String digest = null;
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] bytes = md.digest(data.getBytes("UTF-8"));
digest = byte2hex(bytes);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return digest;
}
/**
* 二进制转十六进制字符串
*
* @param bytes
* @return
*/
private static String byte2hex(byte[] bytes) {
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.toUpperCase());
}
return sign.toString();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# 错误码定义及描述
errorCode | errorMsg | 描述 |
---|---|---|
0 | 请求成功 | 请求成功 |
3020001 | 请求参数不合法 | 请求参数不合法 |
3020002 | 请求参数错误 | 业务校验参数错误 |
3020003 | 请求时间戳超出了请求有效期 | 时间戳与当前时间差超过10分钟 |
3020004 | 签名错误 | 签名校验错误 |
3020005 | 调用第三方内容服务结果为空 | 调用第三方内容服务结果为空 |
3024444 | 调用内容服务异常 | 调用第三方内容服务异常 |
3029999 | 系统异常 | 联系管理员 |