如何配置Firebase实时数据库规则以读取所有用户的mention节点?
核心问题:Firebase Realtime Database权限拒绝,无法读取所有用户的mention节点
我正在配置Firebase Realtime Database规则,想要允许所有已认证用户读取任意其他用户数据里的mention节点,但调用checkMention()方法时一直弹出"permission denied"的Toast提示,不确定当前规则配置是否正确。
当前数据库规则
{ "rules": { "users": { "$uid": { ".read": "auth.uid == $uid", ".write": "auth.uid == $uid" }, "mention": { ".read": "true", ".write": "auth.uid == null" } } } }
当前代码实现
checkMention方法
public void checkMention(String mention, MentionListener mentionListener) { database.child("users").addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { for (DataSnapshot userSnapshot : snapshot.getChildren()) { String userMention = userSnapshot.child("mention").getValue(String.class); if (userMention != null && userMention.equals(mention)) { if (mentionListener != null) { mentionListener.onMentionData(true); } } } } @Override public void onCancelled(@NonNull DatabaseError error) { Toast.makeText(context, "Error: " + error.getMessage(), Toast.LENGTH_SHORT).show(); if (mentionListener != null) { mentionListener.onMentionData(false); } } }); }
MainActivity对话框代码
private void openDialogUpdate() { MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this); DialogUpdateBinding updateBinding = DialogUpdateBinding.inflate(getLayoutInflater()); AlertDialog dialog = builder.setView(updateBinding.getRoot()).create(); TextInputLayout usernameInput = updateBinding.usernameInput; TextInputEditText inputUsername = updateBinding.inputUsername; MaterialButton buttonUpdate = updateBinding.buttonUpdate; inputUsername.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { if (mentionCheckRunnable != null) { handler.removeCallbacks(mentionCheckRunnable); } mentionCheckRunnable = () -> { String mention = "@" + s.toString().trim().replace(" ", "_").toLowerCase(); if (!mention.equals("@")) { accountFirebase.checkMention(mention, exists -> { if (exists) { usernameInput.setError(getString(R.string.unavailable_mention)); } else { usernameInput.setHelperText(getString(R.string.available_mention)); usernameInput.setError(null); } }); } buttonUpdate.setText(mention); }; handler.postDelayed(mentionCheckRunnable, 1500); } }); buttonUpdate.setOnClickListener(v -> { if (inputUsername.getText() != null) { String uid = accountFirebase.getUid(); String username = inputUsername.getText().toString().trim(); if (!username.isEmpty() && username.length() > 8) { accountFirebase.updateUsername(username, uid).addOnCompleteListener(task -> { if (task.isSuccessful()) { dialog.dismiss(); } }); } else if (!username.isEmpty() && username.length() < 8) { showMessage(updateBinding.getRoot(), getString(R.string.username_too_short)); } else { showMessage(updateBinding.getRoot(), getString(R.string.username_empty)); } } }); dialog.setCancelable(false); dialog.show(); }
预期目标
- 用户创建账户进入MainActivity后,弹出对话框要求更新名称
- 用户输入时,
checkMention()检索所有用户的mention节点,判断该mention是否已被占用 - 仅当mention未被占用时,用户才能完成名称更新
解决方案
1. 修正数据库规则
当前规则的结构错误:你把mention节点直接挂在users下,但实际数据里mention是每个$uid节点的子节点。同时代码读取的是整个users节点,而规则里未开放users节点的读取权限,因此触发权限拒绝。
调整规则,让每个用户节点下的mention子节点对所有已认证用户开放读取权限,同时保留用户其他数据的私有性:
{ "rules": { "users": { "$uid": { ".read": "auth.uid == $uid", ".write": "auth.uid == $uid", "mention": { ".read": "auth != null" } } } } }
2. 优化查询代码(推荐)
当前代码读取整个users节点效率极低,随着用户量增加会消耗大量带宽和资源。建议利用Firebase的查询功能直接检索目标mention:
public void checkMention(String mention, MentionListener mentionListener) { database.child("users") .orderByChild("mention") .equalTo(mention) .addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { boolean exists = snapshot.exists(); if (mentionListener != null) { mentionListener.onMentionData(exists); } } @Override public void onCancelled(@NonNull DatabaseError error) { Toast.makeText(context, "Error: " + error.getMessage(), Toast.LENGTH_SHORT).show(); if (mentionListener != null) { mentionListener.onMentionData(false); } } }); }
这样既减少了数据传输量,又提升了查询效率,配合修正后的规则即可正常读取mention节点数据。
内容的提问来源于stack exchange,提问作者Vanderclin Rocha
相关产品推荐
相关产品推荐

