fix:优化和对齐版本
This commit is contained in:
1 parent
364fc837b3
commit
bd6bb0ffce
115 files changed
+155
-3647
No files matched your search
@@ -1,16 +1,19 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
|
||||
import '../services/device_header_service.dart';
|
||||
import '../storage/secure_identity_storage.dart';
|
||||
|
||||
const _kJwtKey = 'jwt_token';
|
||||
const _storage = FlutterSecureStorage();
|
||||
final _defaultIdentityStorage = SecureIdentityStorage();
|
||||
|
||||
Future<void> saveJwt(String token) =>
|
||||
_storage.write(key: _kJwtKey, value: token);
|
||||
Future<void> clearJwt() => _storage.delete(key: _kJwtKey);
|
||||
Future<String?> loadJwt() => _storage.read(key: _kJwtKey);
|
||||
Future<void> saveJwt(String token) => _defaultIdentityStorage.saveJwt(token);
|
||||
Future<void> clearJwt() => _defaultIdentityStorage.clearJwt();
|
||||
Future<String?> loadJwt() => _defaultIdentityStorage.readJwt();
|
||||
|
||||
class AuthInterceptor extends Interceptor {
|
||||
AuthInterceptor(this._identityStorage);
|
||||
|
||||
final SecureIdentityStorage _identityStorage;
|
||||
|
||||
@override
|
||||
Future<void> onRequest(
|
||||
RequestOptions options,
|
||||
@@ -19,7 +22,7 @@ class AuthInterceptor extends Interceptor {
|
||||
await DeviceHeaderService.to.ensureInitialized();
|
||||
options.headers.addAll(DeviceHeaderService.to.commonHeaders());
|
||||
options.headers.addAll(DeviceHeaderService.to.platformIdentityHeaders());
|
||||
final jwt = await loadJwt();
|
||||
final jwt = await _identityStorage.readJwt();
|
||||
if (jwt != null && jwt.isNotEmpty) {
|
||||
options.headers['Authorization'] = 'Bearer $jwt';
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
import '../config/app_config.dart';
|
||||
import '../storage/secure_identity_storage.dart';
|
||||
import 'auth_interceptor.dart';
|
||||
import 'kratos_error_interceptor.dart';
|
||||
|
||||
@@ -12,12 +13,20 @@ part 'dio_provider.g.dart';
|
||||
|
||||
const _protobufContentType = 'application/x-protobuf';
|
||||
|
||||
/// Per-request override for native protobuf-over-HTTP endpoints.
|
||||
Options protobufOptions() => Options(
|
||||
responseType: ResponseType.bytes,
|
||||
contentType: _protobufContentType,
|
||||
headers: const {'Accept': _protobufContentType},
|
||||
);
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
AppConfig appConfig(Ref ref) => AppConfig.fromEnvironment();
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
Dio dio(Ref ref) {
|
||||
final config = ref.watch(appConfigProvider);
|
||||
final identityStorage = ref.watch(secureIdentityStorageProvider);
|
||||
final d = Dio(
|
||||
BaseOptions(
|
||||
baseUrl: config.apiBaseUrl,
|
||||
@@ -31,7 +40,7 @@ Dio dio(Ref ref) {
|
||||
},
|
||||
),
|
||||
);
|
||||
d.interceptors.add(AuthInterceptor());
|
||||
d.interceptors.add(AuthInterceptor(identityStorage));
|
||||
d.interceptors.add(KratosErrorInterceptor());
|
||||
if (!kReleaseMode) {
|
||||
d.interceptors.add(LogInterceptor(requestBody: false, responseBody: false));
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
/// Android requires explicit privacy consent before collecting device
|
||||
/// identifiers. iOS follows its existing startup flow instead.
|
||||
bool requiresAndroidPrivacyConsent({
|
||||
required bool isWeb,
|
||||
required TargetPlatform platform,
|
||||
required bool privacyAgreed,
|
||||
}) => !isWeb && platform == TargetPlatform.android && !privacyAgreed;
|
||||
@@ -3,7 +3,7 @@ import 'dart:io' show Platform;
|
||||
import 'package:android_id/android_id.dart';
|
||||
import 'package:android_oaid/android_oaid.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
import 'package:flutter/foundation.dart' show defaultTargetPlatform, kIsWeb;
|
||||
import 'package:flutter_paid/flutter_paid.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
@@ -12,6 +12,7 @@ import 'package:uuid/uuid.dart';
|
||||
|
||||
import '../storage/storage_keys.dart';
|
||||
import '../storage/storage_service.dart';
|
||||
import '../privacy/privacy_gate.dart';
|
||||
|
||||
/// 请求公共 + 平台身份 header 来源(完全对齐研值契约)。
|
||||
/// AuthInterceptor 每个请求注入 commonHeaders() + platformIdentityHeaders(),
|
||||
@@ -52,9 +53,11 @@ class DeviceHeaderService {
|
||||
|
||||
// Align with tradingagents-flutter-app: Android only initializes device
|
||||
// information after the user has accepted the privacy policy.
|
||||
if (!kIsWeb &&
|
||||
Platform.isAndroid &&
|
||||
!StorageService.to.getBool(storagePrivacyAgreed)) {
|
||||
if (requiresAndroidPrivacyConsent(
|
||||
isWeb: kIsWeb,
|
||||
platform: defaultTargetPlatform,
|
||||
privacyAgreed: StorageService.to.getBool(storagePrivacyAgreed),
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -184,10 +187,11 @@ class DeviceHeaderService {
|
||||
|
||||
/// 是否需要首启隐私合规确认。**仅 Android**(合规要求:同意前不得采集设备
|
||||
/// 标识)。由 go_router redirect 调用,未同意则强制跳隐私页。
|
||||
bool get privacyConsentRequired =>
|
||||
!kIsWeb &&
|
||||
Platform.isAndroid &&
|
||||
!StorageService.to.getBool(storagePrivacyAgreed);
|
||||
bool get privacyConsentRequired => requiresAndroidPrivacyConsent(
|
||||
isWeb: kIsWeb,
|
||||
platform: defaultTargetPlatform,
|
||||
privacyAgreed: StorageService.to.getBool(storagePrivacyAgreed),
|
||||
);
|
||||
|
||||
/// iOS 首次启动页:仅在首次安装后展示一次。
|
||||
bool get iosStartupRequired =>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
const _jwtKey = 'jwt_token';
|
||||
|
||||
/// Sensitive identity values stay behind one injectable storage boundary.
|
||||
class SecureIdentityStorage {
|
||||
SecureIdentityStorage({FlutterSecureStorage? storage})
|
||||
: _storage = storage ?? const FlutterSecureStorage();
|
||||
|
||||
final FlutterSecureStorage _storage;
|
||||
|
||||
Future<void> saveJwt(String token) =>
|
||||
_storage.write(key: _jwtKey, value: token);
|
||||
Future<void> clearJwt() => _storage.delete(key: _jwtKey);
|
||||
Future<String?> readJwt() => _storage.read(key: _jwtKey);
|
||||
}
|
||||
|
||||
final secureIdentityStorageProvider = Provider<SecureIdentityStorage>(
|
||||
(ref) => SecureIdentityStorage(),
|
||||
);
|
||||
Reference in new issue
Block a user