Firebase实时数据库与存储权限问题:图片无法按用户隔离
问题原因分析
- 云存储规则变量名大小写不匹配:规则中定义的路径变量是
{userid}(小写d),但权限判断时用的是userId(大写D),Firebase规则对变量名大小写敏感,导致权限验证失败。 - 云存储规则路径匹配不全:当前规则仅匹配
/users/{userid}目录本身,而代码中上传的文件是该目录下的子文件(如/users/xxx/12345.jpg),规则未覆盖子路径,因此文件读写请求被拒绝。 - 实时数据库规则未覆盖图片数据节点:代码中把图片URL存在
Image根节点下,但当前规则仅配置了users节点,导致所有用户都能读取该节点下的所有图片,无法实现用户隔离。 - 数据库存储路径未按用户隔离:上传代码中把所有图片URL都存在
Image根节点,没有关联用户UID,展示页面直接读取整个Image节点,自然会加载所有用户的图片。
修正方案
1. 修正Firebase云存储规则
调整变量名大小写一致性,并匹配目录下所有子路径:
rules_version = '1'; service firebase.storage { match /users/{userId}/** { allow read: if request.auth.uid == userId; allow write: if request.auth.uid == userId; } }
**表示匹配/users/{userId}目录下的所有文件和子目录,确保文件级别的权限验证。- 变量名
userId保持一致,避免大小写导致的判断失效。
2. 修正Firebase实时数据库规则
添加Image节点的用户隔离规则,确保每个用户只能读写自己的图片数据:
{ "rules": { "users": { "$uid": { ".write": "$uid === auth.uid", ".read": "$uid === auth.uid" } }, "Image": { "$uid": { ".write": "$uid === auth.uid", ".read": "$uid === auth.uid" } } } }
3. 调整图片上传代码(数据库存储路径)
将图片URL存储到当前用户的专属节点下,而不是Image根节点:
private void uploadToFirebase(Uri uri){ if (FirebaseAuth.getInstance().getCurrentUser() != null) { String uid = FirebaseAuth.getInstance().getCurrentUser().getUid(); StorageReference fileRef = reference .child(uid) .child(System.currentTimeMillis() + "." + getFileExtension(uri)); fileRef.putFile(uri).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() { @Override public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { fileRef.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() { @Override public void onSuccess(Uri uri) { Model model = new Model(uri.toString()); String modelId = root.push().getKey(); // 修改:将图片数据存储到当前用户的Image节点下 root.child(uid).child(modelId).setValue(model); progressBar.setVisibility(View.INVISIBLE); Toast.makeText(MainActivity.this, "Uploaded Successfully", Toast.LENGTH_SHORT).show(); imageView.setImageResource(R.drawable.ic_baseline_add_photo_alternate_24); } }); } }).addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() { @Override public void onProgress(@NonNull UploadTask.TaskSnapshot snapshot) { progressBar.setVisibility(View.VISIBLE); } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { progressBar.setVisibility(View.INVISIBLE); Toast.makeText(MainActivity.this, "Uploading Failed !!", Toast.LENGTH_SHORT).show(); } }); } }
- 注意:确保
root是指向Image节点的数据库引用,即root = FirebaseDatabase.getInstance().getReference("Image");
4. 调整图片展示页面代码
仅读取当前用户的图片数据,而不是整个Image节点:
package com.example.auth; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import android.os.Bundle; import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.database.DataSnapshot; import com.google.firebase.database.DatabaseError; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; import com.google.firebase.database.ValueEventListener; import java.util.ArrayList; public class ShowActivity extends AppCompatActivity { private RecyclerView recyclerView; private ArrayList<Model> list; private MyAdapter adapter; private DatabaseReference root; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_show); recyclerView = findViewById(R.id.recyclerview); recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(new LinearLayoutManager(this)); list = new ArrayList<>(); adapter = new MyAdapter(this , list); recyclerView.setAdapter(adapter); // 获取当前用户UID,指向专属的图片节点 if(FirebaseAuth.getInstance().getCurrentUser() != null){ String uid = FirebaseAuth.getInstance().getCurrentUser().getUid(); root = FirebaseDatabase.getInstance().getReference("Image").child(uid); root.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { list.clear(); // 避免重复加载数据 for (DataSnapshot dataSnapshot : snapshot.getChildren()){ Model model = dataSnapshot.getValue(Model.class); list.add(model); } adapter.notifyDataSetChanged(); } @Override public void onCancelled(@NonNull DatabaseError error) { // 可添加错误提示逻辑 } }); } } }
- 添加
list.clear()避免页面重复加载时数据重复。 - 仅读取当前用户UID对应的
Image/{uid}节点,确保只加载自己的图片。
内容的提问来源于stack exchange,提问作者Morgan
相关产品推荐
相关产品推荐

