本指南以学习 Firebase 安全规则语言指南的核心语法为基础,展示了如何向云存储的 Firebase 安全规则添加条件。
云存储安全规则的主要构建块是条件。条件是一个布尔表达式,用于确定是允许还是拒绝特定操作。对于基本规则,使用true
和false
文字作为条件非常有效。但是 Firebase 云存储安全规则语言为您提供了编写更复杂条件的方法,这些条件可以:
- 检查用户身份验证
- 验证传入数据
验证
Firebase 云存储安全规则与 Firebase 身份验证集成,为云存储提供强大的基于用户的身份验证。这允许基于 Firebase 身份验证令牌的声明进行精细的访问控制。
当经过身份验证的用户对 Cloud Storage 执行请求时, request.auth
变量会填充用户的uid
( request.auth.uid
) 以及 Firebase 身份验证 JWT ( request.auth.token
) 的声明。
此外,在使用自定义身份验证时,其他声明会出现在request.auth.token
字段中。
当未经身份验证的用户执行请求时, request.auth
变量为null
。
使用此数据,有几种常见的方法可以使用身份验证来保护文件:
- 公共:忽略
request.auth
- 经过身份验证的私有:检查
request.auth
是否不为null
- 用户私有:检查
request.auth.uid
是否等于路径uid
- Group private:检查自定义令牌的声明以匹配所选声明,或读取文件元数据以查看是否存在元数据字段
上市
任何不考虑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 身份验证负载之外, request
变量还包含执行请求的文件路径、收到请求的时间以及新的resource
值(如果该请求是一个写。
request
对象还包含用户的唯一 ID 和request.auth
对象中的 Firebase 身份验证有效负载,这将在文档的基于用户的安全部分中进一步解释。
request
对象中的完整属性列表如下:
财产 | 类型 | 描述 |
---|---|---|
auth | 地图<字符串,字符串> | 当用户登录时,提供用户的唯一 ID uid 和token ,即 Firebase 身份验证 JWT 声明的映射。否则,它将为null 。 |
params | 地图<字符串,字符串> | 包含请求的查询参数的映射。 |
path | 小路 | 表示正在执行请求的path 的路径。 |
resource | 地图<字符串,字符串> | 新的资源值,仅出现在write 请求中。 |
time | 时间戳 | 表示请求被评估的服务器时间的时间戳。 |
资源评估
在评估规则时,您可能还想评估正在上传、下载、修改或删除的文件的元数据。这使您可以创建复杂而强大的规则,例如只允许上传具有特定内容类型的文件,或者只允许删除大于特定大小的文件。
Cloud Storage 的 Firebase 安全规则在resource
对象中提供文件元数据,其中包含 Cloud Storage 对象中显示的元数据的键/值对。可以在read
或write
请求时检查这些属性以确保数据完整性。
在write
请求(例如上传、元数据更新和删除)时,除了包含请求路径中当前存在的文件的文件元数据的resource
对象之外,您还可以使用request.resource
对象,如果允许写入,它包含要写入的文件元数据的子集。您可以使用这两个值来确保数据完整性或实施应用程序约束,例如文件类型或大小。
下面提供了resource
对象中的完整属性列表:
财产 | 类型 | 描述 |
---|---|---|
name | 细绳 | 对象的全名 |
bucket | 细绳 | 此对象所在的存储桶的名称。 |
generation | 整数 | 此对象的Google Cloud Storage 对象生成。 |
metageneration | 整数 | 此对象的Google Cloud Storage 对象元生成。 |
size | 整数 | 对象的大小(以字节为单位)。 |
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)).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 功能的云存储安全规则后,系统会在 Firebase 控制台或 Firebase CLI 中提示您启用连接这两个产品的权限。
您可以通过删除 IAM 角色来禁用该功能,如管理和部署 Firebase 安全规则中所述。
验证数据
Cloud Storage 的 Firebase 安全规则也可用于数据验证,包括验证文件名和路径以及文件元数据属性,例如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 安全规则变得越来越复杂,您可能希望将条件集包装在可以在整个规则集中重复使用的函数中。安全规则支持自定义函数。自定义函数的语法有点像 JavaScript,但 Firebase 安全规则函数是用特定领域的语言编写的,具有一些重要的限制:
- 函数只能包含一个
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 安全规则中使用函数可以使它们更易于维护。
下一步
在讨论了条件之后,您对规则有了更深入的了解,并准备好:
了解如何处理核心用例,并了解开发、测试和部署规则的工作流程:
- 编写解决常见情况的规则。
- 通过回顾您必须发现和避免不安全规则的情况来积累您的知识。
- 使用云存储模拟器和专用安全规则测试库测试规则。
- 查看可用于部署规则的方法。