From 9323bdb9b152c2f6ff290722e834738eab2d22d6 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Fri, 25 Jul 2025 12:08:42 +0800 Subject: [PATCH] =?UTF-8?q?refactor(apple=E7=99=BB=E5=BD=95):=20=E9=87=8D?= =?UTF-8?q?=E6=9E=84Apple=E7=99=BB=E5=BD=95=E9=80=BB=E8=BE=91=E5=B9=B6?= =?UTF-8?q?=E7=A7=BB=E9=99=A4Firebase=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 使用纯sign_in_with_apple实现替代原有Firebase依赖方案 - 添加环境检查、调试日志和错误处理 - 实现完整的凭证管理和用户信息提取 - 更新依赖版本至sign_in_with_apple 7.0.1 --- lib/modules/login/controllers/appleLogin.dart | 193 ++++++++++++++---- .../login/controllers/login_controller.dart | 49 +++-- pubspec.yaml | 2 +- 3 files changed, 183 insertions(+), 61 deletions(-) diff --git a/lib/modules/login/controllers/appleLogin.dart b/lib/modules/login/controllers/appleLogin.dart index f77b86393..02c4db1f7 100644 --- a/lib/modules/login/controllers/appleLogin.dart +++ b/lib/modules/login/controllers/appleLogin.dart @@ -1,12 +1,12 @@ import 'dart:convert'; import 'dart:io'; import 'dart:math'; -import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/foundation.dart'; import 'package:sign_in_with_apple/sign_in_with_apple.dart'; import 'package:crypto/crypto.dart'; import 'package:get/get.dart'; +/// Apple登录控制器(纯sign_in_with_apple实现) class Apple { String? token = ''; String name = ''; @@ -14,7 +14,12 @@ class Apple { String pictureurl = ''; String id = ''; - /// 生成随机nonce字符串 + // 存储原始的Apple凭证信息 + String? _identityToken; + String? _authorizationCode; + String? _userIdentifier; + + /// 生成随机nonce字符串(用于安全验证) String generateNonce([int length = 32]) { const charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._'; @@ -30,7 +35,7 @@ class Apple { return digest.toString(); } - /// Apple登录主方法 + /// Apple登录主方法(纯sign_in_with_apple实现) Future signInWithApple() async { try { // 检查Apple登录可用性 @@ -41,11 +46,23 @@ class Apple { return false; } - UserCredential userCredential = await loginApple(); - token = await userCredential.user?.getIdToken(); + if (kDebugMode) { + print('开始执行Apple登录流程'); + } + + // 执行Apple登录 + final appleCredential = await _getAppleCredential(); + + // 提取并存储用户信息 + _extractUserInfo(appleCredential); + + // 使用identityToken作为登录token + token = appleCredential.identityToken; if (kDebugMode) { - print('Apple登录响应数据token: $token'); + print('Apple登录成功'); + print('Token: $token'); + print('用户信息: name=$name, email=$email, id=$id'); } return token != null && token!.isNotEmpty; @@ -57,83 +74,102 @@ class Apple { } } - /// 执行Apple登录流程 - Future loginApple() async { - // 生成nonce用于安全验证 + /// 获取Apple登录凭证 + Future _getAppleCredential() async { + // 生成nonce用于安全验证(遵循官方最佳实践) final rawNonce = generateNonce(); final nonce = sha256ofString(rawNonce); try { - // 请求Apple登录凭证 + // 请求Apple登录凭证(与官方示例保持一致的参数结构) final appleCredential = await SignInWithApple.getAppleIDCredential( scopes: [ AppleIDAuthorizationScopes.email, AppleIDAuthorizationScopes.fullName, ], nonce: nonce, + // 可以添加state参数用于防CSRF攻击(可选) + state: 'voitrans-login-${DateTime.now().millisecondsSinceEpoch}', ); - // 提取用户信息 - _extractUserInfo(appleCredential); - - // 创建Firebase OAuth凭证 - final oauthCredential = OAuthProvider("apple.com").credential( - idToken: appleCredential.identityToken, - rawNonce: rawNonce, - ); + // 存储原始凭证信息(遵循官方示例的数据提取方式) + _identityToken = appleCredential.identityToken; + _authorizationCode = appleCredential.authorizationCode; + _userIdentifier = appleCredential.userIdentifier; if (kDebugMode) { - print('Apple OAuth凭证创建成功'); - print('用户信息: name=$name, email=$email, id=$id'); + print('Apple凭证获取成功'); + print('Identity Token长度: ${_identityToken?.length ?? 0}'); + print('Authorization Code长度: ${_authorizationCode?.length ?? 0}'); + print('User Identifier: $_userIdentifier'); + print('State: ${appleCredential.state}'); + + // 调试输出用户信息(与官方示例类似) + if (appleCredential.givenName != null) { + print('Given Name: ${appleCredential.givenName}'); + } + if (appleCredential.familyName != null) { + print('Family Name: ${appleCredential.familyName}'); + } + if (appleCredential.email != null) { + print('Email: ${appleCredential.email}'); + } } - // 使用Firebase Auth进行登录 - return await FirebaseAuth.instance.signInWithCredential(oauthCredential); + return appleCredential; } catch (e) { if (kDebugMode) { - print('Apple登录流程异常:$e'); + print('获取Apple凭证异常:$e'); } - throw Exception('Apple登录失败: $e'); + throw Exception('获取Apple凭证失败: $e'); } } - /// 提取Apple返回的用户信息 + + + /// 提取Apple返回的用户信息(遵循官方示例的处理方式) void _extractUserInfo(AuthorizationCredentialAppleID credential) { - // 用户ID + // 用户ID(官方示例中的主要标识符) id = credential.userIdentifier ?? ''; // 邮箱(可能为空,如果用户选择隐藏邮箱) email = credential.email ?? ''; - // 用户姓名(仅首次登录时提供) - if (credential.givenName != null || credential.familyName != null) { - final givenName = credential.givenName ?? ''; - final familyName = credential.familyName ?? ''; - name = '$givenName $familyName'.trim(); + // 用户姓名处理(与官方示例保持一致的逻辑) + // 注意:姓名信息仅在首次登录时提供 + final givenName = credential.givenName; + final familyName = credential.familyName; + + if (givenName != null || familyName != null) { + // 按照官方示例的方式组合姓名 + final firstName = givenName ?? ''; + final lastName = familyName ?? ''; + name = '$firstName $lastName'.trim(); + } else { + // 如果没有姓名信息,保持空值(后续登录不会提供姓名) + name = ''; } // Apple不提供头像,使用默认值 pictureurl = ''; if (kDebugMode) { - print('提取的用户信息:'); - print('ID: $id'); - print('Email: $email'); - print('Name: $name'); + print('=== 提取的用户信息 ==='); + print('User Identifier: $id'); + print('Email: ${email.isEmpty ? "未提供或隐藏" : email}'); + print('Given Name: ${givenName ?? "未提供"}'); + print('Family Name: ${familyName ?? "未提供"}'); + print('Combined Name: ${name.isEmpty ? "未提供" : name}'); print('Picture URL: $pictureurl'); + print('========================'); } } /// 退出Apple登录 Future signOutApple() async { try { - await FirebaseAuth.instance.signOut(); // 清空用户信息 - token = ''; - name = ''; - email = ''; - pictureurl = ''; - id = ''; + _clearUserInfo(); if (kDebugMode) { print('Apple登录退出成功'); @@ -145,14 +181,87 @@ class Apple { } } + /// 清空用户信息 + void _clearUserInfo() { + token = ''; + name = ''; + email = ''; + pictureurl = ''; + id = ''; + _identityToken = null; + _authorizationCode = null; + _userIdentifier = null; + } + /// 检查Apple登录环境 static Future checkAppleSignInEnvironment() async { if (kDebugMode) { print('=== Apple登录环境检查 ==='); print('平台: ${GetPlatform.isIOS ? "iOS" : "非iOS"}'); print('Apple登录可用性: ${await SignInWithApple.isAvailable()}'); - print('Firebase Auth实例: ${FirebaseAuth.instance.currentUser?.uid ?? "未登录"}'); + + // 检测设备地区 + final locale = Get.deviceLocale; + final countryCode = locale?.countryCode?.toUpperCase(); + print('设备地区: $countryCode'); + print('认证方式: 纯Apple登录(无Firebase依赖)'); + print('========================'); } } + + /// 获取认证方式名称 + String getAuthMethodName() { + return 'Apple Sign-In'; + } + + /// 获取详细的凭证信息(用于调试) + Map getCredentialInfo() { + return { + 'identityToken': _identityToken, + 'authorizationCode': _authorizationCode, + 'userIdentifier': _userIdentifier, + 'name': name, + 'email': email, + 'id': id, + }; + } + + /// 获取用于后端验证的数据(遵循官方示例的后端集成方式) + /// + /// [deviceId] 设备ID + /// 返回格式化的登录数据,包含Apple凭证信息 + Map getLoginData(String deviceId) { + return { + // 现有系统兼容的字段 + "mail": email, + "phone": "", + "stype": 5, // Apple登录类型 + "vcode": "", + "ttoken": token ?? '', // 主要使用identityToken + "phonemodel": "", + "phonemac": deviceId, + "name": name, + "avatar": pictureurl, + "auth_method": "apple_signin", // 标识使用纯Apple登录 + + // Apple特有的凭证信息(遵循官方示例) + "apple_credentials": { + "identity_token": _identityToken, + "authorization_code": _authorizationCode, + "user_identifier": _userIdentifier, + "email": email, + "name": name, + } + }; + } + + /// 获取原始Apple凭证信息(用于高级验证) + Map getAppleCredentials() { + return { + 'identityToken': _identityToken, + 'authorizationCode': _authorizationCode, + 'userIdentifier': _userIdentifier, + }; + } } diff --git a/lib/modules/login/controllers/login_controller.dart b/lib/modules/login/controllers/login_controller.dart index 6852634ef..818e94b8e 100644 --- a/lib/modules/login/controllers/login_controller.dart +++ b/lib/modules/login/controllers/login_controller.dart @@ -2,6 +2,7 @@ import '../../../data/models/appconfig.dart'; import '../../../data/models/appconfig_model.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart'; import 'package:logger/web.dart'; import '../../../routes/app_routes.dart'; import 'dart:convert'; @@ -881,28 +882,39 @@ class LoginController extends GetxController { } case 5: //Apple登录 { - Logger().d("数据获取Apple"); + Logger().d("开始Apple登录流程"); final apple = Apple(); + + // 先检查环境(调试用) + if (kDebugMode) { + await Apple.checkAppleSignInEnvironment(); + } + bool authResult = await apple.signInWithApple(); if (authResult) { - Logger().d("数据获取_Apple.token${apple.token}"); - Logger().d("数据获取_Apple.email${apple.email}"); - Logger().d("数据获取_Apple.name${apple.name}"); - Logger().d("数据获取_Apple.id${apple.id}"); - - // 验证成功,跳转到配对页面 - requestLogin({ - "mail": apple.email, - "phone": "", - "stype": loginType, - "vcode": "", - "ttoken": apple.token, - "phonemodel": "", - "phonemac": _deviceId, - "name": apple.name, - "avatar": apple.pictureurl, - }); + Logger().d("Apple登录成功"); + Logger().d("认证方式: ${apple.getAuthMethodName()}"); + Logger().d("Token: ${apple.token}"); + Logger().d("Email: ${apple.email}"); + Logger().d("Name: ${apple.name}"); + Logger().d("ID: ${apple.id}"); + + // 获取登录数据并发送到后端 + final loginData = apple.getLoginData(_deviceId); + Logger().d("发送登录数据: $loginData"); + + // 直接调用现有的requestLogin方法 + requestLogin(loginData); + } else { + Logger().e("Apple登录失败"); + Get.snackbar( + 'error'.tr, + 'Apple登录失败,请重试', + snackPosition: SnackPosition.TOP, + backgroundColor: Colors.red.withValues(alpha: 0.1), + colorText: Colors.red, + ); } break; } @@ -1029,4 +1041,5 @@ class LoginController extends GetxController { return false; } } + } diff --git a/pubspec.yaml b/pubspec.yaml index c601eebd1..db31dc5c8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -65,7 +65,7 @@ dependencies: firebase_auth: ^5.5.1 google_sign_in: ^6.2.2 flutter_facebook_auth: ^7.1.1 - sign_in_with_apple: ^6.1.2 + sign_in_with_apple: ^7.0.1 flutter_image_compress: ^2.4.0 common_utils: ^2.1.0 image_gallery_saver_plus: ^4.0.1