全新推出 Cloud Firestore(测试版):试用 Firebase 和 Google Cloud Platform 全新推出的规模可扩展且灵活的数据库。详细了解 Cloud Firestore

基于用户的安全性

本文档在回顾保护数据概念的基础上,加入了关于预定义 auth 变量的内容,旨在帮助您创建用于保护数据的全面解决方案。

集成身份验证

Firebase 身份验证已与 Firebase 实时数据库集成,使您可以按用户控制数据访问权限。

用户进行身份验证后,其信息将填充至“Firebase 数据库规则”规则中的 auth 变量。这些信息包括其唯一标识符 (uid) 以及关联的帐号数据,例如 Facebook ID 或电子邮件地址以及其他信息。如果您实现自定义身份验证提供方,则可以将自己的字段添加到用户的身份验证有效负载中。

本指南介绍如何将 Firebase 实时数据库规则语言与用户身份验证信息相结合。通过结合这两个概念,您可以根据用户身份控制对数据的访问权限。

auth 变量

在用户进行身份验证之前,规则中的预定义 auth 变量为空。用户通过 Firebase 身份验证进行身份验证之后,它将包含以下属性:

provider 所使用的身份验证方法(“password”、“anonymous”、“facebook”、“github”、“google”或“twitter”)。
uid 唯一用户 ID,保证在所有用户(无论来自哪个提供方)范围内是不重复的。
token 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 身份验证允许开发者在其服务器上创建自己的自定义身份验证令牌

开发者在创建自己的自定义身份验证令牌时,可视情况为这些令牌添加额外的声明。这些额外的声明将会出现在您的规则中的 auth.token 变量中。下面是使用 hasEmergencyTowel 自定义声明的规则示例:

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

Admin SDK 还允许您设置自定义令牌声明来帮助针对访问控制定义用户角色。请参阅使用自定义声明和安全规则控制访问权限

回顾聊天示例

我们以保护数据中的聊天示例为基础,添加一些用户身份验证,综合所有这些概念来构建一个有效的应用:

{
  "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 }
        }
      }
    }
  }
}

后续步骤

发送以下问题的反馈:

此网页
Firebase 实时数据库
需要帮助?请访问我们的支持页面