You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firestore全禁权限下Android端仍可读取数据问题排查

异常诱因

这个现象不是Firestore安全规则失效,也不是Android端绕过了权限校验,核心原因是Firestore Android SDK默认开启本地离线持久化缓存:

  • 你使用的addSnapshotListener是实时快照监听器,默认触发逻辑是优先返回本地已缓存的旧数据,再发起服务端请求做权限校验、拉取最新数据。你当前看到的可读数据,都是之前应用拥有读写权限时,已经缓存在设备本地的历史数据,根本不是从Firestore服务端新拉取的内容。
  • 写入方法crearDatos用的是单次写入操作add(),这类操作必须和服务端通信完成校验才能返回结果,所以会正常抛出权限错误,符合预期。
  • 同项目下的JavaScript应用读写都被正常拦截,是因为Firebase JS SDK默认关闭离线持久化,所有操作直接请求服务端走规则校验,不会读本地缓存。
代码中存在的问题

你的快照监听逻辑存在三个明显缺陷,直接导致了「权限被拒仍能读到数据」的错觉:

  • 未区分数据来源:回调触发时没有判断数据是来自本地缓存还是服务端返回,直接把本地缓存的旧数据加入列表渲染。等后续服务端返回权限拒绝错误时,数据已经加载到UI上了。
  • 错误处理逻辑不完整:收到权限错误时仅打印了日志,没有清空列表中已经加载的缓存数据,导致UI上一直保留缓存内容,看起来和读取成功没有区别。
  • 未绑定页面生命周期:注册快照监听器后没有保存ListenerRegistration实例,也没有在页面销毁时调用remove()方法注销监听,会造成内存泄漏,还会持续触发不必要的本地缓存同步。
修复方案

你可以根据业务需求二选一:

  1. 保留离线缓存能力,修正监听逻辑
    调整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();
        }
    }
    
  2. 不需要离线能力时直接关闭本地持久化
    初始化Firestore时关闭缓存,所有操作直接走服务端校验,表现会和JS端完全一致:
    // 放在FirebaseFirestore.getInstance()之后调用
    FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
            .setPersistenceEnabled(false)
            .build();
    mFirestore.setFirestoreSettings(settings);
    

你可以做个简单验证:清空应用数据(或卸载重装)彻底删除本地缓存后再打开应用,会发现快照监听器直接返回权限错误,读不到任何数据,和JS端表现完全一致,证明安全规则本身是正常生效的。

内容的提问来源于stack exchange,提问作者dostoyevski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 05:51:30