我已经实现了Firebase实时数据库状态系统,如官方Firebase文档中所示。我想确保数据库的安全性,以便已登录的用户只能写自己在数据库中的状态条目。因此,在登录时,用户将写入参考路径/auth/{authId}/connections
,同时设置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断开连接,否则onDisconnect
不会执行。我假设在实时数据库上运行的代码由于权限不再有效而被拒绝权限。
//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应用程序正在查看是否有多个选项卡正在使用该应用程序,而一个用户注销了),则该用户将保持不删除连接的状态。
如果我在注销前未调用goOffline()
,即使强制关闭应用程序,rtdb中的连接也不会被删除。
我还验证了,如果我将rtdb规则更改为".write": "true"
<-,那一切都可以正常进行,这不好。这告诉我,当用户注销身份验证时,运行onDisconnect
时会拒绝一个权限。
我希望我的实时规则是这样的。
{
"rules": {
"auth": {
"$uid": {
".read": "auth != null && auth.uid == $uid",
".write": "auth != null && auth.uid == $uid"
}
}
}
}
我希望在设置onDisconnect
时,onDisconnect
仍然能够通过用户的身份执行。
答案 0 :(得分:0)
在附加onDisconnect()
处理程序时,您正在Firebase服务器上注册延迟写入。在附加处理程序和触发处理程序时,都会检查是否允许该写入。并且由于触发写操作时您的用户已注销,因此规则会拒绝它。没有配置选项可以更改此行为,因此您必须提出另一种方法。
答案 1 :(得分:0)
因此,因为1.)根据RTDB的规则评估onDisconnect()
的执行情况; 2。)设置onDisconnect()
的用户可能会丢失身份验证,以及3.)我想为存在身份验证的用户提供安全的在线状态系统...我想出了以下解决方案:
首先,在包含用户的authId和UUID的路径下,将状态项写入RTDB,以使位置“不可用”。
"/presence/" + {auth-uid} + "/connections/" + {UUID}
并设置.onDisconnect()
来删除存储在无法猜测的位置的该值。
然后,设置RTDB规则以执行以下操作:
"presence": {
".read": "false",
".write": "false",
"$auth_id": {
"connections": {
"$uuid": {
".write": "(newData.exists() && $auth_id === auth.uid) || !newData.exists()"
}
}
}
}
最后,在RTDB上设置一个触发功能,以读取.ref('/presence/{authid}')
位置并将用户的状态推送到另一个用户可访问位置(我将其推送到Firestore数据库)。另外,如果用户从“联机”更改为“脱机”,则将lastOnline时间戳更新为当前时间。
鉴于我对拥有可靠和安全的状态系统的要求,这似乎是最好的解决方案。我希望这对其他人有帮助。