Firebase跨设备用户ID一致但数据检索不一致问题求助
跨设备Firebase认证与Firestore数据一致性问题
问题背景
在基于Compose官方文档的MakeItSo App项目中,将原项目的匿名认证改为仅允许已认证用户进行Firestore读写操作,但出现跨设备数据无法互通的问题:同一账号在不同设备登录后,保存的数据userId一致,但跨设备查询时无法找到对应文档。
数据模型
data class User( val id: String = "", ) data class Client( @DocumentId val id: String = "", val name: String = "", val phone: String = "", val city: String = "", val userId: String = "" )
当前Firestore规则
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write: if request.time < timestamp.date(2023, 12, 8); } } }
问题现象
- 同一账号在两台设备登录,Firebase返回的UID相同,保存的Client数据userId均为
xxx - 跨设备查询时,无法获取到另一设备保存的对应userId的Client文档
相关代码实现
AccountServiceImpl
class AccountServiceImpl @Inject constructor( private val auth: FirebaseAuth ) : AccountService { override val currentUserId: String get() = auth.currentUser?.uid.orEmpty() override val hasUser: Boolean get() = auth.currentUser != null override val currentUser: Flow<User> get() = callbackFlow { val listener = FirebaseAuth.AuthStateListener { auth -> this.trySend(auth.currentUser?.let { User(it.uid) } ?: User()) } auth.addAuthStateListener(listener) awaitClose { auth.removeAuthStateListener(listener) } } override suspend fun authenticate(email: String, password: String) { auth.signInWithEmailAndPassword(email, password).await() } override suspend fun signOut() { auth.signOut() } }
StorageServiceImpl
class StorageServiceImpl @Inject constructor( private val firestore: FirebaseFirestore, private val auth: AccountService ) : StorageService { @OptIn(ExperimentalCoroutinesApi::class) override val clients: Flow<List<Client>> get() = auth.currentUser.flatMapLatest { user -> firestore.collection(CLIENTS_COLLECTION) .whereEqualTo(USER_ID_FIELD, user.id) .orderBy(ORDER_CLIENTS_BY) .dataObjects() } override suspend fun getClient(clientId: String): Client? = firestore.collection(CLIENTS_COLLECTION).document(clientId).get().await().toObject() override suspend fun saveClient(client: Client): String { val clientWithUserId = client.copy(userId = auth.currentUserId) return firestore.collection(CLIENTS_COLLECTION).add(clientWithUserId).await().id } override suspend fun updateClient(client: Client) { firestore.collection(CLIENTS_COLLECTION).document(client.id).set(client).await() } override suspend fun deleteClient(clientId: String) { firestore.collection(CLIENTS_COLLECTION).document(clientId).delete().await() } companion object { const val CLIENTS_COLLECTION = "clients" const val ORDER_CLIENTS_BY = "name" const val USER_ID_FIELD = "userId" } }
需求
确保已认证用户ID唯一,并实现跨设备数据检索的一致性。
解决方案
1. 修正Firestore安全规则
当前规则仅通过时间限制访问,未验证用户身份且截止日期已过,导致实际无合法访问权限。替换为以下规则,确保仅已认证用户能读写自己的数据:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /clients/{client} { // 仅允许已认证用户读取自己的客户端数据 allow read: if request.auth != null && resource.data.userId == request.auth.uid; // 仅允许已认证用户创建/更新属于自己的客户端数据 allow write: if request.auth != null && request.resource.data.userId == request.auth.uid; } } }
2. 检查查询与数据同步逻辑
强制从服务器获取数据:避免本地缓存导致的查询不一致,修改
clientsFlow的查询逻辑:@OptIn(ExperimentalCoroutinesApi::class) override val clients: Flow<List<Client>> get() = auth.currentUser.flatMapLatest { user -> flow { val snapshot = firestore.collection(CLIENTS_COLLECTION) .whereEqualTo(USER_ID_FIELD, user.id) .orderBy(ORDER_CLIENTS_BY) .get(Source.SERVER) // 强制从服务器拉取数据 .await() emit(snapshot.toObjects(Client::class.java)) } }若需实时监听,确保
snapshotListener优先同步服务器数据,或调整Firestore缓存策略。确保更新时userId不被覆盖:在
updateClient方法中强制保留当前用户的userId,避免更新时传入错误值:override suspend fun updateClient(client: Client) { val clientWithCorrectUserId = client.copy(userId = auth.currentUserId) firestore.collection(CLIENTS_COLLECTION).document(client.id).set(clientWithCorrectUserId).await() }
3. 验证用户认证状态的实时性
- 确认
currentUserFlow正确监听AuthState变化,确保用户登录后及时更新用户ID,避免使用空字符串或旧ID查询。 - 在查询和数据操作前添加非空校验,例如判断
auth.currentUserId是否为空,避免无效操作。
4. 检查Firestore数据存储
直接在Firebase控制台查看clients集合的文档,确认userId字段与用户UID完全一致(注意大小写、空格等细节,Firestore字段值区分大小写)。
内容的提问来源于stack exchange,提问作者Collaxd
相关产品推荐
相关产品推荐

