opt: wbiSign (#332)

This commit is contained in:
My-Responsitories
2025-02-26 14:01:38 +00:00
committed by GitHub
parent fb11208bbe
commit 1c3d77b95d
8 changed files with 57 additions and 101 deletions

View File

@@ -278,7 +278,7 @@ class MemberHttp {
dynamic wwebid, dynamic wwebid,
}) async { }) async {
space(mid: mid); space(mid: mid);
Map params = await WbiSign().makSign({ Map params = await WbiSign.makSign({
'mid': mid, 'mid': mid,
'token': token, 'token': token,
'platform': 'web', 'platform': 'web',
@@ -348,7 +348,7 @@ class MemberHttp {
}) async { }) async {
String dmImgStr = Utils.base64EncodeRandomString(16, 64); String dmImgStr = Utils.base64EncodeRandomString(16, 64);
String dmCoverImgStr = Utils.base64EncodeRandomString(32, 128); String dmCoverImgStr = Utils.base64EncodeRandomString(32, 128);
Map params = await WbiSign().makSign({ Map params = await WbiSign.makSign({
'mid': mid, 'mid': mid,
'ps': ps, 'ps': ps,
'tid': tid, 'tid': tid,
@@ -393,7 +393,7 @@ class MemberHttp {
}) async { }) async {
String dmImgStr = Utils.base64EncodeRandomString(16, 64); String dmImgStr = Utils.base64EncodeRandomString(16, 64);
String dmCoverImgStr = Utils.base64EncodeRandomString(32, 128); String dmCoverImgStr = Utils.base64EncodeRandomString(32, 128);
Map params = await WbiSign().makSign({ Map params = await WbiSign.makSign({
'offset': offset ?? '', 'offset': offset ?? '',
'host_mid': mid, 'host_mid': mid,
'timezone_offset': '-480', 'timezone_offset': '-480',
@@ -595,7 +595,7 @@ class MemberHttp {
// 最近投币 // 最近投币
static Future<LoadingState> getRecentCoinVideo({required int mid}) async { static Future<LoadingState> getRecentCoinVideo({required int mid}) async {
Map params = await WbiSign().makSign({ Map params = await WbiSign.makSign({
'mid': mid, 'mid': mid,
'gaia_source': 'main_web', 'gaia_source': 'main_web',
'web_location': 333.999, 'web_location': 333.999,
@@ -621,7 +621,7 @@ class MemberHttp {
// 最近点赞 // 最近点赞
static Future<LoadingState> getRecentLikeVideo({required int mid}) async { static Future<LoadingState> getRecentLikeVideo({required int mid}) async {
Map params = await WbiSign().makSign({ Map params = await WbiSign.makSign({
'mid': mid, 'mid': mid,
'gaia_source': 'main_web', 'gaia_source': 'main_web',
'web_location': 333.999, 'web_location': 333.999,
@@ -713,7 +713,7 @@ class MemberHttp {
'name': name, 'name': name,
'web_location': 333.999, 'web_location': 333.999,
}; };
Map params = await WbiSign().makSign(data); Map params = await WbiSign.makSign(data);
var res = await Request().get(Api.followSearch, queryParameters: { var res = await Request().get(Api.followSearch, queryParameters: {
...data, ...data,
'w_rid': params['w_rid'], 'w_rid': params['w_rid'],

View File

@@ -258,7 +258,7 @@ class MsgHttp {
String? biz, String? biz,
}) async { }) async {
String csrf = await Request.getCsrf(); String csrf = await Request.getCsrf();
Map<String, dynamic> data = await WbiSign().makSign({ Map<String, dynamic> data = await WbiSign.makSign({
'file_up': await MultipartFile.fromFile(path), 'file_up': await MultipartFile.fromFile(path),
if (category != null) 'category': category, if (category != null) 'category': category,
if (biz != null) 'biz': biz, if (biz != null) 'biz': biz,
@@ -285,7 +285,7 @@ class MsgHttp {
dynamic content, dynamic content,
) async { ) async {
String csrf = await Request.getCsrf(); String csrf = await Request.getCsrf();
Map<String, dynamic> data = await WbiSign().makSign({ Map<String, dynamic> data = await WbiSign.makSign({
'dynamic_id': 0, 'dynamic_id': 0,
'type': 4, 'type': 4,
'rid': 0, 'rid': 0,
@@ -311,7 +311,7 @@ class MsgHttp {
dynamic dynamicId, dynamic dynamicId,
) async { ) async {
String csrf = await Request.getCsrf(); String csrf = await Request.getCsrf();
Map<String, dynamic> data = await WbiSign().makSign({ Map<String, dynamic> data = await WbiSign.makSign({
'dynamic_id': dynamicId, 'dynamic_id': dynamicId,
'csrf_token': csrf, 'csrf_token': csrf,
'csrf': csrf, 'csrf': csrf,
@@ -334,7 +334,7 @@ class MsgHttp {
dynamic talkerId, dynamic talkerId,
) async { ) async {
String csrf = await Request.getCsrf(); String csrf = await Request.getCsrf();
Map<String, dynamic> data = await WbiSign().makSign({ Map<String, dynamic> data = await WbiSign.makSign({
'talker_id': talkerId, 'talker_id': talkerId,
'session_type': 1, 'session_type': 1,
'build': 0, 'build': 0,
@@ -387,7 +387,7 @@ class MsgHttp {
int opType, int opType,
) async { ) async {
String csrf = await Request.getCsrf(); String csrf = await Request.getCsrf();
Map<String, dynamic> data = await WbiSign().makSign({ Map<String, dynamic> data = await WbiSign.makSign({
'talker_id': talkerId, 'talker_id': talkerId,
'session_type': 1, 'session_type': 1,
'op_type': opType, 'op_type': opType,
@@ -422,7 +422,7 @@ class MsgHttp {
params['end_ts'] = endTs; params['end_ts'] = endTs;
} }
Map signParams = await WbiSign().makSign(params); Map signParams = await WbiSign.makSign(params);
var res = await Request().get(Api.sessionList, queryParameters: signParams); var res = await Request().get(Api.sessionList, queryParameters: signParams);
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
try { try {
@@ -475,7 +475,7 @@ class MsgHttp {
static Future sessionMsg({ static Future sessionMsg({
int? talkerId, int? talkerId,
}) async { }) async {
Map params = await WbiSign().makSign({ Map params = await WbiSign.makSign({
'talker_id': talkerId, 'talker_id': talkerId,
'session_type': 1, 'session_type': 1,
'size': 20, 'size': 20,
@@ -508,7 +508,7 @@ class MsgHttp {
int? ackSeqno, int? ackSeqno,
}) async { }) async {
String csrf = await Request.getCsrf(); String csrf = await Request.getCsrf();
Map params = await WbiSign().makSign({ Map params = await WbiSign.makSign({
'talker_id': talkerId, 'talker_id': talkerId,
'session_type': 1, 'session_type': 1,
'ack_seqno': ackSeqno, 'ack_seqno': ackSeqno,
@@ -559,7 +559,7 @@ class MsgHttp {
'csrf_token': csrf, 'csrf_token': csrf,
'csrf': csrf, 'csrf': csrf,
}; };
Map<String, dynamic> params = await WbiSign().makSign(base); Map<String, dynamic> params = await WbiSign.makSign(base);
var res = await Request().post(Api.sendMsg, var res = await Request().post(Api.sendMsg,
queryParameters: <String, dynamic>{ queryParameters: <String, dynamic>{
'w_sender_uid': params['msg[sender_uid]'], 'w_sender_uid': params['msg[sender_uid]'],

View File

@@ -356,7 +356,7 @@ class UserHttp {
} }
// // 相互关系查询 // // 相互关系查询
// static Future relationSearch(int mid) async { // static Future relationSearch(int mid) async {
// Map params = await WbiSign().makSign({ // Map params = await WbiSign.makSign({
// 'mid': mid, // 'mid': mid,
// 'token': '', // 'token': '',
// 'platform': 'web', // 'platform': 'web',

View File

@@ -235,7 +235,7 @@ class VideoHttp {
data['try_look'] = 1; data['try_look'] = 1;
} }
Map params = await WbiSign().makSign({ Map params = await WbiSign.makSign({
...data, ...data,
'fourk': 1, 'fourk': 1,
'voice_balance': 1, 'voice_balance': 1,
@@ -976,7 +976,7 @@ class VideoHttp {
int? cid, int? cid,
int? upMid, int? upMid,
}) async { }) async {
Map params = await WbiSign().makSign({ Map params = await WbiSign.makSign({
'bvid': bvid, 'bvid': bvid,
'cid': cid, 'cid': cid,
'up_mid': upMid, 'up_mid': upMid,

View File

@@ -717,7 +717,7 @@ class LocalCacheKey {
accessKey = 'accessKey', accessKey = 'accessKey',
// //
wbiKeys = 'wbiKeys', mixinKey = 'mixinKey',
timeStamp = 'timeStamp'; timeStamp = 'timeStamp';
} }

View File

@@ -5,12 +5,15 @@
import 'dart:convert'; import 'dart:convert';
import 'package:crypto/crypto.dart'; import 'package:crypto/crypto.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:synchronized/synchronized.dart';
import '../http/index.dart'; import '../http/index.dart';
import 'storage.dart'; import 'storage.dart';
class WbiSign { class WbiSign {
static Box get localCache => GStorage.localCache; static Box get localCache => GStorage.localCache;
final List<int> mixinKeyEncTab = <int>[ static final Lock lock = Lock();
static final RegExp chrFilter = RegExp(r"[!\'\(\)\*]");
static const mixinKeyEncTab = <int>[
46, 46,
47, 47,
18, 18,
@@ -42,106 +45,58 @@ class WbiSign {
12, 12,
38, 38,
41, 41,
13, 13
37,
48,
7,
16,
24,
55,
40,
61,
26,
17,
0,
1,
60,
51,
30,
4,
22,
25,
54,
21,
56,
59,
6,
63,
57,
62,
11,
36,
20,
34,
44,
52
]; ];
// 对 imgKey 和 subKey 进行字符顺序打乱编码 // 对 imgKey 和 subKey 进行字符顺序打乱编码
String getMixinKey(String orig) { static String getMixinKey(String orig) {
String temp = ''; return mixinKeyEncTab.map((i) => orig[i]).join();
for (int i = 0; i < mixinKeyEncTab.length; i++) {
temp += orig[mixinKeyEncTab[i]];
}
return temp.substring(0, 32);
} }
// 为请求参数进行 wbi 签名 // 为请求参数进行 wbi 签名
Map<String, dynamic> encWbi( static void encWbi(Map<String, dynamic> params, String mixinKey) {
Map<String, dynamic> params, String imgKey, String subKey) { params['wts'] = DateTime.now().millisecondsSinceEpoch ~/ 1000;
final String mixinKey = getMixinKey(imgKey + subKey);
final DateTime now = DateTime.now();
final int currTime = (now.millisecondsSinceEpoch / 1000).round();
final RegExp chrFilter = RegExp(r"[!\'\(\)*]");
final List<String> query = <String>[];
final Map<String, dynamic> newParams = Map.from(params)
..addAll({"wts": currTime}); // 添加 wts 字段
// 按照 key 重排参数 // 按照 key 重排参数
final List<String> keys = newParams.keys.toList()..sort(); final List<String> keys = params.keys.toList()..sort();
for (String i in keys) { final queryStr = keys
query.add( .map((i) =>
'${Uri.encodeComponent(i)}=${Uri.encodeComponent(newParams[i].toString().replaceAll(chrFilter, ''))}'); '${Uri.encodeComponent(i)}=${Uri.encodeComponent(params[i].toString().replaceAll(chrFilter, ''))}')
} .join('&');
final String queryStr = query.join('&'); params['w_rid'] =
final String wbiSign =
md5.convert(utf8.encode(queryStr + mixinKey)).toString(); // 计算 w_rid md5.convert(utf8.encode(queryStr + mixinKey)).toString(); // 计算 w_rid
return {'w_rid': wbiSign, 'wts': currTime.toString()}; }
static String getFileName(String uri) {
return uri.substring(uri.lastIndexOf('/') + 1, uri.lastIndexOf('.'));
} }
// 获取最新的 img_key 和 sub_key 可以从缓存中获取 // 获取最新的 img_key 和 sub_key 可以从缓存中获取
static Future<Map<String, dynamic>> getWbiKeys() async { static Future<String> getWbiKeys() async {
final DateTime nowDate = DateTime.now(); final DateTime nowDate = DateTime.now();
if (localCache.get(LocalCacheKey.wbiKeys) != null && String? mixinKey = localCache.get(LocalCacheKey.mixinKey);
if (mixinKey != null &&
DateTime.fromMillisecondsSinceEpoch( DateTime.fromMillisecondsSinceEpoch(
localCache.get(LocalCacheKey.timeStamp) as int) localCache.get(LocalCacheKey.timeStamp) as int)
.day == .day ==
nowDate.day) { nowDate.day) {
final Map cacheWbiKeys = localCache.get('wbiKeys'); return mixinKey;
return Map<String, dynamic>.from(cacheWbiKeys);
} }
var resp = final resp = await Request().get(Api.userInfo);
await Request().get('https://api.bilibili.com/x/web-interface/nav'); final wbiUrls = resp.data['data']['wbi_img'];
var jsonContent = resp.data['data'];
final String imgUrl = jsonContent['wbi_img']['img_url']; mixinKey = getMixinKey(
final String subUrl = jsonContent['wbi_img']['sub_url']; getFileName(wbiUrls['img_url']) + getFileName(wbiUrls['sub_url']));
final Map<String, dynamic> wbiKeys = {
'imgKey': imgUrl localCache.put(LocalCacheKey.mixinKey, mixinKey);
.substring(imgUrl.lastIndexOf('/') + 1, imgUrl.length)
.split('.')[0],
'subKey': subUrl
.substring(subUrl.lastIndexOf('/') + 1, subUrl.length)
.split('.')[0]
};
localCache.put(LocalCacheKey.wbiKeys, wbiKeys);
localCache.put(LocalCacheKey.timeStamp, nowDate.millisecondsSinceEpoch); localCache.put(LocalCacheKey.timeStamp, nowDate.millisecondsSinceEpoch);
return wbiKeys; return mixinKey;
} }
Future<Map<String, dynamic>> makSign(Map<String, dynamic> params) async { static Future<Map<String, dynamic>> makSign(
Map<String, dynamic> params) async {
// params 为需要加密的请求参数 // params 为需要加密的请求参数
final Map<String, dynamic> wbiKeys = await getWbiKeys(); final String mixinKey = await lock.synchronized(getWbiKeys);
final Map<String, dynamic> query = params encWbi(params, mixinKey);
..addAll(encWbi(params, wbiKeys['imgKey'], wbiKeys['subKey'])); return params;
return query;
} }
} }

View File

@@ -1774,7 +1774,7 @@ packages:
source: hosted source: hosted
version: "1.2.0" version: "1.2.0"
synchronized: synchronized:
dependency: transitive dependency: "direct main"
description: description:
name: synchronized name: synchronized
sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225" sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225"

View File

@@ -182,6 +182,7 @@ dependencies:
flex_seed_scheme: ^3.4.1 flex_seed_scheme: ^3.4.1
live_photo_maker: ^0.0.6 live_photo_maker: ^0.0.6
fl_chart: ^0.69.2 fl_chart: ^0.69.2
synchronized: ^3.3.0
dependency_overrides: dependency_overrides:
screen_brightness: ^2.0.1 screen_brightness: ^2.0.1