feat: 1.调整项目初始化,不用等待site/config接口,仅在hi_user_info中调用;将site_config_service拆成loadDomain和获取配置,在splash中只做loadDomain
2. 优化接口拦截的解密日志 3. 调整获取订阅到kr_home,与源版一致 4. 修复闪连抖动问题,增加套餐有效期判断
This commit is contained in:
@@ -25,15 +25,14 @@ class BaseResponse<T> {
|
||||
final dataMap = json['data'] ?? Map<String, dynamic>();
|
||||
final cipherText = dataMap['data'] ?? "";
|
||||
final nonce = dataMap['time'] ?? "";
|
||||
print('明文${cipherText}');
|
||||
// 判断是否需要解密:根据站点配置的 enable_security 字段
|
||||
if (cipherText.isNotEmpty && nonce.isNotEmpty) {
|
||||
try {
|
||||
if (kDebugMode) {
|
||||
// print('═══════════════════════════════════════');
|
||||
// print('🔐 检测到加密响应,开始解密...');
|
||||
print('📥 加密数据长度: ${cipherText.length} 字符');
|
||||
print('⏰ 时间戳: $nonce');
|
||||
// print('📥 加密数据长度: ${cipherText.length} 字符');
|
||||
// print('⏰ 时间戳: $nonce');
|
||||
}
|
||||
|
||||
final decrypted = KRAesUtil.decryptData(cipherText, nonce, AppConfig.kr_encryptionKey);
|
||||
|
||||
+100
-211
@@ -24,7 +24,6 @@ import 'package:loggy/loggy.dart';
|
||||
|
||||
import '../utils/kr_aes_util.dart';
|
||||
import '../utils/kr_log_util.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
// import 'package:video/app/utils/common_util.dart';
|
||||
// import 'package:video/app/utils/log_util.dart';
|
||||
@@ -144,70 +143,11 @@ class HttpUtil {
|
||||
}
|
||||
|
||||
var map = <String, dynamic>{};
|
||||
// 判断是否需要加密:根据站点配置的 enable_security 字段
|
||||
final shouldEncrypt = KRSiteConfigService().isDeviceSecurityEnabled();
|
||||
if (shouldEncrypt) {
|
||||
KRLogUtil.kr_i('🔐 需要加密请求数据', tag: 'HttpUtil');
|
||||
final plainText = jsonEncode(params);
|
||||
map = KRAesUtil.encryptData(plainText, AppConfig.kr_encryptionKey);
|
||||
} else {
|
||||
map = params;
|
||||
}
|
||||
final plainText = jsonEncode(params);
|
||||
map = KRAesUtil.encryptData(plainText, AppConfig.kr_encryptionKey);
|
||||
|
||||
// 初始化请求头
|
||||
final headers = _initHeader('signature', 'userId', 'token');
|
||||
int? _kr_parseUserIdFromToken(String token) {
|
||||
try {
|
||||
// JWT格式: header.payload.signature
|
||||
final parts = token.split('.');
|
||||
if (parts.length != 3) {
|
||||
KRLogUtil.kr_e('JWT token格式错误', tag: 'AppRunData');
|
||||
return null;
|
||||
}
|
||||
|
||||
// 解码payload部分(base64)
|
||||
String payload = parts[1];
|
||||
// 手动添加必要的padding(base64要求长度是4的倍数)
|
||||
switch (payload.length % 4) {
|
||||
case 0:
|
||||
break; // 不需要padding
|
||||
case 2:
|
||||
payload += '==';
|
||||
break;
|
||||
case 3:
|
||||
payload += '=';
|
||||
break;
|
||||
default:
|
||||
KRLogUtil.kr_e('JWT payload长度无效', tag: 'AppRunData');
|
||||
return null;
|
||||
}
|
||||
|
||||
final decodedBytes = base64.decode(payload);
|
||||
final decodedString = utf8.decode(decodedBytes);
|
||||
|
||||
// 解析JSON
|
||||
final Map<String, dynamic> payloadMap = jsonDecode(decodedString);
|
||||
|
||||
// 获取UserId
|
||||
if (payloadMap.containsKey('UserId')) {
|
||||
final userId = payloadMap['UserId'];
|
||||
KRLogUtil.kr_i('从JWT解析出userId: $userId', tag: 'AppRunData');
|
||||
return userId is int ? userId : int.tryParse(userId.toString());
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (e) {
|
||||
KRLogUtil.kr_e('解析JWT token失败: $e', tag: 'AppRunData');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final userId =
|
||||
_kr_parseUserIdFromToken(KRAppRunData().kr_token.toString());
|
||||
// 调试:打印请求头
|
||||
KRLogUtil.kr_i('🔍 请求头: $headers', tag: 'HttpUtil');
|
||||
KRLogUtil.kr_i('🔍 请求userId: $userId', tag: 'HttpUtil');
|
||||
KRLogUtil.kr_i('🔍 请求头map: $map', tag: 'HttpUtil');
|
||||
|
||||
Response<Map<String, dynamic>> responseTemp;
|
||||
if (method == HttpMethod.GET) {
|
||||
@@ -316,193 +256,93 @@ class HttpUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/// 拦截器(简洁格式,无边框)
|
||||
class MyInterceptor extends Interceptor {
|
||||
@override
|
||||
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
||||
if (kDebugMode) {
|
||||
print('>>> Request │ ${options.method} │ ${options.uri}');
|
||||
}
|
||||
if (options.data != null) {
|
||||
if (kDebugMode) {
|
||||
print('Body: ${options.data}');
|
||||
}
|
||||
}
|
||||
handler.next(options);
|
||||
}
|
||||
|
||||
@override
|
||||
void onResponse(Response response, ResponseInterceptorHandler handler) {
|
||||
if (kDebugMode) {
|
||||
print(
|
||||
'<<< Response │ ${response.requestOptions.method} │ ${response.statusCode} ${response.statusMessage} │ ${response.requestOptions.uri}');
|
||||
}
|
||||
if (response.data != null) {
|
||||
if (kDebugMode) {
|
||||
print('Body: ${response.data}');
|
||||
}
|
||||
}
|
||||
handler.next(response);
|
||||
}
|
||||
|
||||
@override
|
||||
void onError(DioException err, ErrorInterceptorHandler handler) {
|
||||
if (kDebugMode) {
|
||||
print(
|
||||
'<<< Error │ ${err.requestOptions.method} │ ${err.requestOptions.uri}');
|
||||
}
|
||||
if (kDebugMode) {
|
||||
print('Error Type: ${err.type}');
|
||||
}
|
||||
if (err.message != null) {
|
||||
if (kDebugMode) {
|
||||
print('Error Message: ${err.message}');
|
||||
}
|
||||
}
|
||||
if (err.response?.data != null) {
|
||||
if (kDebugMode) {
|
||||
print('Response Data: ${err.response?.data}');
|
||||
}
|
||||
}
|
||||
handler.next(err);
|
||||
}
|
||||
}
|
||||
|
||||
/// 自定义简洁 HTTP 拦截器(无边框符号)
|
||||
class _KRSimpleHttpInterceptor extends Interceptor {
|
||||
/// 常量:手动控制是否打印拦截器日志与解密结果
|
||||
static const bool KR_HTTP_PRINT = true;
|
||||
final Dio _dio;
|
||||
_KRSimpleHttpInterceptor(this._dio);
|
||||
static String? _lastPath;
|
||||
static int _lastTsMs = 0;
|
||||
@override
|
||||
|
||||
/// 请求拦截器
|
||||
///
|
||||
/// 功能:在调试模式下优先打印请求的解密明文;若解密失败则打印原始数据并标识失败。
|
||||
/// 参数:
|
||||
/// - options: 请求选项(包含 data 与 queryParameters)
|
||||
/// - handler: 拦截器处理器
|
||||
/// 返回:void
|
||||
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
||||
if (kDebugMode) {
|
||||
if (KR_HTTP_PRINT) {
|
||||
print('>>> Request │ ${options.method} │ ${options.uri}');
|
||||
}
|
||||
if (options.data != null) {
|
||||
if (kDebugMode) {
|
||||
print('Body: ${options.data}');
|
||||
}
|
||||
|
||||
// 检查是否是加密数据(包含 data 和 time 字段)
|
||||
if (KR_HTTP_PRINT) {
|
||||
Map<String, dynamic>? m;
|
||||
if (options.data is Map<String, dynamic>) {
|
||||
final data = options.data as Map<String, dynamic>;
|
||||
if (data.containsKey('data') && data.containsKey('time')) {
|
||||
try {
|
||||
if (kDebugMode) {
|
||||
print('');
|
||||
}
|
||||
// 尝试解密并打印原始数据
|
||||
final encryptedData = data['data'] as String;
|
||||
final nonce = data['time'] as String;
|
||||
final decrypted = KRAesUtil.decryptData(
|
||||
encryptedData,
|
||||
nonce,
|
||||
AppConfig.kr_encryptionKey,
|
||||
);
|
||||
if (kDebugMode) {
|
||||
print('🔓 解密后的原始请求数据:');
|
||||
}
|
||||
// 尝试格式化 JSON
|
||||
try {
|
||||
final jsonData = jsonDecode(decrypted);
|
||||
final prettyJson = JsonEncoder.withIndent(' ').convert(jsonData);
|
||||
if (kDebugMode) {
|
||||
print(prettyJson);
|
||||
}
|
||||
} catch (_) {
|
||||
if (kDebugMode) {
|
||||
print(decrypted);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print('⚠️ 请求解密失败: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
m = options.data as Map<String, dynamic>;
|
||||
} else if (options.queryParameters.isNotEmpty) {
|
||||
m = options.queryParameters;
|
||||
}
|
||||
if (m != null) {
|
||||
final decrypted = _tryDecryptFromMap(m);
|
||||
_printDecryptedOrFallback(
|
||||
phase: 'Request', raw: m, decrypted: decrypted);
|
||||
}
|
||||
}
|
||||
handler.next(options);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
/// 响应拦截器
|
||||
///
|
||||
/// 功能:在调试模式下优先打印响应的解密明文;若解密失败则打印原始数据并标识失败。
|
||||
/// 参数:
|
||||
/// - response: 响应对象(优先识别 Map 格式)
|
||||
/// - handler: 拦截器处理器
|
||||
/// 返回:void
|
||||
void onResponse(Response response, ResponseInterceptorHandler handler) {
|
||||
if (kDebugMode) {
|
||||
if (KR_HTTP_PRINT) {
|
||||
print(
|
||||
'<<< Response │ ${response.requestOptions.method} │ ${response.statusCode} ${response.statusMessage} │ ${response.requestOptions.uri}');
|
||||
}
|
||||
if (response.data != null) {
|
||||
if (kDebugMode) {
|
||||
print('Body: ${response.data}');
|
||||
}
|
||||
|
||||
// 检查响应是否是加密数据(包含 data 和 time 字段)
|
||||
if (response.data is Map<String, dynamic>) {
|
||||
final dataMap = response.data as Map<String, dynamic>;
|
||||
|
||||
// 检查是否包含嵌套的 data 字段(加密数据格式)
|
||||
final nestedData = dataMap['data'];
|
||||
if (nestedData is Map<String, dynamic> &&
|
||||
nestedData.containsKey('data') &&
|
||||
nestedData.containsKey('time')) {
|
||||
try {
|
||||
if (kDebugMode) {
|
||||
print('');
|
||||
}
|
||||
if (kDebugMode) {
|
||||
print('🔐 检测到加密响应,正在解密...');
|
||||
}
|
||||
// 尝试解密并打印原始响应数据
|
||||
final encryptedData = nestedData['data'] as String;
|
||||
final nonce = nestedData['time'] as String;
|
||||
final decrypted = KRAesUtil.decryptData(
|
||||
encryptedData,
|
||||
nonce,
|
||||
AppConfig.kr_encryptionKey,
|
||||
);
|
||||
if (kDebugMode) {
|
||||
print('🔓 解密后的原始响应数据:');
|
||||
}
|
||||
// 尝试格式化 JSON
|
||||
try {
|
||||
// final jsonData = jsonDecode(decrypted);
|
||||
// final prettyJson = JsonEncoder.withIndent(' ').convert(jsonData);
|
||||
// if (kDebugMode) {
|
||||
// print(prettyJson);
|
||||
// }
|
||||
} catch (_) {
|
||||
if (kDebugMode) {
|
||||
print(decrypted);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print('⚠️ 响应解密失败: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (KR_HTTP_PRINT && response.data is Map<String, dynamic>) {
|
||||
final dataMap = response.data as Map<String, dynamic>;
|
||||
String? decrypted = _tryDecryptFromMap(dataMap);
|
||||
if (decrypted == null && dataMap['data'] is Map<String, dynamic>) {
|
||||
decrypted = _tryDecryptFromMap(dataMap['data'] as Map<String, dynamic>);
|
||||
}
|
||||
_printDecryptedOrFallback(
|
||||
phase: 'Response', raw: dataMap, decrypted: decrypted);
|
||||
}
|
||||
handler.next(response);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
/// 错误拦截器
|
||||
///
|
||||
/// 功能:打印错误信息并保持原有的 Unknown 错误重试与失败提示去重逻辑。
|
||||
/// 参数:
|
||||
/// - err: Dio 错误对象
|
||||
/// - handler: 拦截器处理器
|
||||
/// 返回:void
|
||||
void onError(DioException err, ErrorInterceptorHandler handler) {
|
||||
if (kDebugMode) {
|
||||
if (KR_HTTP_PRINT) {
|
||||
print(
|
||||
'<<< Error │ ${err.requestOptions.method} │ ${err.requestOptions.uri}');
|
||||
}
|
||||
if (kDebugMode) {
|
||||
if (KR_HTTP_PRINT) {
|
||||
print('Error Type: ${err.type}');
|
||||
}
|
||||
if (err.message != null) {
|
||||
if (kDebugMode) {
|
||||
if (KR_HTTP_PRINT) {
|
||||
print('Error Message: ${err.message}');
|
||||
}
|
||||
}
|
||||
if (err.response?.data != null) {
|
||||
if (kDebugMode) {
|
||||
if (KR_HTTP_PRINT) {
|
||||
print('Response Data: ${err.response?.data}');
|
||||
}
|
||||
}
|
||||
@@ -545,4 +385,53 @@ class _KRSimpleHttpInterceptor extends Interceptor {
|
||||
}
|
||||
handler.next(err);
|
||||
}
|
||||
|
||||
/// 尝试从 Map 中解密,若存在 data/time 字段则返回明文,否则返回 null
|
||||
String? _tryDecryptFromMap(Map<String, dynamic> m) {
|
||||
final data = m['data'];
|
||||
final time = m['time'];
|
||||
if (data is String && time is String) {
|
||||
try {
|
||||
return KRAesUtil.decryptData(data, time, AppConfig.kr_encryptionKey);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// 将字符串尽量按 JSON 缩进格式化,失败时返回原文
|
||||
String _prettyPrintJson(String s) {
|
||||
try {
|
||||
final jsonData = jsonDecode(s);
|
||||
return JsonEncoder.withIndent(' ').convert(jsonData);
|
||||
} catch (_) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
/// 统一输出逻辑:成功仅打印解密明文;失败打印失败标识与原文
|
||||
void _printDecryptedOrFallback({
|
||||
required String phase,
|
||||
required Object? raw,
|
||||
required String? decrypted,
|
||||
}) {
|
||||
if (!KR_HTTP_PRINT) return;
|
||||
if (decrypted != null) {
|
||||
print('🔓 $phase 明文:');
|
||||
print(_prettyPrintJson(decrypted));
|
||||
} else {
|
||||
print('⚠️ $phase 解密失败');
|
||||
print('原文:');
|
||||
if (raw is Map<String, dynamic>) {
|
||||
try {
|
||||
print(JsonEncoder.withIndent(' ').convert(raw));
|
||||
} catch (_) {
|
||||
print(raw.toString());
|
||||
}
|
||||
} else {
|
||||
print(raw?.toString() ?? 'null');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user