From 1c3d77b95d503b77c6f410e90471995ddff559b7 Mon Sep 17 00:00:00 2001 From: My-Responsitories <107370289+My-Responsitories@users.noreply.github.com> Date: Wed, 26 Feb 2025 14:01:38 +0000 Subject: [PATCH] opt: wbiSign (#332) --- lib/http/member.dart | 12 ++--- lib/http/msg.dart | 18 +++---- lib/http/user.dart | 2 +- lib/http/video.dart | 4 +- lib/utils/storage.dart | 2 +- lib/utils/wbi_sign.dart | 117 +++++++++++++--------------------------- pubspec.lock | 2 +- pubspec.yaml | 1 + 8 files changed, 57 insertions(+), 101 deletions(-) diff --git a/lib/http/member.dart b/lib/http/member.dart index 7901635d..7fe70ded 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -278,7 +278,7 @@ class MemberHttp { dynamic wwebid, }) async { space(mid: mid); - Map params = await WbiSign().makSign({ + Map params = await WbiSign.makSign({ 'mid': mid, 'token': token, 'platform': 'web', @@ -348,7 +348,7 @@ class MemberHttp { }) async { String dmImgStr = Utils.base64EncodeRandomString(16, 64); String dmCoverImgStr = Utils.base64EncodeRandomString(32, 128); - Map params = await WbiSign().makSign({ + Map params = await WbiSign.makSign({ 'mid': mid, 'ps': ps, 'tid': tid, @@ -393,7 +393,7 @@ class MemberHttp { }) async { String dmImgStr = Utils.base64EncodeRandomString(16, 64); String dmCoverImgStr = Utils.base64EncodeRandomString(32, 128); - Map params = await WbiSign().makSign({ + Map params = await WbiSign.makSign({ 'offset': offset ?? '', 'host_mid': mid, 'timezone_offset': '-480', @@ -595,7 +595,7 @@ class MemberHttp { // 最近投币 static Future getRecentCoinVideo({required int mid}) async { - Map params = await WbiSign().makSign({ + Map params = await WbiSign.makSign({ 'mid': mid, 'gaia_source': 'main_web', 'web_location': 333.999, @@ -621,7 +621,7 @@ class MemberHttp { // 最近点赞 static Future getRecentLikeVideo({required int mid}) async { - Map params = await WbiSign().makSign({ + Map params = await WbiSign.makSign({ 'mid': mid, 'gaia_source': 'main_web', 'web_location': 333.999, @@ -713,7 +713,7 @@ class MemberHttp { 'name': name, 'web_location': 333.999, }; - Map params = await WbiSign().makSign(data); + Map params = await WbiSign.makSign(data); var res = await Request().get(Api.followSearch, queryParameters: { ...data, 'w_rid': params['w_rid'], diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 0d61e94a..448d724f 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -258,7 +258,7 @@ class MsgHttp { String? biz, }) async { String csrf = await Request.getCsrf(); - Map data = await WbiSign().makSign({ + Map data = await WbiSign.makSign({ 'file_up': await MultipartFile.fromFile(path), if (category != null) 'category': category, if (biz != null) 'biz': biz, @@ -285,7 +285,7 @@ class MsgHttp { dynamic content, ) async { String csrf = await Request.getCsrf(); - Map data = await WbiSign().makSign({ + Map data = await WbiSign.makSign({ 'dynamic_id': 0, 'type': 4, 'rid': 0, @@ -311,7 +311,7 @@ class MsgHttp { dynamic dynamicId, ) async { String csrf = await Request.getCsrf(); - Map data = await WbiSign().makSign({ + Map data = await WbiSign.makSign({ 'dynamic_id': dynamicId, 'csrf_token': csrf, 'csrf': csrf, @@ -334,7 +334,7 @@ class MsgHttp { dynamic talkerId, ) async { String csrf = await Request.getCsrf(); - Map data = await WbiSign().makSign({ + Map data = await WbiSign.makSign({ 'talker_id': talkerId, 'session_type': 1, 'build': 0, @@ -387,7 +387,7 @@ class MsgHttp { int opType, ) async { String csrf = await Request.getCsrf(); - Map data = await WbiSign().makSign({ + Map data = await WbiSign.makSign({ 'talker_id': talkerId, 'session_type': 1, 'op_type': opType, @@ -422,7 +422,7 @@ class MsgHttp { 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); if (res.data['code'] == 0) { try { @@ -475,7 +475,7 @@ class MsgHttp { static Future sessionMsg({ int? talkerId, }) async { - Map params = await WbiSign().makSign({ + Map params = await WbiSign.makSign({ 'talker_id': talkerId, 'session_type': 1, 'size': 20, @@ -508,7 +508,7 @@ class MsgHttp { int? ackSeqno, }) async { String csrf = await Request.getCsrf(); - Map params = await WbiSign().makSign({ + Map params = await WbiSign.makSign({ 'talker_id': talkerId, 'session_type': 1, 'ack_seqno': ackSeqno, @@ -559,7 +559,7 @@ class MsgHttp { 'csrf_token': csrf, 'csrf': csrf, }; - Map params = await WbiSign().makSign(base); + Map params = await WbiSign.makSign(base); var res = await Request().post(Api.sendMsg, queryParameters: { 'w_sender_uid': params['msg[sender_uid]'], diff --git a/lib/http/user.dart b/lib/http/user.dart index b58593e8..482e114c 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -356,7 +356,7 @@ class UserHttp { } // // 相互关系查询 // static Future relationSearch(int mid) async { - // Map params = await WbiSign().makSign({ + // Map params = await WbiSign.makSign({ // 'mid': mid, // 'token': '', // 'platform': 'web', diff --git a/lib/http/video.dart b/lib/http/video.dart index 30976496..3b0ba31a 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -235,7 +235,7 @@ class VideoHttp { data['try_look'] = 1; } - Map params = await WbiSign().makSign({ + Map params = await WbiSign.makSign({ ...data, 'fourk': 1, 'voice_balance': 1, @@ -976,7 +976,7 @@ class VideoHttp { int? cid, int? upMid, }) async { - Map params = await WbiSign().makSign({ + Map params = await WbiSign.makSign({ 'bvid': bvid, 'cid': cid, 'up_mid': upMid, diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 3c9ac224..73079682 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -717,7 +717,7 @@ class LocalCacheKey { accessKey = 'accessKey', // - wbiKeys = 'wbiKeys', + mixinKey = 'mixinKey', timeStamp = 'timeStamp'; } diff --git a/lib/utils/wbi_sign.dart b/lib/utils/wbi_sign.dart index 59cf4455..efb6eb72 100644 --- a/lib/utils/wbi_sign.dart +++ b/lib/utils/wbi_sign.dart @@ -5,12 +5,15 @@ import 'dart:convert'; import 'package:crypto/crypto.dart'; import 'package:hive/hive.dart'; +import 'package:synchronized/synchronized.dart'; import '../http/index.dart'; import 'storage.dart'; class WbiSign { static Box get localCache => GStorage.localCache; - final List mixinKeyEncTab = [ + static final Lock lock = Lock(); + static final RegExp chrFilter = RegExp(r"[!\'\(\)\*]"); + static const mixinKeyEncTab = [ 46, 47, 18, @@ -42,106 +45,58 @@ class WbiSign { 12, 38, 41, - 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 + 13 ]; + // 对 imgKey 和 subKey 进行字符顺序打乱编码 - String getMixinKey(String orig) { - String temp = ''; - for (int i = 0; i < mixinKeyEncTab.length; i++) { - temp += orig[mixinKeyEncTab[i]]; - } - return temp.substring(0, 32); + static String getMixinKey(String orig) { + return mixinKeyEncTab.map((i) => orig[i]).join(); } // 为请求参数进行 wbi 签名 - Map encWbi( - Map params, String imgKey, String subKey) { - 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 query = []; - final Map newParams = Map.from(params) - ..addAll({"wts": currTime}); // 添加 wts 字段 + static void encWbi(Map params, String mixinKey) { + params['wts'] = DateTime.now().millisecondsSinceEpoch ~/ 1000; // 按照 key 重排参数 - final List keys = newParams.keys.toList()..sort(); - for (String i in keys) { - query.add( - '${Uri.encodeComponent(i)}=${Uri.encodeComponent(newParams[i].toString().replaceAll(chrFilter, ''))}'); - } - final String queryStr = query.join('&'); - final String wbiSign = + final List keys = params.keys.toList()..sort(); + final queryStr = keys + .map((i) => + '${Uri.encodeComponent(i)}=${Uri.encodeComponent(params[i].toString().replaceAll(chrFilter, ''))}') + .join('&'); + params['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 可以从缓存中获取 - static Future> getWbiKeys() async { + static Future getWbiKeys() async { final DateTime nowDate = DateTime.now(); - if (localCache.get(LocalCacheKey.wbiKeys) != null && + String? mixinKey = localCache.get(LocalCacheKey.mixinKey); + if (mixinKey != null && DateTime.fromMillisecondsSinceEpoch( localCache.get(LocalCacheKey.timeStamp) as int) .day == nowDate.day) { - final Map cacheWbiKeys = localCache.get('wbiKeys'); - return Map.from(cacheWbiKeys); + return mixinKey; } - var resp = - await Request().get('https://api.bilibili.com/x/web-interface/nav'); - var jsonContent = resp.data['data']; + final resp = await Request().get(Api.userInfo); + final wbiUrls = resp.data['data']['wbi_img']; - final String imgUrl = jsonContent['wbi_img']['img_url']; - final String subUrl = jsonContent['wbi_img']['sub_url']; - final Map wbiKeys = { - 'imgKey': imgUrl - .substring(imgUrl.lastIndexOf('/') + 1, imgUrl.length) - .split('.')[0], - 'subKey': subUrl - .substring(subUrl.lastIndexOf('/') + 1, subUrl.length) - .split('.')[0] - }; - localCache.put(LocalCacheKey.wbiKeys, wbiKeys); + mixinKey = getMixinKey( + getFileName(wbiUrls['img_url']) + getFileName(wbiUrls['sub_url'])); + + localCache.put(LocalCacheKey.mixinKey, mixinKey); localCache.put(LocalCacheKey.timeStamp, nowDate.millisecondsSinceEpoch); - return wbiKeys; + return mixinKey; } - Future> makSign(Map params) async { + static Future> makSign( + Map params) async { // params 为需要加密的请求参数 - final Map wbiKeys = await getWbiKeys(); - final Map query = params - ..addAll(encWbi(params, wbiKeys['imgKey'], wbiKeys['subKey'])); - return query; + final String mixinKey = await lock.synchronized(getWbiKeys); + encWbi(params, mixinKey); + return params; } } diff --git a/pubspec.lock b/pubspec.lock index a6e1cbec..b7d5a07b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1774,7 +1774,7 @@ packages: source: hosted version: "1.2.0" synchronized: - dependency: transitive + dependency: "direct main" description: name: synchronized sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225" diff --git a/pubspec.yaml b/pubspec.yaml index adb2b296..5f533b9e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -182,6 +182,7 @@ dependencies: flex_seed_scheme: ^3.4.1 live_photo_maker: ^0.0.6 fl_chart: ^0.69.2 + synchronized: ^3.3.0 dependency_overrides: screen_brightness: ^2.0.1