本頁面由 Cloud Translation API 翻譯而成。
Switch to English

基於用戶的安全性

本文檔重新審視了“ 保護數據”中的概念,並結合了預定義的auth變量以創建用於保護數據的完整解決方案。

集成身份驗證

Firebase身份驗證與Firebase實時數據庫集成在一起,可讓您基於每個用戶控制數據訪問。

用戶驗證後,您的實時數據庫規則中的auth變量將填充用戶的信息。此信息包括其唯一標識符( uid )以及鏈接的帳戶數據(例如Facebook ID或電子郵件地址)以及其他信息。如果實現自定義身份驗證提供程序,則可以將自己的字段添加到用戶的身份驗證有效負載中。

本指南說明瞭如何將Firebase實時數據庫規則語言與有關用戶的身份驗證信息結合在一起。通過結合這兩個概念,您可以基於用戶身份控制對數據的訪問。

auth變量

進行身份驗證之前,規則中的預定義auth變量為null。使用Firebase身份驗證對用戶進行身份驗證後 ,它將包含以下屬性:

提供者使用的身份驗證方法(“密碼”,“匿名”,“ facebook”,“ github”,“ google”或“ twitter”)。
uid 唯一的用戶ID,保證在所有提供商中都是唯一的。
代幣 Firebase身份驗證ID令牌的內容。有關更多詳細信息,請參見auth.token的參考文檔。

以下是一個示例規則,該規則使用auth變量來確保每個用戶只能寫入用戶特定的路徑:

{
  "rules": {
    "users": {
      "$user_id": {
        // grants write access to the owner of this user account
        // whose uid must exactly match the key ($user_id)
        ".write": "$user_id === auth.uid"
      }
    }
  }
}

構建數據庫

有時以簡化編寫安全規則的方式來結構化數據庫很有幫助。例如,在實時數據庫中存儲用戶數據的一種常見模式是將所有用戶存儲在單個users節點中,其子節點是每個用戶的uid值。如果您想限制對此數據的訪問,以使只有登錄的用戶可以看到他們自己的數據,則您的規則將如下所示:

{
  "rules": {
    "users": {
      "$uid": {
        ".read": "auth != null && auth.uid == $uid"
      }
    }
  }
}

使用身份驗證自定義聲明

對於需要針對不同用戶進行自定義訪問控制的應用,Firebase身份驗證允許開發人員對Firebase用戶設置聲明 。您可以在規則的auth.token變量中使用這些聲明。以下是使用hasEmergencyTowel自定義聲明的規則示例:

{
  "rules": {
    "frood": {
      // A towel is about the most massively useful thing an interstellar
      // hitchhiker can have
      ".read": "auth.token.hasEmergencyTowel === true"
    }
  }
}

創建自己的自定義身份驗證令牌的開發人員可以選擇向這些令牌添加聲明。這些聲明可用於您規則中的auth.token變量。

重訪聊天示例

讓我們以保護您的數據為基礎的聊天示例為基礎,並添加一些用戶身份驗證,將所有這些概念整合到一個可運行的應用程序中:

{
  "rules": {
    "room_names": {
      // any logged in user can get a list of room names
      ".read": "auth !== null",

      "$room_id": {
        // this is just for documenting the structure of rooms, since
        // they are read-only and no write rule allows this to be set
        ".validate": "newData.isString()"
      }
    },

    "members": {
       // I can join or leave any room (otherwise it would be a boring demo)
       // I can have a different name in each room just for fun
       "$room_id": {
          // any member can read the list of member names
          ".read": "data.child(auth.uid).exists()",

          // room must already exist to add a member
          ".validate": "root.child('room_names/'+$room_id).exists()",

          "$user_id": {
             ".write": "auth.uid === $user_id",
             ".validate": "newData.isString() && newData.val().length > 0 && newData.val().length < 20"
          }
       }
    },

    "messages": {
      "$room_id": {
        // the list of messages for a room can be read by any member
        ".read": "root.child('members/'+$room_id+'/'+auth.uid).exists()",

        // room we want to write a message to must be valid
        ".validate": "root.child('room_names/'+$room_id).exists()",

        "$message_id": {
          // a new message can be created if it does not exist, but it
          // cannot be modified or deleted
          // any member of a room can write a new message
          ".write": "root.child('members/'+$room_id+'/'+auth.uid).exists() && !data.exists() && newData.exists()",

          // the room attribute must be a valid key in room_names/ (the room must exist)
          // the object to write must have a name, message, and timestamp
          ".validate": "newData.hasChildren(['user', 'message', 'timestamp'])",

          // the message must be written by logged in user
          "user": {
             ".validate": "newData.val() === auth.uid"
          },

          // the message must be longer than 0 chars and less than 50
          "message": { ".validate": "newData.isString() && newData.val().length > 0 && newData.val().length < 50" },

          // messages cannot be added in the past or the future
          // clients should use firebase.database.ServerValue.TIMESTAMP
          // to ensure accurate timestamps
          "timestamp": { ".validate": "newData.val() <= now" },

          // no other fields can be included in a message
          "$other": { ".validate": false }
        }
      }
    }
  }
}

下一步