本指南以学习 Firebase Security Rules 语言的核心语法指南为基础,介绍如何在面向 Cloud Storage 的 Firebase Security Rules 中添加条件。
Cloud Storage Security Rules 的主要构成要素是条件。条件是一个布尔表达式,用于确定应该允许还是拒绝执行特定操作。对于基本规则,最好使用 true 和 false 字面量作为条件。不过,“面向 Cloud Storage 的 Firebase Security Rules”语言为您提供了编写更复杂条件的方法,这些条件可以:
- 检查用户身份验证
- 验证传入数据
身份验证
面向 Cloud Storage 的 Firebase Security Rules 与 Firebase Authentication 集成,可为 Cloud Storage 提供基于用户的强大身份验证功能。这样就可以根据 Firebase Authentication 令牌的声明进行精确的访问权限控制。
当经过身份验证的用户对 Cloud Storage 发出请求时,系统将使用该用户的 uid (request.auth.uid) 以及 Firebase Authentication JWT (request.auth.token) 的声明来填充 request.auth 变量。
另外,当使用自定义身份验证时,request.auth.token 字段中会显示其他声明。
当未经身份验证的用户发出请求时,request.auth 变量为 null。
使用这些数据时,可以通过几种常用方法来利用身份验证保护文件:
- 公开:忽略
request.auth - 仅对经过身份验证的用户公开:确定
request.auth不为null - 用户私有:检查
request.auth.uid是否等于路径uid - 群组私有:检查自定义令牌的声明是否与所选的声明相匹配,或读取文件元数据以确定某个元数据字段是否存在
公开
任何不考虑 request.auth 上下文的规则均可被视为 public 规则,因为它不考虑用户的身份验证上下文。这些规则适合用于呈现公开数据(如游戏资源、声音文件或其他静态内容)的场景。
// Anyone to read a public image if the file is less than 100kB // Anyone can upload a public file ending in '.txt' match /public/{imageId} { allow read: if resource.size < 100 * 1024; allow write: if imageId.matches(".*\\.txt"); }
仅对经过身份验证的用户公开
在某些情况下,您可能希望允许所有经过身份验证的应用用户查看数据,但不允许未经身份验证的用户查看。由于所有未经身份验证的用户的 request.auth 变量均为 null,因此如果要求必须通过身份验证,您只需检查 request.auth 变量是否存在即可:
// Require authentication on all internal image reads match /internal/{imageId} { allow read: if request.auth != null; }
用户私有
到目前为止,request.auth 最常见的使用场景是为个人用户提供对其文件的精细权限,例如上传个人资料照片和读取私人文件等。
因为 Cloud Storage 中的文件具有完整的文件“路径”,所以若要让用户拥有某个文件的控制权限,只需要在文件名前缀中包含一个唯一的用户识别信息(例如用户的 uid),在对规则求值时可以检查此信息:
// Only a user can upload their profile picture, but anyone can view it match /users/{userId}/profilePicture.png { allow read; allow write: if request.auth.uid == userId; }
群组私有
另一个同样常见的使用场景是为群组提供对象权限,例如允许多名团队成员协同撰写或修改某个共享的文档。下列几种方法可以实现这个目的:
将这些数据存储在令牌或文件元数据中后,就可以在规则中引用这些数据:
// Allow reads if the group ID in your token matches the file metadata's `owner` property // Allow writes if the group ID is in the user's custom token match /files/{groupId}/{fileName} { allow read: if resource.metadata.owner == request.auth.token.groupId; allow write: if request.auth.token.groupId == groupId; }
请求求值
上传、下载、元数据更改和删除操作是使用发送到 Cloud Storage 的 request 进行求值的。除了如上所述的 request.auth 对象中的用户唯一 ID 和 Firebase Authentication 载荷外,request 变量还包含正在执行请求的文件路径、请求接收时间以及新的 resource 值(如果请求为写入操作)。
request 对象还包含用户的唯一 ID 和 request.auth 对象中的 Firebase Authentication 载荷,我们将在相关文档的基于用户的安全性部分进一步阐述这一点。
request 对象中属性的完整列表如下:
| 属性 | 类型 | 说明 |
|---|---|---|
auth |
映射<字符串, 字符串> | 如果用户已登录,此属性将提供 uid(用户的唯一 ID)和 token(Firebase Authentication JWT 声明的映射)。如果用户未登录,则其值为 null。 |
params |
映射<字符串, 字符串> | 包含请求的查询参数的映射。 |
path |
路径 | path 表示正在执行请求的路径。 |
resource |
映射<字符串, 字符串> | 新资源值,仅当请求为 write 操作时存在。 |
time |
时间戳 | 时间戳,表示对请求进行评估时的服务器时间。 |
资源求值
对规则求值时,您可能还需要对正在上传、下载、修改或删除的文件的元数据求值。这样一来,您就可以创建功能强大的复杂规则来执行任务,例如仅允许上传包含特定内容类型的文件,或仅允许删除超过特定大小的文件。
面向 Cloud Storage 的 Firebase Security Rules 在 resource 对象中提供文件元数据,其中包含 Cloud Storage 对象中提供的元数据键值对。可以在 read 或 write 请求中检查这些属性,以确保数据完整性。
在 write 请求(如上传、元数据更新和删除请求)中,除了 resource 对象(其中包含请求路径下现有文件的文件元数据)外,您还可以使用 request.resource 对象,其中包含在允许写入时要写入的一部分文件元数据。您可以使用这两个值来确保数据完整性,或强制实施应用限制(如文件类型或大小)。
resource 对象中属性的完整列表如下:
| 属性 | 类型 | 说明 |
|---|---|---|
name |
字符串 | 此对象的完整名称 |
bucket |
字符串 | 此对象所在存储桶的名称。 |
generation |
int | 此对象的 Google Cloud Storage 对象生成。 |
metageneration |
int | 此对象的 Google Cloud Storage 对象元生成。 |
size |
int | 此对象的大小(以字节为单位)。 |
timeCreated |
时间戳 | 表示此对象的创建时间的时间戳。 |
updated |
时间戳 | 表示此对象的最后更新时间的时间戳。 |
md5Hash |
字符串 | 此对象的 MD5 哈希。 |
crc32c |
字符串 | 此对象的 crc32c 哈希。 |
etag |
字符串 | 与此对象相关联的 etag。 |
contentDisposition |
字符串 | 与此对象相关联的内容处置。 |
contentEncoding |
字符串 | 与此对象相关联的内容编码。 |
contentLanguage |
字符串 | 与此对象相关联的内容语言。 |
contentType |
字符串 | 与此对象相关联的内容类型。 |
metadata |
映射<字符串, 字符串> | 开发者指定的其他自定义元数据的键值对。 |
request.resource 包含上述除 generation、metageneration、etag、timeCreated 和 updated 外的所有属性。
使用 Cloud Firestore 增强安全性
您可以使用 Cloud Firestore 中的文档来对更多授权条件进行评估。
利用 firestore.get() 和 firestore.exists() 函数,您的安全规则可以针对 Cloud Firestore 中的文档评估传入请求。firestore.get() 和 firestore.exists() 函数都需要指定完整的文档路径。使用变量为 firestore.get() 和 firestore.exists() 构建路径时,您需要使用 $(variable) 语法对变量进行明确转义。
下面的示例展示了一条规则,规定只有特定俱乐部的成员才能读取文件。
service firebase.storage {
match /b/{bucket}/o {
match /users/{club}/files/{fileId} {
allow read: if club in
firestore.get(/databases/(default)/documents/users/$(request.auth.id)).data.memberships
}
}
}service firebase.storage {
match /b/{bucket}/o {
match /users/{userId}/photos/{fileId} {
allow read: if
firestore.exists(/databases/(default)/documents/users/$(userId)/friends/$(request.auth.id))
}
}
}在您创建并保存首个使用这些 Cloud Firestore 函数的 Cloud Storage Security Rules 后,系统会通过 Firebase 控制台或 Firebase CLI 提示您启用相应权限以将这两款产品关联起来。
您可以通过移除相应 IAM 角色来停用该功能,如管理和部署 Firebase Security Rules 中所述。
验证数据
面向 Cloud Storage 的 Firebase Security Rules 也可用于数据验证,包括验证文件名和路径以及文件元数据属性(例如 contentType 和 size)。
service firebase.storage { match /b/{bucket}/o { match /images/{imageId} { // Only allow uploads of any image file that's less than 5MB allow write: if request.resource.size < 5 * 1024 * 1024 && request.resource.contentType.matches('image/.*'); } } }
自定义函数
随着您的 Firebase Security Rules 变得越来越复杂,您可能需要将条件集封装在函数中,以便在规则集中重复使用。安全规则支持自定义函数。自定义函数的语法有点类似于 JavaScript,但 Firebase Security Rules 函数是用网域特定语言编写的,该语言具有以下一些重要限制:
- 函数只能包含一个
return语句,不能包含任何额外的逻辑。例如,它们无法执行循环或调用外部服务。 - 函数可以自动访问其所在范围内的函数和变量。例如,在
service firebase.storage范围内定义的函数可以访问resource变量以及(仅限于 Cloud Firestore)get()和exists()等内置函数。 - 函数可以调用其他函数,但不可以递归。调用堆栈总深度不得超过 10。
- 在版本
rules2中,函数可以使用let关键字定义变量。函数可包含任意数量的 let 绑定,但必须以 return 语句结尾。
函数是用 function 关键字定义的,可以接受零个或零个以上的参数。例如,您可能想要将上述示例中使用的两种条件组合成一个函数:
service firebase.storage {
match /b/{bucket}/o {
// True if the user is signed in or the requested data is 'public'
function signedInOrPublic() {
return request.auth.uid != null || resource.data.visibility == 'public';
}
match /images/{imageId} {
allow read, write: if signedInOrPublic();
}
match /mp3s/{mp3Ids} {
allow read: if signedInOrPublic();
}
}
}
在 Firebase Security Rules 中使用函数,可以在规则变得越来越复杂时使其更易于维护。
后续步骤
在讨论条件之后,您将对规则有更深入的了解,而且可以:
了解如何处理核心使用场景以及了解开发、测试和部署规则的工作流程:
- 编写用于解决常见场景的规则。
- 如需进一步熟悉,请查看必须发现并避免不安全规则的情况。
- 使用 Cloud Storage 模拟器和专用安全规则测试库测试规则。
- 查看可用于部署 Rules 的方法。