Flutter:非Firebase环境下授权Isolate访问Google Drive的方法
非Firebase应用Google Drive后台备份授权解决方案
一、主线程获取并存储授权凭证
1. 配置OAuth权限与客户端ID
在Google Cloud Console中为你的应用创建OAuth 2.0客户端ID(对应Android/iOS/Desktop平台),并添加所需的Google Drive授权范围,推荐用https://www.googleapis.com/auth/drive.file(仅访问应用创建的文件,权限更安全)。
2. 获取完整授权凭证并存储
使用google_signin包获取包含refresh token的授权数据——注意refresh token仅在首次登录时返回,后续登录不会重复返回,所以必须在首次获取时保存:
import 'package:google_sign_in/google_sign_in.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; final GoogleSignIn _googleSignIn = GoogleSignIn( scopes: ['https://www.googleapis.com/auth/drive.file'], clientId: '你的OAuth客户端ID', // 非Firebase应用必须手动指定 ); final _secureStorage = const FlutterSecureStorage(); Future<void> signInAndPersistCredentials() async { try { final account = await _googleSignIn.signIn(); if (account == null) return; final auth = await account.authentication; // 仅在refresh token存在时存储,避免覆盖已有的有效refresh token if (auth.refreshToken != null) { await _secureStorage.write(key: 'refresh_token', value: auth.refreshToken); } // 存储access token和过期时间(默认1小时有效期) await _secureStorage.write(key: 'access_token', value: auth.accessToken); await _secureStorage.write( key: 'token_expiry', value: DateTime.now().add(const Duration(seconds: 3600)).toIso8601String(), ); } catch (e) { print('授权失败: $e'); } }
3. 刷新过期的access token
当access token过期时,用refresh token换取新的access token,并更新存储中的数据:
import 'package:http/http.dart' as http; import 'dart:convert'; Future<String?> refreshToken(String refreshToken) async { final response = await http.post( Uri.parse('https://oauth2.googleapis.com/token'), body: { 'client_id': '你的OAuth客户端ID', 'client_secret': '你的OAuth客户端密钥', // 服务器端客户端ID需要,Android/iOS可选 'refresh_token': refreshToken, 'grant_type': 'refresh_token', }, ); if (response.statusCode == 200) { final data = jsonDecode(response.body); final newAccessToken = data['access_token'] as String; final newExpiry = DateTime.now().add(Duration(seconds: data['expires_in'] as int)); await _secureStorage.write(key: 'access_token', value: newAccessToken); await _secureStorage.write(key: 'token_expiry', value: newExpiry.toIso8601String()); return newAccessToken; } else { print('刷新token失败: ${response.body}'); return null; } }
二、后台Isolate中使用授权数据
1. 从SecureStorage读取凭证
flutter_secure_storage支持在Isolate中直接读取,无需额外线程同步:
Future<Map<String, String>?> fetchCredentials() async { final storage = const FlutterSecureStorage(); final accessToken = await storage.read(key: 'access_token'); final refreshToken = await storage.read(key: 'refresh_token'); final expiryStr = await storage.read(key: 'token_expiry'); if (accessToken == null || refreshToken == null || expiryStr == null) { return null; } return { 'access_token': accessToken, 'refresh_token': refreshToken, 'expiry': expiryStr, }; }
2. 验证凭证并调用Google Drive API
在后台WorkManager任务中,先检查access token是否过期,过期则刷新,然后执行备份操作:
import 'package:googleapis/drive/v3.dart' as drive; import 'package:http/http.dart' as http; import 'dart:io'; Future<void> performDriveBackup() async { final credentials = await fetchCredentials(); if (credentials == null) { // 无有效凭证,需提示用户重新登录 return; } final expiry = DateTime.parse(credentials['expiry']!); var accessToken = credentials['access_token']!; final refreshToken = credentials['refresh_token']!; // 检查token是否过期 if (DateTime.now().isAfter(expiry)) { final newToken = await refreshToken(refreshToken); if (newToken == null) { // 刷新失败,需重新授权 return; } accessToken = newToken; } // 初始化Drive API客户端 final authClient = http.Client() ..headers['Authorization'] = 'Bearer $accessToken'; final driveApi = drive.DriveApi(authClient); // 执行备份(示例:上传本地备份文件) final backupFile = File('/path/to/your/backup.file'); final media = drive.Media(backupFile.openRead(), backupFile.lengthSync()); final fileMetadata = drive.File()..name = 'app_backup_${DateTime.now().toIso8601String()}'; try { await driveApi.files.create(fileMetadata, uploadMedia: media); } catch (e) { print('备份失败: $e'); } finally { authClient.close(); } }
3. 后台任务配置注意事项
- Android:在
AndroidManifest.xml中声明INTERNET权限,并为WorkManager任务设置网络约束(比如要求UNMETERED或NOT_ROAMING网络)。 - iOS:开启后台模式中的"Background fetch"或"Remote notifications"权限,确保后台任务能被系统调度执行。
- 安全:始终使用
flutter_secure_storage存储凭证,避免明文存储在SharedPreferences等不安全位置。
内容的提问来源于stack exchange,提问作者zakiya.tamimi
相关产品推荐
相关产品推荐

