You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 07:54:22