Firestore全禁权限下Android端仍可读取数据问题排查
异常诱因
这个现象不是Firestore安全规则失效,也不是Android端绕过了权限校验,核心原因是Firestore Android SDK默认开启本地离线持久化缓存:
- 你使用的
addSnapshotListener是实时快照监听器,默认触发逻辑是优先返回本地已缓存的旧数据,再发起服务端请求做权限校验、拉取最新数据。你当前看到的可读数据,都是之前应用拥有读写权限时,已经缓存在设备本地的历史数据,根本不是从Firestore服务端新拉取的内容。 - 写入方法
crearDatos用的是单次写入操作add(),这类操作必须和服务端通信完成校验才能返回结果,所以会正常抛出权限错误,符合预期。 - 同项目下的JavaScript应用读写都被正常拦截,是因为Firebase JS SDK默认关闭离线持久化,所有操作直接请求服务端走规则校验,不会读本地缓存。
代码中存在的问题
你的快照监听逻辑存在三个明显缺陷,直接导致了「权限被拒仍能读到数据」的错觉:
- 未区分数据来源:回调触发时没有判断数据是来自本地缓存还是服务端返回,直接把本地缓存的旧数据加入列表渲染。等后续服务端返回权限拒绝错误时,数据已经加载到UI上了。
- 错误处理逻辑不完整:收到权限错误时仅打印了日志,没有清空列表中已经加载的缓存数据,导致UI上一直保留缓存内容,看起来和读取成功没有区别。
- 未绑定页面生命周期:注册快照监听器后没有保存
ListenerRegistration实例,也没有在页面销毁时调用remove()方法注销监听,会造成内存泄漏,还会持续触发不必要的本地缓存同步。
修复方案
你可以根据业务需求二选一:
- 保留离线缓存能力,修正监听逻辑
调整EventChangeListener方法,增加数据来源判断和权限错误的UI处理,同时做好监听注销:// 声明监听实例变量,方便后续注销 private ListenerRegistration articulosListener; public void EventChangeListener() { articulosListener = mFirestore.collection("Articulos").addSnapshotListener(new EventListener<QuerySnapshot>() { @Override public void onEvent(@Nullable QuerySnapshot value, @Nullable FirebaseFirestoreException error) { if (error != null) { Log.e("Firestore Error",error.getMessage()); // 收到权限错误时清空列表,刷新UI if (error.getCode() == FirebaseFirestoreException.Code.PERMISSION_DENIED) { articulos.clear(); a.notifyDataSetChanged(); } return; } // 仅处理服务端返回的有效数据,避免直接加载无权限场景下的本地缓存 if (value != null && !value.getMetadata().isFromCache()) { articulos.clear(); for (DocumentChange dc : value.getDocumentChanges()) { if (dc.getType() == DocumentChange.Type.ADDED) { articulos.add(dc.getDocument().toObject(Articulo.class)); } } a.notifyDataSetChanged(); } } }); } // 在onDestroy中注销监听 @Override protected void onDestroy() { super.onDestroy(); if (articulosListener != null) { articulosListener.remove(); } } - 不需要离线能力时直接关闭本地持久化
初始化Firestore时关闭缓存,所有操作直接走服务端校验,表现会和JS端完全一致:// 放在FirebaseFirestore.getInstance()之后调用 FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder() .setPersistenceEnabled(false) .build(); mFirestore.setFirestoreSettings(settings);
你可以做个简单验证:清空应用数据(或卸载重装)彻底删除本地缓存后再打开应用,会发现快照监听器直接返回权限错误,读不到任何数据,和JS端表现完全一致,证明安全规则本身是正常生效的。
内容的提问来源于stack exchange,提问作者dostoyevski
相关产品推荐
相关产品推荐

