Files
PiliPlus/lib/utils/wbi_sign.dart
My-Responsitories c40d794180 tweaks (#1802)
* opt: uuid

* tweak

* opt: SlideDialog

* mod: fvmrc [skip ci]

* Revert "mod: fvmrc [skip ci]"

This reverts commit 500fd7f454.

* Revert "opt: SlideDialog"

This reverts commit b435a312a6.

---------

Co-authored-by: dom <githubaccount56556@proton.me>
2026-01-11 10:45:51 +08:00

121 lines
3.0 KiB
Dart

// Wbi签名 用于生成 REST API 请求中的 w_rid 和 wts 字段
// https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/misc/sign/wbi.md
// import md5 from 'md5'
// import axios from 'axios'
import 'dart:async';
import 'dart:convert';
import 'package:PiliPlus/http/api.dart';
import 'package:PiliPlus/http/init.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:PiliPlus/utils/storage_key.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:crypto/crypto.dart';
import 'package:hive/hive.dart';
abstract final class WbiSign {
static Box get _localCache => GStorage.localCache;
static final RegExp _chrFilter = RegExp(r"[!\'\(\)\*]");
static const _mixinKeyEncTab = <int>[
46,
47,
18,
2,
53,
8,
23,
32,
15,
50,
10,
31,
58,
3,
45,
35,
27,
43,
5,
49,
33,
9,
42,
19,
29,
28,
14,
39,
12,
38,
41,
13,
];
static Future<String>? _future;
// 对 imgKey 和 subKey 进行字符顺序打乱编码
static String getMixinKey(String orig) {
final codeUnits = orig.codeUnits;
return String.fromCharCodes(_mixinKeyEncTab.map((i) => codeUnits[i]));
}
// 为请求参数进行 wbi 签名
static void encWbi(Map<String, Object> params, String mixinKey) {
params['wts'] = DateTime.now().millisecondsSinceEpoch ~/ 1000;
// 按照 key 重排参数
final List<String> 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
}
static Future<String> _getWbiKeys() async {
final resp = await Request().get(Api.userInfo);
try {
final wbiUrls = resp.data['data']['wbi_img'];
final mixinKey = getMixinKey(
Utils.getFileName(wbiUrls['img_url'], fileExt: false) +
Utils.getFileName(wbiUrls['sub_url'], fileExt: false),
);
_localCache.put(LocalCacheKey.mixinKey, mixinKey);
return mixinKey;
} catch (_) {
return '';
}
}
static FutureOr<String> getWbiKeys() {
final nowDate = DateTime.now();
if (DateTime.fromMillisecondsSinceEpoch(
_localCache.get(LocalCacheKey.timeStamp, defaultValue: 0) as int,
).day ==
nowDate.day) {
final String? mixinKey = _localCache.get(LocalCacheKey.mixinKey);
if (mixinKey != null) return mixinKey;
return _future ??= _getWbiKeys();
} else {
return _future = _localCache
.put(LocalCacheKey.timeStamp, nowDate.millisecondsSinceEpoch)
.then((_) => _getWbiKeys());
}
}
static Future<Map<String, Object>> makSign(
Map<String, Object> params,
) async {
// params 为需要加密的请求参数
final String mixinKey = await getWbiKeys();
encWbi(params, mixinKey);
return params;
}
}