From c23f15b195cddd2385629c5e8126b392a39697f0 Mon Sep 17 00:00:00 2001 From: bggRGjQaUbCoE Date: Tue, 24 Dec 2024 11:28:18 +0800 Subject: [PATCH] feat: import/export login info Signed-off-by: bggRGjQaUbCoE --- lib/http/init.dart | 18 +++++- lib/pages/about/index.dart | 101 +++++++++++++++++++++++++++++++- lib/pages/login/controller.dart | 72 ++--------------------- lib/utils/storage.dart | 2 + lib/utils/utils.dart | 72 +++++++++++++++++++++++ 5 files changed, 194 insertions(+), 71 deletions(-) diff --git a/lib/http/init.dart b/lib/http/init.dart index 15466121..8defe91c 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -18,6 +18,7 @@ import 'api.dart'; import 'constants.dart'; import 'interceptor.dart'; import 'interceptor_anonymity.dart'; +import 'package:flutter_inappwebview/flutter_inappwebview.dart' as web; class Request { static final Request _instance = Request._internal(); @@ -43,8 +44,19 @@ class Request { cookieManager = CookieManager(cookieJar); dio.interceptors.add(cookieManager); dio.interceptors.add(AnonymityInterceptor()); - // final List cookie = await cookieManager.cookieJar - // .loadForRequest(Uri.parse(HttpString.baseUrl)); + final List cookies = await cookieManager.cookieJar + .loadForRequest(Uri.parse(HttpString.baseUrl)); + for (Cookie item in cookies) { + await web.CookieManager().setCookie( + url: web.WebUri(item.domain ?? ''), + name: item.name, + value: item.value, + path: item.path ?? '', + domain: item.domain, + isSecure: item.secure, + isHttpOnly: item.httpOnly, + ); + } final userInfo = userInfoCache.get('userInfoCache'); if (userInfo != null && userInfo.mid != null) { final List cookie2 = await cookieManager.cookieJar @@ -65,7 +77,7 @@ class Request { log("setCookie, ${e.toString()}"); } - // final String cookieString = cookie + // final String cookieString = cookies // .map((Cookie cookie) => '${cookie.name}=${cookie.value}') // .join('; '); // dio.options.headers['cookie'] = cookieString; diff --git a/lib/pages/about/index.dart b/lib/pages/about/index.dart index e6c0b521..45a081e8 100644 --- a/lib/pages/about/index.dart +++ b/lib/pages/about/index.dart @@ -1,4 +1,9 @@ +import 'dart:convert'; + +import 'package:PiliPalaX/http/constants.dart'; import 'package:PiliPalaX/services/loggeer.dart'; +import 'package:cookie_jar/cookie_jar.dart'; +import 'package:dio_cookie_manager/dio_cookie_manager.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -189,12 +194,104 @@ class _AboutPageState extends State { ), ), ), + ListTile( + title: const Text('导入/导出登录信息'), + leading: const Icon(Icons.import_export_outlined), + onTap: () { + showDialog( + context: context, + builder: (context) => SimpleDialog( + clipBehavior: Clip.hardEdge, + children: [ + ListTile( + title: const Text('导出'), + onTap: () async { + dynamic accessKey = GStorage.localCache + .get(LocalCacheKey.accessKey, defaultValue: {}); + dynamic cookies = (await CookieManager(PersistCookieJar( + ignoreExpires: true, + storage: FileStorage(await Utils.getCookiePath()), + )) + .cookieJar + .loadForRequest(Uri.parse(HttpString.baseUrl))) + .map( + (Cookie cookie) => { + 'name': cookie.name, + 'value': cookie.value, + }, + ) + .toList(); + dynamic res = jsonEncode({ + 'accessKey': accessKey, + 'cookies': cookies, + }); + Utils.copyText('$res'); + if (context.mounted) { + showDialog( + context: context, + builder: (context) => AlertDialog( + content: SelectableText('$res'), + ), + ); + } + }, + ), + ListTile( + title: const Text('导入'), + onTap: () async { + Get.back(); + ClipboardData? data = + await Clipboard.getData('text/plain'); + if (data == null || + data.text == null || + data.text!.isEmpty) { + SmartDialog.showToast('剪贴板无数据'); + return; + } + if (!context.mounted) return; + await showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('是否导入以下登录信息?'), + content: Text(data.text!), + actions: [ + TextButton( + onPressed: Get.back, + child: const Text('取消'), + ), + TextButton( + onPressed: () async { + Get.back(); + try { + dynamic res = jsonDecode(data.text!); + Utils.afterLoginByApp( + res['accessKey'], + {'cookies': res['cookies']}, + ); + } catch (e) { + SmartDialog.showToast('导入失败:$e'); + } + }, + child: const Text('确定'), + ), + ], + ); + }, + ); + }, + ), + ], + ), + ); + }, + ), ListTile( title: const Text('导入/导出设置'), dense: false, leading: const Icon(Icons.import_export_outlined), - onTap: () async { - await showDialog( + onTap: () { + showDialog( context: context, builder: (context) { return SimpleDialog( diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart index 54e546f6..dd970dfa 100644 --- a/lib/pages/login/controller.dart +++ b/lib/pages/login/controller.dart @@ -2,22 +2,13 @@ import 'dart:async'; import 'dart:io'; import 'package:PiliPalaX/common/constants.dart'; +import 'package:PiliPalaX/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:PiliPalaX/http/login.dart'; import 'package:gt3_flutter_plugin/gt3_flutter_plugin.dart'; import 'package:PiliPalaX/models/login/index.dart'; -import '../../utils/login.dart'; -import 'package:hive/hive.dart'; -import 'package:webview_cookie_manager/webview_cookie_manager.dart'; - -import '../../http/constants.dart'; -import '../../http/init.dart'; -import '../../http/user.dart'; -import '../../utils/storage.dart'; -import '../home/controller.dart'; -import '../media/controller.dart'; class LoginPageController extends GetxController with GetSingleTickerProviderStateMixin { @@ -86,7 +77,7 @@ class LoginPageController extends GetxController if (value['status']) { t.cancel(); statusQRCode.value = '扫码成功'; - await afterLoginByApp( + await Utils.afterLoginByApp( value['data'], value['data']['cookie_info']); Get.back(); } else if (value['code'] == 86038) { @@ -111,58 +102,6 @@ class LoginPageController extends GetxController } } - Future afterLoginByApp(Map token_info, cookie_info) async { - try { - Box localCache = GStorage.localCache; - localCache.put(LocalCacheKey.accessKey, { - 'mid': token_info['mid'], - 'value': token_info['access_token'], - 'refresh': token_info['refresh_token'] - }); - List cookieInfo = cookie_info['cookies']; - List cookies = []; - String cookieStrings = cookieInfo.map((cookie) { - String cstr = - '${cookie['name']}=${cookie['value']};Domain=.bilibili.com;Path=/;'; - cookies.add(Cookie.fromSetCookieValue(cstr)); - return cstr; - }).join(''); - List Urls = [ - HttpString.baseUrl, - HttpString.apiBaseUrl, - HttpString.tUrl - ]; - for (var url in Urls) { - await Request.cookieManager.cookieJar - .saveFromResponse(Uri.parse(url), cookies); - } - Request.dio.options.headers['cookie'] = cookieStrings; - await WebviewCookieManager().setCookies(cookies); - } catch (e) { - SmartDialog.showToast('设置登录态失败,$e'); - } - final result = await UserHttp.userInfo(); - if (result['status'] && result['data'].isLogin) { - SmartDialog.showToast('登录成功,当前采用「' - '${GStorage.setting.get(SettingBoxKey.defaultRcmdType, defaultValue: 'web')}' - '端」推荐'); - await GStorage.userInfo.put('userInfoCache', result['data']); - try { - final HomeController homeCtr = Get.find(); - homeCtr.updateLoginStatus(true); - homeCtr.userFace.value = result['data'].face; - final MediaController mediaCtr = Get.find(); - mediaCtr.mid = result['data'].mid; - } catch (_) {} - await LoginUtils.refreshLoginStatus(true); - } else { - // 获取用户信息失败 - SmartDialog.showNotify( - msg: '登录失败,请检查cookie是否正确,${result['message']}', - notifyType: NotifyType.warning); - } - } - // 申请极验验证码 Future getCaptcha(geeGt, geeChallenge, onSuccess) async { var registerData = Gt3RegisterData( @@ -415,7 +354,8 @@ class LoginPageController extends GetxController return; } SmartDialog.showToast('正在保存身份信息'); - await afterLoginByApp(data['token_info'], data['cookie_info']); + await Utils.afterLoginByApp( + data['token_info'], data['cookie_info']); Get.back(); Get.back(); }, @@ -432,7 +372,7 @@ class LoginPageController extends GetxController return; } SmartDialog.showToast('正在保存身份信息'); - await afterLoginByApp(data['token_info'], data['cookie_info']); + await Utils.afterLoginByApp(data['token_info'], data['cookie_info']); Get.back(); } else { // handle login result @@ -495,7 +435,7 @@ class LoginPageController extends GetxController if (res['status']) { SmartDialog.showToast('登录成功'); var data = res['data']; - await afterLoginByApp(data['token_info'], data['cookie_info']); + await Utils.afterLoginByApp(data['token_info'], data['cookie_info']); Get.back(); } else { SmartDialog.showToast(res['msg']); diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index b7fc0b74..49c377b2 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -22,6 +22,8 @@ class GStorage { static late final Box setting; static late final Box video; + static bool get isLogin => userInfo.get('userInfoCache') != null; + static List get speedList => List.from( video.get( VideoBoxKey.speedsList, diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 2c0070b7..16c1348d 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -3,13 +3,20 @@ import 'dart:convert'; import 'dart:io'; import 'dart:math'; import 'package:PiliPalaX/grpc/app/main/community/reply/v1/reply.pb.dart'; +import 'package:PiliPalaX/http/constants.dart'; +import 'package:PiliPalaX/http/init.dart'; import 'package:PiliPalaX/http/member.dart'; import 'package:PiliPalaX/http/search.dart'; +import 'package:PiliPalaX/http/user.dart'; import 'package:PiliPalaX/http/video.dart'; import 'package:PiliPalaX/models/bangumi/info.dart'; import 'package:PiliPalaX/models/common/search_type.dart'; +import 'package:PiliPalaX/pages/home/controller.dart'; +import 'package:PiliPalaX/pages/media/controller.dart'; import 'package:PiliPalaX/pages/video/detail/introduction/widgets/group_panel.dart'; import 'package:PiliPalaX/utils/feed_back.dart'; +import 'package:PiliPalaX/utils/login.dart'; +import 'package:PiliPalaX/utils/storage.dart'; import 'package:crypto/crypto.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/material.dart'; @@ -18,10 +25,75 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:path_provider/path_provider.dart'; import 'package:url_launcher/url_launcher.dart'; +import 'package:webview_cookie_manager/webview_cookie_manager.dart'; +import 'package:flutter_inappwebview/flutter_inappwebview.dart' as web; class Utils { static final Random random = Random(); + static Future afterLoginByApp( + Map token_info, cookie_info) async { + try { + GStorage.localCache.put(LocalCacheKey.accessKey, { + 'mid': token_info['mid'], + 'value': token_info['access_token'] ?? token_info['value'], + 'refresh': token_info['refresh_token'] ?? token_info['refresh'] + }); + List cookieInfo = cookie_info['cookies']; + List cookies = []; + String cookieStrings = cookieInfo.map((cookie) { + String cstr = + '${cookie['name']}=${cookie['value']};Domain=.bilibili.com;Path=/;'; + cookies.add(Cookie.fromSetCookieValue(cstr)); + return cstr; + }).join(''); + List urls = [ + HttpString.baseUrl, + HttpString.apiBaseUrl, + HttpString.tUrl + ]; + for (var url in urls) { + await Request.cookieManager.cookieJar + .saveFromResponse(Uri.parse(url), cookies); + } + Request.dio.options.headers['cookie'] = cookieStrings; + await WebviewCookieManager().setCookies(cookies); + for (Cookie item in cookies) { + await web.CookieManager().setCookie( + url: web.WebUri(item.domain ?? ''), + name: item.name, + value: item.value, + path: item.path ?? '', + domain: item.domain, + isSecure: item.secure, + isHttpOnly: item.httpOnly, + ); + } + } catch (e) { + SmartDialog.showToast('设置登录态失败,$e'); + } + final result = await UserHttp.userInfo(); + if (result['status'] && result['data'].isLogin) { + SmartDialog.showToast('登录成功,当前采用「' + '${GStorage.setting.get(SettingBoxKey.defaultRcmdType, defaultValue: 'web')}' + '端」推荐'); + await GStorage.userInfo.put('userInfoCache', result['data']); + try { + final HomeController homeCtr = Get.find(); + homeCtr.updateLoginStatus(true); + homeCtr.userFace.value = result['data'].face; + final MediaController mediaCtr = Get.find(); + mediaCtr.mid = result['data'].mid; + } catch (_) {} + await LoginUtils.refreshLoginStatus(true); + } else { + // 获取用户信息失败 + SmartDialog.showNotify( + msg: '登录失败,请检查cookie是否正确,${result['message']}', + notifyType: NotifyType.warning); + } + } + static bool isStringNumeric(str) { RegExp numericRegex = RegExp(r'^[\d\.]+$'); return numericRegex.hasMatch(str.toString());