You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firebase任务管理应用数据隔离问题:用户间任务互相可见求助

解决Firebase任务管理应用中用户数据共享的问题

当前开发的Kotlin+Firebase任务管理应用存在数据隔离问题——不同账号用户能互相查看彼此的任务,所有任务都存储在同一数据节点下。需要实现每个用户的数据独立存储,仅能访问自己的任务。

注册代码

class RegisterPage : AppCompatActivity() {
    private lateinit var binding: ActivityRegisterPageBinding
    private lateinit var firebaseAuth: FirebaseAuth
    private lateinit var progressDialog: ProgressDialog

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityRegisterPageBinding.inflate(layoutInflater)
        setContentView(binding.root)

        firebaseAuth = FirebaseAuth.getInstance()

        progressDialog = ProgressDialog(this)
        progressDialog.setTitle("Please wait")
        progressDialog.setCanceledOnTouchOutside(false)

        binding.backButton.setOnClickListener{
            onBackPressed()
        }

        binding.regButton.setOnClickListener{
            validateData()
        }
    }

    private var name =""
    private var surname = ""
    private var email = ""
    private var password = ""
    private var confirmPassword = ""

    private fun validateData() {
        name = binding.regName.text.toString().trim()
        surname = binding.regSurname.text.toString().trim()
        email = binding.regEmail.text.toString().trim()
        password = binding.regCreate.text.toString().trim()
        confirmPassword = binding.regConfirm.text.toString().trim()

        if (name.isNotEmpty() && surname.isNotEmpty() && email.isNotEmpty() && password.isNotEmpty() && confirmPassword.isNotEmpty()) {
            if(password == confirmPassword){
                createAccount()
            }else{
                Toast.makeText(this, "Passwords do not match", Toast.LENGTH_SHORT).show()
            }
        }else{
            Toast.makeText(this, "Please provide missing details", Toast.LENGTH_SHORT).show()
        }
    }

    private fun createAccount(){
        progressDialog.setMessage("Creating account...")
        progressDialog.show()

        // Adding user to firebase
        firebaseAuth.createUserWithEmailAndPassword(email, password).addOnSuccessListener() {
            updateUser()
        }.addOnFailureListener{e->
            progressDialog.dismiss()
            Toast.makeText(this, "${e.message}", Toast.LENGTH_SHORT).show()
        }
    }

    private fun updateUser(){
        progressDialog.setMessage("Saving...")

        val timestamp = System.currentTimeMillis()

        // get curr user id
        val uid = firebaseAuth.uid

        // setup data to add in db
        val hashMap: HashMap<String, Any?> = HashMap()
        hashMap["uid"] = uid
        hashMap["name"] = name
        hashMap["surname"] = surname
        hashMap["email"] = email
        hashMap["timestamp"] = timestamp
        hashMap["profilePicture"] = ""
        hashMap["userType"] = "user"

        //set data to db
        val ref = FirebaseDatabase.getInstance().getReference("Users")
        ref.child(uid!!).setValue(hashMap).addOnSuccessListener {
            //Info saved, open home screen
            progressDialog.dismiss()
            Toast.makeText(this, "Account successfully created!", Toast.LENGTH_SHORT).show()
            startActivity(Intent(this@RegisterPage, HomeScreen::class.java))
            finish()

        }.addOnFailureListener{ e ->
            //failed adding data to db
            progressDialog.dismiss()
            Toast.makeText(this, "${e.message}", Toast.LENGTH_SHORT).show()
        }
    }
}

登录代码

class LoginPage : AppCompatActivity() {
    private lateinit var binding: ActivityLoginPageBinding
    private lateinit var firebaseAuth: FirebaseAuth
    private lateinit var progressDialog: ProgressDialog

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityLoginPageBinding.inflate(layoutInflater)
        setContentView(binding.root)

        firebaseAuth = FirebaseAuth.getInstance()

        progressDialog = ProgressDialog(this)
        progressDialog.setTitle("Please wait")
        progressDialog.setCanceledOnTouchOutside(false)

        binding.noAcc.setOnClickListener{
            startActivity(Intent(this, RegisterPage::class.java))
        }

        binding.loginButton.setOnClickListener{
            validateUserData()
        }

