求Android/iOS/Flutter多应用单设备集中登录方案(类Yandex ID)
多应用共享认证账户实现方案(仿Yandex ID)
针对你需要多同认证体系应用共享已登录账户(含Token)、数据随任一应用留存的需求,分平台给出具体实现思路和代码示例:
Android 平台
推荐方案:自定义账户类型的AccountManager
AccountManager是Android系统级的账户管理组件,通过自定义统一账户类型,让所有同体系应用都能操作该类型账户,避免单一应用卸载导致数据丢失。
步骤1:配置权限与账户类型
所有同体系应用的AndroidManifest.xml中添加权限:
<uses-permission android:name="android.permission.GET_ACCOUNTS" /> <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" /> <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
同时定义自定义账户类型(比如com.yourapp.account),并注册Authenticator服务(所有应用可复用相同的Authenticator实现):
<service android:name=".AccountAuthenticatorService" android:exported="true"> <intent-filter> <action android:name="android.accounts.AccountAuthenticator" /> </intent-filter> <meta-data android:name="android.accounts.AccountAuthenticator" android:resource="@xml/authenticator" /> </service>
res/xml/authenticator.xml配置:
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android" android:accountType="com.yourapp.account" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" />
步骤2:Authenticator服务实现
class AccountAuthenticatorService : Service() { private lateinit var authenticator: AccountAuthenticator override fun onCreate() { authenticator = AccountAuthenticator(this) } override fun onBind(intent: Intent): IBinder { return authenticator.iBinder } inner class AccountAuthenticator(context: Context) : AbstractAccountAuthenticator(context) { override fun addAccount(response: AccountAuthenticatorResponse?, accountType: String?, authTokenType: String?, requiredFeatures: Array<out String>?, options: Bundle?): Bundle { return Bundle() } override fun getAuthToken(response: AccountAuthenticatorResponse?, account: Account?, authTokenType: String?, options: Bundle?): Bundle { return Bundle() } // 实现其他必要的抽象方法,返回默认Bundle即可 override fun confirmCredentials(response: AccountAuthenticatorResponse?, account: Account?, options: Bundle?): Bundle = Bundle() override fun editProperties(response: AccountAuthenticatorResponse?, accountType: String?): Bundle = Bundle() override fun getAuthTokenLabel(authTokenType: String?): String? = null override fun hasFeatures(response: AccountAuthenticatorResponse?, account: Account?, features: Array<out String>?): Bundle = Bundle() override fun updateCredentials(response: AccountAuthenticatorResponse?, account: Account?, authTokenType: String?, options: Bundle?): Bundle = Bundle() } }
步骤3:账户增查操作
登录成功后保存账户:
val accountManager = AccountManager.get(context) val account = Account(username, "com.yourapp.account") // 存储Token到账户的UserData中 accountManager.addAccountExplicitly(account, null, bundleOf("auth_token" to loginToken))
其他应用查询账户列表:
val accounts = accountManager.getAccountsByType("com.yourapp.account") val accountList = accounts.map { account -> val token = accountManager.getUserData(account, "auth_token") Pair(account.name, token) }
iOS 平台
推荐方案:Keychain Groups
iOS的Keychain Groups是官方原生的跨应用敏感数据共享方案,只要所有应用配置相同的Keychain Group,即可共享数据,且数据随任一应用留存。
步骤1:配置Keychain Group
- 在Apple开发者后台,为所有同体系应用开启相同的Keychain Group(比如
group.com.yourapp.accounts) - 在每个应用的
Info.plist中添加配置:
<key>keychain-access-groups</key> <array> <string>group.com.yourapp.accounts</string> </array>
步骤2:封装Keychain操作工具类
import Security class SharedAccountManager { static let shared = SharedAccountManager() private let groupIdentifier = "group.com.yourapp.accounts" // 保存账户与Token func saveAccount(username: String, token: String) { let query: [CFString: Any] = [ kSecClass: kSecClassGenericPassword, kSecAttrAccount: username, kSecAttrService: groupIdentifier, kSecValueData: token.data(using: .utf8)!, kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly ] // 先删除旧数据再新增 SecItemDelete(query as CFDictionary) SecItemAdd(query as CFDictionary, nil) } // 获取所有账户列表 func getAllAccounts() -> [(username: String, token: String)] { let query: [CFString: Any] = [ kSecClass: kSecClassGenericPassword, kSecAttrService: groupIdentifier, kSecReturnAttributes: kCFBooleanTrue!, kSecReturnData: kCFBooleanTrue!, kSecMatchLimit: kSecMatchLimitAll ] var result: AnyObject? let status = SecItemCopyMatching(query as CFDictionary, &result) guard status == errSecSuccess, let items = result as? [[String: Any]] else { return [] } return items.compactMap { item in guard let username = item[kSecAttrAccount as String] as? String, let tokenData = item[kSecValueData as String] as? Data, let token = String(data: tokenData, encoding: .utf8) else { return nil } return (username, token) } } }
步骤3:业务层调用
登录成功保存:
SharedAccountManager.shared.saveAccount(username: "user123", token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...")
其他应用获取账户:
let accounts = SharedAccountManager.shared.getAllAccounts() for account in accounts { print("用户名:\(account.username),Token:\(account.token)") }
Flutter 跨平台方案
通过MethodBridge桥接原生实现,实现跨平台账户共享:
Flutter端代码
import 'package:flutter/services.dart'; class SharedAccountManager { static const MethodChannel _channel = MethodChannel('com.yourapp/shared_account'); /// 保存账户 static Future<void> saveAccount(String username, String token) async { await _channel.invokeMethod( 'saveAccount', {'username': username, 'token': token}, ); } /// 获取账户列表 static Future<List<Map<String, String>>> getAccounts() async { final List<dynamic> result = await _channel.invokeMethod('getAccounts'); return result.map((item) => { 'username': item['username'] as String, 'token': item['token'] as String, }).toList(); } }
原生端适配
分别在Android和iOS端实现MethodChannel的方法回调,对应上述原生平台的代码逻辑即可。
云端同步补充(可选)
如果需要跨设备同步账户(如Yandex ID),可结合认证服务器实现:
- 无需依赖设备ID,首次启动时生成随机UUID存储在本地,将UUID与账户信息绑定存储到服务器
- 应用重装后,通过用户登录行为关联新UUID,拉取该用户的登录记录
- 云端存储需加密敏感数据,仅在客户端解密使用
内容的提问来源于stack exchange,提问作者Djakh
相关产品推荐
相关产品推荐

