feat(mobile): 添加存储与 session cookie 持久化

- core/storage/app_storage.dart: shared_preferences 包装;保存
  服务端地址、用户名与保存密码偏好。
- core/storage/secure_storage.dart: flutter_secure_storage 包装;
  仅保存可选密码、token、session cookie 等高敏感数据。
- core/api/cookie_store.dart: 从 Set-Cookie 中提取 session=...
  并写入 secure storage,请求时再回填到 Cookie header。
This commit is contained in:
chaos-zhu
2026-05-16 17:02:09 +08:00
parent bb78062f75
commit 264e21e813
3 changed files with 81 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
import '../storage/secure_storage.dart';
/// Persists the EasyNode `session` cookie in secure storage and rehydrates
/// it on every request. The cookie is the second half of EasyNode's auth
/// pair (token header + session cookie), so it must survive process restarts.
class SessionCookieStore {
SessionCookieStore(this._storage);
final SecureAppStorage _storage;
/// Look for `session=...` inside a `Set-Cookie` header list and persist it.
/// Other cookies are ignored; EasyNode does not use them.
Future<void> saveFromSetCookieHeaders(List<String> headers) async {
for (final header in headers) {
final firstPart = header.split(';').first.trim();
if (firstPart.startsWith('session=')) {
await _storage.writeSessionCookie(firstPart);
return;
}
}
}
Future<String?> readCookieHeader() => _storage.readSessionCookie();
Future<void> clear() => _storage.deleteSessionCookie();
}
+24
View File
@@ -0,0 +1,24 @@
import 'package:shared_preferences/shared_preferences.dart';
/// Low-sensitivity persistence — server address, username, save-password
/// preference. Stored in `SharedPreferences` because Android/iOS app sandboxes
/// already isolate these values from other apps and the value of using
/// hardware-backed storage is small here.
class AppStorage {
AppStorage(this._prefs);
final SharedPreferences _prefs;
static const _keyServerAddress = 'serverAddress';
static const _keyUsername = 'username';
static const _keySavePassword = 'savePassword';
String get serverAddress => _prefs.getString(_keyServerAddress) ?? '';
Future<void> setServerAddress(String value) => _prefs.setString(_keyServerAddress, value);
String get username => _prefs.getString(_keyUsername) ?? '';
Future<void> setUsername(String value) => _prefs.setString(_keyUsername, value);
bool get savePassword => _prefs.getBool(_keySavePassword) ?? false;
Future<void> setSavePassword(bool value) => _prefs.setBool(_keySavePassword, value);
}
@@ -0,0 +1,33 @@
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
/// Hardware-backed secret store (Keystore on Android, Keychain on iOS).
/// Holds saved password (only when user opted in), login token, session
/// cookie, and any other high-sensitivity material.
class SecureAppStorage {
SecureAppStorage(this._storage);
final FlutterSecureStorage _storage;
String _passwordKey(String serverAddress, String username) =>
'password:$serverAddress:$username';
Future<String?> readPassword(String serverAddress, String username) {
return _storage.read(key: _passwordKey(serverAddress, username));
}
Future<void> writePassword(String serverAddress, String username, String password) {
return _storage.write(key: _passwordKey(serverAddress, username), value: password);
}
Future<void> deletePassword(String serverAddress, String username) {
return _storage.delete(key: _passwordKey(serverAddress, username));
}
Future<String?> readToken() => _storage.read(key: 'token');
Future<void> writeToken(String token) => _storage.write(key: 'token', value: token);
Future<void> deleteToken() => _storage.delete(key: 'token');
Future<String?> readSessionCookie() => _storage.read(key: 'sessionCookie');
Future<void> writeSessionCookie(String value) =>
_storage.write(key: 'sessionCookie', value: value);
Future<void> deleteSessionCookie() => _storage.delete(key: 'sessionCookie');
}