mirror of
https://github.com/SocialSisterYi/bilibili-API-collect.git
synced 2024-12-29 14:00:09 +08:00
5.2 KiB
5.2 KiB
APP API 签名与鉴权
APP API 签名特性
部分客户端专用的 REST API 存在基于参数签名的鉴权,需要使用规定的appkey
及其对应的appsec
与原始请求参数进行签名计算,部分AppKey
及与之对应的AppSec
已经被公开:见该文档 APPKey
-
不同
appkey
对应不同的 app (如客户端、概念版、必剪、漫画、bililink等) -
不同平台同 app 也会存在不同的
appkey
(如安卓端、ios端、TV端等) -
同平台同 app 下不同功能也会存在不同的
appkey
(如登录专用、取流专用等) -
不同版本的客户端的
appkey
也可能不同 -
appkey与appsec一一对应
APP API 签名算法
- 首先为参数中添加
appkey
字段 - 然后按照参数的 Key 重新排序
- 再对这个 Key-Value 进行 url query 序列化,并拼接与之对应的
appsec
(盐) 进行 md5 Hash 运算(32-bit 字符小写),该 hash 便是 API 签名 - 最后在参数尾部增添
sign
字段,它的 Value 为上一步计算所得的 hash,一并作为表单或 Query 提交
Demo
使用 appkey = 1d8b6e7d45233436
, appsec = 560c52ccd288fed045859ed18bffd973
对如下 params
参数进行签名
上述示例appkey
、AppSec
均来自文档 APPKey
Python
import hashlib
import urllib.parse
def appsign(params, appkey, appsec):
'为请求参数进行 APP 签名'
params.update({'appkey': appkey})
params = dict(sorted(params.items())) # 按照 key 重排参数
query = urllib.parse.urlencode(params) # 序列化参数
sign = hashlib.md5((query+appsec).encode()).hexdigest() # 计算 api 签名
params.update({'sign':sign})
return params
appkey = '1d8b6e7d45233436'
appsec = '560c52ccd288fed045859ed18bffd973'
params = {
'id':114514,
'str':'1919810',
'test':'いいよ,こいよ',
}
signed_params = appsign(params, appkey, appsec)
query = urllib.parse.urlencode(signed_params)
print(signed_params)
print(query)
输出内容分别是进行 APP 签名的后参数的 key-Value 以及 url query 形式
{'appkey': '1d8b6e7d45233436', 'id': 114514, 'str': '1919810', 'test': 'いいよ,こいよ', 'sign': '01479cf20504d865519ac50f33ba3a7d'}
appkey=1d8b6e7d45233436&id=114514&str=1919810&test=%E3%81%84%E3%81%84%E3%82%88%EF%BC%8C%E3%81%93%E3%81%84%E3%82%88&sign=01479cf20504d865519ac50f33ba3a7d
Java
package io.github.cctyl;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.net.URLEncoder;
import java.util.TreeMap;
/**
* @author cctyl
*/
public class AppSigner {
private static final String APP_KEY = "1d8b6e7d45233436";
private static final String APP_SEC = "560c52ccd288fed045859ed18bffd973";
public static String appSign(Map<String, String> params) {
// 为请求参数进行 APP 签名
params.put("appkey", APP_KEY);
// 按照 key 重排参数
Map<String, String> sortedParams = new TreeMap<>(params);
// 序列化参数
StringBuilder queryBuilder = new StringBuilder();
for (Map.Entry<String, String> entry : sortedParams.entrySet()) {
if (queryBuilder.length() > 0) {
queryBuilder.append('&');
}
queryBuilder
.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8))
.append('=')
.append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8));
}
return generateMD5(queryBuilder .append(APP_SEC).toString());
}
private static String generateMD5(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(input.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%02x", b));
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
Map<String, String> params = new HashMap<>();
params.put("id", "114514");
params.put("str", "1919810");
params.put("test", "いいよ,こいよ");
System.out.println(appSign(params));
}
}
输出结果为:01479cf20504d865519ac50f33ba3a7d
TypeScript/JavaScript
import { createHash } from 'node:crypto'
type Params = Record<string, any>
const md5 = (str: string) => createHash('md5').update(str).digest('hex')
/**
* 为请求参数进行 APP 签名
*/
export function appSign(params: Params, appkey: string, appsec: string) {
params.appkey = appkey
const searchParams = new URLSearchParams(params)
searchParams.sort()
return md5(searchParams.toString() + appsec)
}
console.log(
appSign(
{
id: 114514,
str: '1919810',
test: 'いいよ,こいよ',
},
'1d8b6e7d45233436',
'560c52ccd288fed045859ed18bffd973',
),
'01479cf20504d865519ac50f33ba3a7d',
)
输出结果为:01479cf20504d865519ac50f33ba3a7d