当前位置: 首页 > 知识库问答 >
问题:

实时数据库onDisconnect注销后不执行

朱啸
2023-03-14

我已经实现了Firebase实时数据库存在系统,如Firebase官方文档所示。我想使数据库安全,以便登录的用户只能在数据库中写入他们自己的存在条目。因此,在登录时,用户写入引用路径< code >/auth/{ authId }/connections ,同时设置< code>onDisconnect来删除该值。

以下是Android应用程序在rtdb中设置状态的代码:

getFirebaseDatabase().goOnline();
DatabaseReference.goOnline();

// Since I can connect from multiple devices, we store each connection instance separately
// any time that connectionsRef's value is null (i.e. has no children) I am offline
final FirebaseDatabase database = getFirebaseDatabase();
final DatabaseReference myConnectionsRef = database.getReference("/auth/" + getFirebaseAuth().getUid() + "/connections");

// Stores the timestamp of my last disconnect (the last time I was seen online)
final DatabaseReference lastOnlineRef = database.getReference("/auth/" + getFirebaseAuth().getUid() + "/lastOnline");

connectedRef = database.getReference(".info/connected");
presenceChangeListener = connectedRef.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot snapshot) {
        boolean connected = snapshot.getValue(Boolean.class);
        if (connected) {
            DatabaseReference con = myConnectionsRef.push();

            // When this device disconnects, remove it
            con.onDisconnect().removeValue()
                    .addOnSuccessListener(new OnSuccessListener<Void>() {
                        @Override
                        public void onSuccess(Void aVoid) {
                            // Add this device to my connections list
                            // this value could contain info about the device or a timestamp too
                            con.setValue("ANDROID");
                        }
                    })
                    .addOnFailureListener(new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            Log.d(TAG, "### Failed to set onDisconnect ###");
                            e.printStackTrace();
                        }
                    });

            // When I disconnect, update the last time I was seen online
            lastOnlineRef.onDisconnect().setValue(ServerValue.TIMESTAMP);
        }
    }

    @Override
    public void onCancelled(DatabaseError error) {
        Log.w(TAG, "Listener was cancelled at .info/connected");
    }
});

我遇到的问题是,如果用户注销,除非我首先手动断开与rtdb的连接,否则< code>onDisconnect不会执行。我假设运行在实时数据库上的代码得到了一个拒绝的许可,因为auth不再有效。

//If I don't go offline first the record in rtdb will not be removed.
DatabaseReference.goOffline();

AuthUI.getInstance().signOut(this)
.addOnCompleteListener(new OnCompleteListener<Void>() {
    public void onComplete(@NonNull Task<Void> task) {
        // user is now signed out
        Log.d(TAG, "Logged out");
        application.clearData();
        DatabaseReference.goOffline(); //This doesn't cause a presence update here
        finish();
    }
});

以上是我正在使用的解决方法,首先告诉数据库goOffline,然后注销。如果用户通过其他方式注销(Web应用程序正在查看多个选项卡是否正在使用该应用程序并且一个注销),用户将保留一个未删除的连接。

如果我在注销前不调用<code>goOffline()我还验证了,如果我将rtdb规则更改为<code>,我可以使一切正常工作。编写:<true</code>

我希望我的实时规则是这样的。

{
  "rules": {
    "auth": {
      "$uid": {
        ".read": "auth != null && auth.uid == $uid",
        ".write": "auth != null && auth.uid == $uid"
      }
    }
  }
}

我希望onDisconnect在设置onDisconnect时仍能在用户的身份验证下执行。

共有3个答案

郜联
2023-03-14

这是一个古老的问题,但它让我思考了一个可能的解决方案,并带来了以下内容...

使用“设置值/删除值”,只要您对应用程序有控制/感知能力

注销前使用onDisconnect.cancel和删除数据

我接受了@FrankvanPuffelen代码并修改了它,但没有测试它,所以。。。。

//getFirebaseDatabase().goOnline();
//DatabaseReference.goOnline();

// Since I can connect from multiple devices, we store each connection instance separately
// any time that connectionsRef's value is null (i.e. has no children) I am offline
final FirebaseDatabase database = getFirebaseDatabase();
final DatabaseReference myConnectionsRef = database.getReference("/auth/" + getFirebaseAuth().getUid() + "/connections");

// Stores the timestamp of my last disconnect (the last time I was seen online)
final DatabaseReference lastOnlineRef = database.getReference("/auth/" + getFirebaseAuth().getUid() + "/lastOnline");

connectedRef = database.getReference(".info/connected");
presenceChangeListener = connectedRef.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot snapshot) {
        boolean connected = snapshot.getValue(Boolean.class);
        if (connected) {
            // simple solution to reuse the old unique key-name otherwise current solution is like performing new registration of a new client over and over on the same client. we should use the old unique key-name until logout is performed
            String keyName = SharedPrefUtil.INSTANCE.getFirebaseConnectionKeyName(context);

             DatabaseReference con;
             if (TextUtils.isEmpty(keyName)) {
                     con = myConnectionsRef.push();
                     SharedPrefUtil.INSTANCE.setFirebaseConnectionKeyName(context.getApplicationContext(), con.getKey());
                 }else{
                      con = myConnectionsRef.child(keyName);
                 }

            // When this device disconnects, remove it
            con.onDisconnect().removeValue()
                    .addOnSuccessListener(new OnSuccessListener<Void>() {
                        @Override
                        public void onSuccess(Void aVoid) {
                            // Add this device to my connections list
                            // this value could contain info about the device or a timestamp too
                            con.setValue("ANDROID");
                        }
                    })
                    .addOnFailureListener(new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception e) {
                            Log.d(TAG, "### Failed to set onDisconnect ###");
                            e.printStackTrace();
                        }
                    });

            // When I disconnect, update the last time I was seen online
            lastOnlineRef.onDisconnect().setValue(ServerValue.TIMESTAMP);
        }
    }

    @Override
    public void onCancelled(DatabaseError error) {
        Log.w(TAG, "Listener was cancelled at .info/connected");
    }
});

