我在JavaScript中编写了以下简单的状态代码(基于https://firebase.google.com/docs/database/web/offline-capabilities#section-sample):
var app = firebase.initializeApp(config);
var mainRef = app.database().ref();
var session = null;
var connected = false;
function do_sessionSubscribe(subscription) {
if (!subscription.entry) {
subscription.entry = subscription.parent.push(true);
subscription.entry.onDisconnect().remove();
}
}
function do_sessionUnsubscribe(subscription) {
if (subscription.entry) {
subscription.entry.remove();
subscription.entry = null;
}
}
mainRef.child(".info/connected").on("value", function(snap) {
connected = snap.val() === true;
if (session) {
if (connected) {
do_sessionSubscribe(session.subscription);
} else {
// workaround
//do_sessionUnsubscribe(session.subscription);
}
}
});
function closeSession() {
if (session) {
do_sessionUnsubscribe(session.subscription);
session = null;
}
}
function openSession(uid) {
session = { uid: uid, subscription: { parent: mainRef.child("session/user/" + uid), entry: null } };
if (connected) {
do_sessionSubscribe(session.subscription);
}
}
app.auth().onAuthStateChanged(function(user) {
closeSession();
if (user && user.uid) {
openSession(user.uid);
}
});
安全规则:
"session": {
"user": {
"$uid": {
".read": "auth.uid === $uid",
".write": "auth.uid === $uid",
"$session": {
".validate": "newData.val() === true"
}
}
},
}
这个想法是,用户的每个活动连接都会在连接/登录时创建/session/user/$uid/$session
,并在断开/注销时删除它。
因此,为了获得在线用户列表,只需/session/user
shallow=true
就可以了。
问题在于有时会话未被清理并永远保持在/session/user/$uid
之下。然后将其解释为如果用户始终在线。
我发现为了轻松重现问题,阻止访问securetoken.googleapis.com(我使用Google身份验证)就足够了,等一个小时然后关闭浏览器。
我尝试通过在断开连接时调用remove()来解决此问题。一旦客户端重新连接,这就清理了陈旧的会话(这太晚了,但迟到总比没有好......)。但是,当用户在断开互联网连接后关闭浏览器,然后在套接字超时之前,身份验证令牌过期,过时的会话将永远存在。