        binding.forgotPassword.setOnClickListener{
            startActivity(Intent(this, ForgotPassword::class.java))
        }
    }

    private var email = ""
    private var password = ""

    private fun validateUserData(){
        // Input data
        email = binding.loginEmail.text.toString().trim()
        password = binding.loginPassword.text.toString().trim()

        // Validating data
        if(!Patterns.EMAIL_ADDRESS.matcher(email).matches()){
            Toast.makeText(this, "Invalid email format", Toast.LENGTH_SHORT).show()
        }else if(password.isEmpty()){
            Toast.makeText(this, "Please enter your password", Toast.LENGTH_SHORT).show()
        }else{
            loginUser()
        }
    }

    // Login firebase auth
    private fun loginUser() {
        progressDialog.setMessage("Logging in...")
        progressDialog.show()

        firebaseAuth.signInWithEmailAndPassword(email, password)
            .addOnSuccessListener {
                // Successful login
                checkUser()
            }.addOnFailureListener { e ->
                // failed to login
                progressDialog.dismiss()
                Toast.makeText(this, "${e.message}", Toast.LENGTH_SHORT).show()
            }
    }

    private fun checkUser() {
        /* Check user type - Firebase Auth
        * user - move to user dashboard
        * admin - move to admin dashboard*/

        progressDialog.setMessage("Checking account...")

        val firebaseUser = firebaseAuth.currentUser!!

        val ref = FirebaseDatabase.getInstance().getReference("Users")
        ref.child(firebaseUser.uid).addListenerForSingleValueEvent(object: ValueEventListener{
            override fun onDataChange(snapshot: DataSnapshot) {
                progressDialog.dismiss()

                // Get user type - user/admin
                val userType = snapshot.child("userType").value
                if(userType == "user"){
                    startActivity(Intent(this@LoginPage, HomeScreen::class.java))
                    finish()
                }else if (userType == "admin"){
                    startActivity(Intent(this@LoginPage, HomeScreen::class.java))
                    finish()
                }
            }

            override fun onCancelled(error: DatabaseError) {
            }
        })
    }
}

解决方案

要实现用户数据完全隔离,需从数据库结构、业务代码、安全规则三个层面同步修改:

1. 调整数据库结构

将任务数据按用户UID分区存储,结构改为:

Tasks
  |--- {用户UID}
        |--- {任务ID}
              |--- title: "任务标题"
              |--- description: "任务描述"
              |--- timestamp: 123456789
              |--- ...其他任务字段

每个用户的任务都存储在自己UID对应的节点下,从结构上实现数据隔离。

2. 修改任务创建逻辑

创建任务时,获取当前登录用户的UID,将任务写入该用户专属节点:

private fun createTask(taskTitle: String, taskDesc: String) {
    val currentUser = firebaseAuth.currentUser ?: return
    val uid = currentUser.uid

    // 生成唯一任务ID
    val taskId = FirebaseDatabase.getInstance().getReference("Tasks")
        .child(uid)
        .push()
        .key ?: return

    val taskMap = hashMapOf(
        "title" to taskTitle,
        "description" to taskDesc,
        "timestamp" to System.currentTimeMillis(),
        "taskId" to taskId
    )

    FirebaseDatabase.getInstance().getReference("Tasks")
        .child(uid)
        .child(taskId)
        .setValue(taskMap)
        .addOnSuccessListener {
            Toast.makeText(this, "任务创建成功", Toast.LENGTH_SHORT).show()
        }
        .addOnFailureListener { e ->
            Toast.makeText(this, "创建失败: ${e.message}", Toast.LENGTH_SHORT).show()
        }
}

3. 修改任务读取逻辑

仅读取当前用户UID节点下的任务数据:

private fun loadUserTasks() {
    val currentUser = firebaseAuth.currentUser ?: return
    val uid = currentUser.uid

    val tasksRef = FirebaseDatabase.getInstance().getReference("Tasks").child(uid)
    tasksRef.addValueEventListener(object : ValueEventListener {
        override fun onDataChange(snapshot: DataSnapshot) {
            val taskList = mutableListOf<Task>() // 需自定义Task数据类
            for (taskSnapshot in snapshot.children) {
                val task = taskSnapshot.getValue(Task::class.java)
                task?.let { taskList.add(it) }
            }
            // 将taskList绑定到RecyclerView等UI组件展示
        }

        override fun onCancelled(error: DatabaseError) {
            Toast.makeText(this@HomeScreen, "加载失败: ${error.message}", Toast.LENGTH_SHORT).show()
        }
    })
}

4. 配置Firebase数据库安全规则

通过安全规则强制用户只能访问自己UID下的数据,防止恶意越权:

{
  "rules": {
    "Users": {
      "$uid": {
        ".read": "$uid === auth.uid",
        ".write": "$uid === auth.uid"
      }
    },
    "Tasks": {
      "$uid": {
        ".read": "$uid === auth.uid",
        ".write": "$uid === auth.uid"
      }
    }
  }
}

该规则确保只有登录用户能读写自己的用户信息和任务数据,从权限层面彻底隔离用户数据。

内容的提问来源于stack exchange,提问作者Skha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 17:45:42