【问题标题】:Firebase Database: apparently rules do not apply if client is offline?Firebase 数据库:如果客户端离线,显然规则不适用?
【发布时间】:2017-03-18 06:46:55
【问题描述】:

考虑以下示例:

我正在test/ 节点上写入一个值,并且我设置了一个规则,该规则将允许写入,仅当“(新值)等于(旧值 + 1)”时,即 @ 987654322@

假设test/节点的初始值为0

如果客户端离线并执行以下命令:

testRef.setValue(1);
testRef.setValue(2);
testRef.setValue(3);
testRef.setValue(4);
testRef.setValue(5);

然后当他重新上线时,5 的值将写入数据库,但我不确定我是否明白为什么,因为5 != 0 + 1。我猜这是由于本地数据库中先前值的缓存而发生的,但不幸的是,这不是我想要达到的结果。我希望服务器拒绝该值,因为它不遵循数据库的规则。

有没有办法做到这一点?

或者是否有任何其他解决方法,以便我可以实现以下内容:

testRef.setValue(1); -> 将值写入本地数据库-> 检查值是否符合在线数据库的规则-> 如果值不符合规则,或者如果我们因为离线而无法检查,则从本地数据库中删除该值

【问题讨论】:

    标签: android firebase firebase-realtime-database firebase-security


    【解决方案1】:

    我认为对于您的问题,“离线”意味着客户端没有连接。在我的测试中,我通过启用飞行模式进行了模拟。

    Firebase 离线功能在 user guide 中进行了描述。那里提供的一个对您的问题很重要的细节是:

    Firebase 实时数据库客户端会自动保留一个队列 在您的应用离线时执行的所有写入操作... 当应用程序重新连接时,所有操作都发送到 Firebase 实时数据库服务器。

    您可以使用以下代码查看此行为,该代码将完成侦听器添加到 setValue() 调用。在我的测试中,我将设备置于飞行模式(离线),运行代码,然后禁用飞行模式以重新上线。为每个setValue() 生成一条日志消息,确认写入操作已排队并在重新建立连接时发送。这就解释了为什么写入满足您的验证规则:客户端不会发送一个最终值为 5 的写入请求,而是发送五个具有原始递增值的请求。

    您可以通过再次运行测试来确认规则是否有效,而无需先将test 的值重置回 0。每次写入都会失败。

    此代码还演示了 Firebase 客户端如何处理客户端离线时所做的更改,这些更改后来被安全规则拒绝。离线时,在客户端缓存中进行更改,onDataChange() 回调触发新的(未验证的)值。稍后,当客户端上线并且更改被服务器拒绝时,onDataChange() 会再次使用之前的值触发。

    final DatabaseReference ref = FirebaseDatabase.getInstance().getReference("test");
    
    ref.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            Log.d(TAG, "onDataChange: test=" + dataSnapshot.getValue(Integer.class));
        }
        @Override
        public void onCancelled(DatabaseError databaseError) {
            throw databaseError.toException();
        }
    });
    
    final DatabaseReference.CompletionListener completionListener =
            new DatabaseReference.CompletionListener() {
        @Override
        public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
            if (databaseError == null) {
                Log.d(TAG, "setValue() Success");
            } else {
                Log.d(TAG, "setValue() Failed " + databaseError.getMessage());
            }
        }
    };
    
    ref.setValue(1, completionListener);
    ref.setValue(2, completionListener);
    ref.setValue(3, completionListener);
    ref.setValue(4, completionListener);
    ref.setValue(5, completionListener);
    

    【讨论】:

    • 谢谢,我可以理解,但是如果由于缺少 Internet 连接而无法验证规则,我希望可以选择从本地缓存中删除值。我需要它的原因是因为我正在开发一个协作应用程序,并且一些节点可供超过 1 个用户访问。无论如何,如果没有任何 Internet 连接,是否有一种解决方法可以从本地缓存中删除这些值?像超时之类的东西? (即如果服务器在 5 秒内无法访问,则从缓存中删除所有挂起的写入/本地值。
    • AFAIK 没有 API 可以删除挂起的写入。请参阅 Firebaser FvP 的答案:stackoverflow.com/a/34625545/4815718。您可以做的最好的事情可能是monitor the connection state,并且只允许在连接时写入。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-30
    • 2021-12-10
    • 1970-01-01
    • 1970-01-01
    • 2018-03-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多