在注销方法中,我们需要取消断开连接

String keyName = SharedPrefUtil.INSTANCE.getFirebaseConnectionKeyName(context);
if (!TextUtils.isEmpty(keyName)) {
    final FirebaseDatabase database = getFirebaseDatabase();
    final DatabaseReference myConnectionsRef = database.getReference("/auth/" + getFirebaseAuth().getUid() + "/connections/" + keyName);

    // Stores the timestamp of my last disconnect (the last time I was seen online)
    final DatabaseReference lastOnlineRef = database.getReference("/auth/" + getFirebaseAuth().getUid() + "/lastOnline");
    // This client/user doesn't need the disconnect functionality 
    myConnectionsRef.onDisconnect().cancel();
    // now we are on our own so we need to remove the key-name from the rmdb
    myConnectionsRef.setValue(null);
    // remove the key-name from the preferences so we will create a new one in the next login session
    SharedPrefUtil.INSTANCE.removeFirebaseConnectionKeyName(context);
    // we will not forget to disconnect last time updates 
    lastOnlineRef.onDisconnect().cancel()
}
AuthUI.getInstance().signOut(this)

我没有测试它,它不会运行,因为它缺少SharedPrefUtil实现

东方宜
2023-03-14

所以,因为1.)根据 RTDB 的规则评估连接() 执行, 2.)设置在断开连接() 上的用户可能会丢失身份验证,并且 3.)我想为我的授权用户提供安全状态系统...我想出了以下解决方案:

首先,将存在条目写入包含用户的authId和UUID的路径下的RTDB,使位置“不可访问”。< br > < code > "/presence/" { auth-uid } "/connections/" { UUID } < br >并设置一个< code >。onDisconnect()删除存储在不可访问位置的值。

然后,设置RTDB规则以执行以下操作:

  • 不允许读取状态数据。
  • 允许用户仅在其授权目录下添加/修改数据。
  • 允许任何用户删除记录(他们需要知道不可用的路径)
    "presence": {
      ".read": "false",
      ".write": "false",

      "$auth_id": {
        "connections": {
          "$uuid": {
            ".write": "(newData.exists() && $auth_id === auth.uid) || !newData.exists()"
          }
        }
      }
    }

最后,在RTDB上设置一个触发函数来读取< code >。ref('/presence/{ authid } ')位置,并将用户的在线状态推送到另一个用户可访问的位置(我正在将其推送到我的Firestore DB)。此外,如果用户从“在线”变为“离线”,则将lastOnline时间戳更新为当前时间。

鉴于我对拥有可靠和安全的存在系统的要求,这似乎是最好的解决方案。我希望这能帮助其他人。

仲承福
2023-03-14

当您附加onDisconnect()处理程序时,您正在Firebase服务器上注册延迟写入。在附加处理程序时和触发处理程序时都会检查是否允许写入。由于您的用户在触发写入时已签出,因此您的规则会拒绝它。没有配置选项可以更改此行为,因此您必须想出不同的方法。

 类似资料:
  • 我在一个Android应用程序中使用Firebase数据库,每次用户启动时,我都会在数据库中存储一些值,为此我会执行以下操作: 正如您在子方法中看到的,如果称为“usrId”,它将创建usrId目录,并在其中添加所有neccesary信息。但是我想为每个用户创建目录,所以我尝试传递usrId变量作为参数。但它不起作用。当我调试代码时,调试器说本地var usrId无法识别,我的问题是如何在Fire

  • 我使用Firebase,确切地说是一个实时数据库,我不知道应该设置什么规则。我制定了以下规则: 但现在每个人都可以写作了。当我设置这些: 使用Gmail的用户无法登录,因为数据库中的记录没有创建,但不是在所有设备上。当我在OnePlus上测试时,一切都很好,当我在三星上测试时,数据库中的记录没有创建。这是我负责创建用户的代码:

  • 我一直试图运行事务方法,但它无法在firebase数据库中获取和设置正确的数据。 代码如下:

  • 我在中收到一条消息,告诉我我的项目有不安全的规则。但当我检查它时,我不太明白什么是不安全的。 它显示如下: 我保留了读取访问权限,因为它支持一个网站。所以任何访问该网站的人都应该能够读取数据。 至于写权限,据我所知,我是唯一能写的人。 请注意,我还有第三个系列(CollectionThree),规则中没有提到。这可能是原因吗? 除此之外,我还可以想象,只有web服务器可以获得读取权限,以便将内容提

  • 我正在尝试检索一些值而不是其他值。事实上,我想通过用户的名字来搜索他们。 我尝试使用和,但找不到正确的解决方案。 如您所见,我还添加了。如果我不添加那个部分,它就不会检索任何东西。我想要的只是从我在TextField中所写的内容开始检索值。