killapp/Assets/Scripts/Managers/FirebaseAuthManager.cs
“虞渠成” 99e461590d fix: 修复国内设备启动闪退问题,禁用 Firebase 自动初始化
修改 FirebaseAuthManager 改为懒加载,仅在用户触发登录时初始化 Firebase
移除 AndroidManifest 中 Firebase 相关自动初始化的 provider
关闭 MainScene 的服务器后门开关,提升安全性
2026-09-24 10:16:09 +08:00

471 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using AppleAuth;
using AppleAuth.Enums;
using AppleAuth.Interfaces;
using AppleAuth.Native;
using Kill.Utils;
using UnityEngine;
#if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR
using Firebase;
using Firebase.Auth;
#endif
namespace Kill.Managers
{
/// <summary>
/// Firebase 第三方登录管理器(支持 Google / Apple
/// </summary>
public class FirebaseAuthManager : MonoBehaviour
{
public static FirebaseAuthManager Instance { get; private set; }
// Google 登录配置 - 从 Firebase 控制台获取的 Web 客户端 ID
private const string WEB_CLIENT_ID = "785438724947-kpjbqi43hbj6eddianbjsgkgkkclkfmd.apps.googleusercontent.com";
#if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR
private FirebaseAuth auth;
#endif
private AppleAuthManager appleAuthManager;
private void Awake()
{
Instance = this;
// 不在此处初始化 Firebase。
// 国内无 Google 服务的设备上,启动即初始化 Firebase 会闪退,
// 因此改为懒加载:在真正用到 Firebase谷歌/苹果登录)时才初始化。
InitializeAppleAuth();
}
private void Update()
{
appleAuthManager?.Update();
}
// Firebase 是否已初始化完成
private bool firebaseReady;
// 初始化是否已开始(防止并发重复初始化)
private bool firebaseInitStarted;
// 等待初始化完成的回调队列
private readonly List<Action> pendingReady = new List<Action>();
/// <summary>
/// 懒加载:确保 Firebase 已初始化,未初始化则先初始化,完成后在主线程回调 onReady。
/// </summary>
private void EnsureFirebaseReady(Action onReady)
{
if (onReady == null) return;
if (firebaseReady)
{
onReady();
return;
}
if (firebaseInitStarted)
{
pendingReady.Add(onReady);
return;
}
firebaseInitStarted = true;
pendingReady.Add(onReady);
StartFirebaseInit();
}
private void StartFirebaseInit()
{
#if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR
FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task =>
{
var dependencyStatus = task.Result;
MainThread.Enqueue(() =>
{
firebaseReady = dependencyStatus == DependencyStatus.Available;
if (firebaseReady)
{
auth = FirebaseAuth.DefaultInstance;
Debug.Log("Firebase 初始化成功");
}
else
{
Debug.LogError($"Firebase 初始化失败: {dependencyStatus},第三方登录不可用");
}
FlushPending();
});
});
#else
FlushPending();
#endif
}
/// <summary>
/// 回调所有等待初始化完成的调用
/// </summary>
private void FlushPending()
{
var callbacks = pendingReady.ToArray();
pendingReady.Clear();
firebaseInitStarted = false;
foreach (var cb in callbacks)
cb?.Invoke();
}
/// <summary>
/// 初始化 Apple Sign In
/// </summary>
private void InitializeAppleAuth()
{
if (AppleAuthManager.IsCurrentPlatformSupported)
{
appleAuthManager = new AppleAuthManager(new PayloadDeserializer());
Debug.Log("Apple Sign In 初始化成功");
}
else
{
Debug.Log("当前平台不支持 Apple Sign In");
}
}
/// <summary>
/// 谷歌登录
/// </summary>
public void SignInWithGoogle(Action<GoogleLoginResult> onSuccess, Action<string> onError, int timeoutSeconds = 30)
{
// 懒加载:仅在用户主动点谷歌登录时才初始化 Firebase未初始化则先初始化
EnsureFirebaseReady(() =>
{
SignInWithProvider("google.com", new string[] { "email", "profile" }, onSuccess, onError, timeoutSeconds);
});
}
/// <summary>
/// 苹果登录(使用 apple-signin-unity + Firebase OAuth Credential
/// </summary>
public void SignInWithApple(Action<GoogleLoginResult> onSuccess, Action<string> onError, int timeoutSeconds = 30)
{
// 懒加载与谷歌登录同理iOS 上 Firebase 也按需初始化
EnsureFirebaseReady(() => SignInWithAppleInternal(onSuccess, onError));
}
/// <summary>
/// 苹果登录真正实现(须在 Firebase 初始化完成后调用)
/// </summary>
private void SignInWithAppleInternal(Action<GoogleLoginResult> onSuccess, Action<string> onError)
{
#if UNITY_IOS && !UNITY_EDITOR
if (appleAuthManager == null)
{
onError?.Invoke("Apple Sign In 未初始化");
return;
}
if (!AppleAuthManager.IsCurrentPlatformSupported)
{
onError?.Invoke("当前平台不支持苹果登录");
return;
}
var rawNonce = GenerateRandomString(32);
var nonce = GenerateSHA256NonceFromRawNonce(rawNonce);
var loginArgs = new AppleAuthLoginArgs(
LoginOptions.IncludeEmail | LoginOptions.IncludeFullName,
nonce);
appleAuthManager.LoginWithAppleId(
loginArgs,
credential =>
{
Debug.Log($"[AppleAuth] 登录成功回调触发, credential类型: {credential?.GetType().Name}");
var appleIdCredential = credential as IAppleIDCredential;
if (appleIdCredential == null)
{
var passwordCredential = credential as IPasswordCredential;
Debug.LogWarning($"[AppleAuth] credential 不是 AppleIDCredential, 实际类型: {credential?.GetType().Name}, PasswordCredential: {passwordCredential != null}");
MainThread.Enqueue(() => onError?.Invoke("未获取到 Apple ID 凭证"));
return;
}
var identityToken = Encoding.UTF8.GetString(appleIdCredential.IdentityToken);
var authorizationCode = Encoding.UTF8.GetString(appleIdCredential.AuthorizationCode);
Debug.Log($"[AppleAuth] identityToken长度: {identityToken?.Length}, authorizationCode长度: {authorizationCode?.Length}");
if (string.IsNullOrEmpty(identityToken))
{
MainThread.Enqueue(() => onError?.Invoke("Apple identity token 为空"));
return;
}
SignInWithAppleCredential(appleIdCredential, identityToken, authorizationCode, rawNonce, onSuccess, onError);
},
error =>
{
Debug.LogError($"[AppleAuth] 登录失败回调触发: {error?.LocalizedDescription}");
MainThread.Enqueue(() => onError?.Invoke($"Apple 登录失败: {error.LocalizedDescription}"));
});
#else
onError?.Invoke("苹果登录仅在 iOS 平台可用");
#endif
}
#if UNITY_IOS && !UNITY_EDITOR
/// <summary>
/// 使用 Apple 凭证完成 Firebase 登录
/// </summary>
private void SignInWithAppleCredential(
IAppleIDCredential appleIdCredential,
string identityToken,
string authorizationCode,
string rawNonce,
Action<GoogleLoginResult> onSuccess,
Action<string> onError)
{
if (auth == null)
{
onError?.Invoke("Firebase 未初始化");
return;
}
var firebaseCredential = OAuthProvider.GetCredential(
"apple.com",
identityToken,
rawNonce,
authorizationCode);
auth.SignInWithCredentialAsync(firebaseCredential).ContinueWith(task =>
{
if (task.IsCanceled)
{
MainThread.Enqueue(() => onError?.Invoke("Firebase 登录被取消"));
return;
}
if (task.IsFaulted)
{
MainThread.Enqueue(() => onError?.Invoke("Firebase 登录失败"));
return;
}
var user = auth.CurrentUser;
if (user == null)
{
MainThread.Enqueue(() => onError?.Invoke("获取 Firebase 用户信息失败"));
return;
}
MainThread.Enqueue(() =>
{
var result = new GoogleLoginResult
{
FirebaseUserId = user.UserId,
Email = user.Email ?? appleIdCredential.Email,
DisplayName = user.DisplayName ?? GetAppleDisplayName(appleIdCredential.FullName),
PhotoUrl = user.PhotoUrl?.ToString(),
IdToken = "",
Provider = "apple.com"
};
onSuccess?.Invoke(result);
});
});
}
#endif
/// <summary>
/// 通用 OAuth 登录Google
/// </summary>
private void SignInWithProvider(string providerId, string[] scopes, Action<GoogleLoginResult> onSuccess, Action<string> onError, int timeoutSeconds)
{
#if UNITY_ANDROID && !UNITY_EDITOR
if (auth == null)
{
onError?.Invoke("Firebase 未初始化");
return;
}
bool isCompleted = false;
// 启动超时检测
Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromSeconds(timeoutSeconds));
if (!isCompleted)
{
isCompleted = true;
MainThread.Enqueue(() => onError?.Invoke($"{providerId} 登录超时({timeoutSeconds}秒)"));
}
});
var provider = new FederatedOAuthProviderData(providerId);
provider.Scopes = scopes;
var federatedProvider = new FederatedOAuthProvider(provider);
auth.SignInWithProviderAsync(federatedProvider).ContinueWith(task =>
{
if (isCompleted) return; // 已超时,忽略结果
isCompleted = true;
if (task.IsFaulted)
{
var errorMsg = task.Exception?.InnerException?.Message ?? task.Exception?.Message ?? "未知错误";
Debug.LogError($"Firebase {providerId} 登录失败详情: {errorMsg}");
MainThread.Enqueue(() => onError?.Invoke($"{providerId} 登录失败"));
return;
}
if (task.IsCanceled)
{
MainThread.Enqueue(() => onError?.Invoke($"{providerId} 登录被取消"));
return;
}
var user = auth.CurrentUser;
if (user == null)
{
MainThread.Enqueue(() => onError?.Invoke("获取用户信息失败"));
return;
}
// 构造登录结果
MainThread.Enqueue(() =>
{
var result = new GoogleLoginResult
{
FirebaseUserId = user.UserId,
Email = user.Email,
DisplayName = user.DisplayName,
PhotoUrl = user.PhotoUrl?.ToString(),
IdToken = "",
Provider = providerId
};
onSuccess?.Invoke(result);
});
});
#else
// 编辑器 / iOS 下谷歌登录不可用
Task.Delay(1000).ContinueWith(_ =>
{
Debug.Log($"编辑器模拟 {providerId} 登录");
MainThread.Enqueue(() =>
{
onError?.Invoke($"{providerId} 登录在当前平台不可用");
});
});
#endif
}
/// <summary>
/// 登出
/// </summary>
public void SignOut()
{
#if (UNITY_ANDROID || UNITY_IOS) && !UNITY_EDITOR
auth?.SignOut();
Debug.Log("Firebase 登出成功");
#endif
}
#region Nonce
/// <summary>
/// 生成随机字符串作为 raw nonce
/// </summary>
private static string GenerateRandomString(int length)
{
if (length <= 0)
{
throw new Exception("Expected nonce to have positive length");
}
const string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._";
using (var rng = new RNGCryptoServiceProvider())
{
var result = new StringBuilder();
var randomNumberHolder = new byte[1];
while (result.Length < length)
{
var randomNumbers = new List<int>(16);
for (var i = 0; i < 16; i++)
{
rng.GetBytes(randomNumberHolder);
randomNumbers.Add(randomNumberHolder[0]);
}
foreach (var randomNumber in randomNumbers)
{
if (result.Length >= length)
break;
if (randomNumber < charset.Length)
{
result.Append(charset[randomNumber]);
}
}
}
return result.ToString();
}
}
/// <summary>
/// 计算 raw nonce 的 SHA256 值,传给 Apple
/// </summary>
private static string GenerateSHA256NonceFromRawNonce(string rawNonce)
{
using (var sha = SHA256.Create())
{
var hash = sha.ComputeHash(Encoding.UTF8.GetBytes(rawNonce));
var result = new StringBuilder();
for (var i = 0; i < hash.Length; i++)
{
result.Append(hash[i].ToString("x2"));
}
return result.ToString();
}
}
#endregion
#region
/// <summary>
/// 从 Apple 全名构造显示名称
/// </summary>
private static string GetAppleDisplayName(IPersonName fullName)
{
if (fullName == null)
return null;
var parts = new List<string>();
if (!string.IsNullOrEmpty(fullName.GivenName))
parts.Add(fullName.GivenName);
if (!string.IsNullOrEmpty(fullName.FamilyName))
parts.Add(fullName.FamilyName);
return parts.Count > 0 ? string.Join(" ", parts) : null;
}
#endregion
}
/// <summary>
/// 第三方登录结果(兼容 Google / Apple
/// </summary>
public class GoogleLoginResult
{
public string FirebaseUserId; // Firebase 用户ID用于绑定
public string Email; // 邮箱
public string DisplayName; // 显示名称
public string PhotoUrl; // 头像URL
public string IdToken; // ID Token
public string Provider; // 登录提供方: "google.com" / "apple.com"
}
}