Firebase Cloud Storage 安全规则用于确定谁对 Cloud Storage 中存储的文件具有读写访问权限,以及文件的结构方式及其包含的元数据。云存储安全规则由考虑request
和resource
以允许或拒绝所需操作(例如上传文件或检索文件元数据)的规则组成。这些参考文档涵盖规则类型、 request
和resource
的属性、云存储安全规则使用的数据类型以及错误的发生方式。
规则
rule
是一个表达式,通过计算来确定是否允许request
执行所需的操作。
类型
允许
allow
规则由方法(例如read
或write
以及可选条件组成。执行规则时,会评估条件,如果条件评估为true
,则允许所需的方法;否则,该方法将被拒绝。无条件的allow
规则始终允许所需的方法。
// Always allow method allow <method>; // Allow method if condition is true allow <method>: if <condition>;
目前, allow
是唯一受支持的规则类型。
请求方法
读
read
方法涵盖了所有读取文件数据或元数据的请求,包括文件下载和文件元数据读取。
// Always allow reads allow read; // Allow reads if condition evaluates to true allow read: if <condition>;
写
write
方法涵盖了所有写入文件数据或元数据的请求,包括文件上传、文件删除、文件元数据更新。
// Always allow writes allow write; // Allow writes if condition evaluates to true allow write: if <condition>;
匹配
当用户request
(例如文件上传或下载)与规则所覆盖的文件路径匹配时,就会执行规则。 match
由路径和正文组成,其中必须至少包含一条allow
规则。如果没有匹配的路径,则拒绝该请求。
您可以match
完整命名的路径,也可以插入通配符来匹配符合特定模式的所有路径。
路径段
single_segment
您可以使用单个路径段创建与 Cloud Storage 中存储的文件匹配的规则。
// Allow read at "path" if condition evaluates to true match /path { allow read: if <condition>; }
还允许多个路径段和嵌套路径:
// Allow read at "path/to/object" if condition evaluates to true match /path { match /to { match /object { allow read: if <condition>; } } }
{single_segment_wildcard}
如果要将规则应用于同一路径下的多个文件,可以使用通配符路径段来匹配某个路径下的所有文件。通过将变量括在大括号中来在路径中声明通配符变量: {variable}
。该变量可以在 match 语句中作为string
访问。
// Allow read at any path "/*", if condition evaluates to true match /{single_path} { // Matches "path", "to", or "object" but not "path/to/object" allow read: if <condition>; }
多个路径段和嵌套路径也可能有通配符:
// Allow read at any path "/path/*/newPath/*", if condition evaluates to true match /path/{first_wildcard} { match /newPath/{second_wildcard} { // Matches "path/to/newPath/newObject" or "path/from/newPath/oldObject" allow read: if <condition>; } }
{multi_segment_wildcard=**}
如果要匹配路径上或路径下的任意数量的路径段,可以使用多段通配符,它将匹配该位置及其下的所有请求。这对于为用户提供自己的自由形式存储空间,或创建匹配许多不同路径段的规则(例如创建公共可读的文件集,或要求对所有写入进行身份验证)非常有用。
多段通配符路径的声明方式与单段通配符类似,只是在变量末尾添加=**
: {variable=**}
。多段通配符变量在 match 语句中可用作path
对象。
// Allow read at any path "/**", if condition evaluates to true match /{multi_path=**} { // Matches anything at or below this, from "path", "path/to", "path/to/object", ... allow read: if <condition>; }
要求
request
变量在条件中提供,以表示在该路径上发出的请求。 request
变量有许多属性,可用于决定是否允许传入请求。
特性
auth
当经过身份验证的用户对 Cloud Storage 执行请求时, auth
变量将填充用户的uid
( request.auth.uid
) 以及 Firebase 身份验证 JWT ( request.auth.token
) 的声明。
request.auth.token
包含以下部分或全部键:
场地 | 描述 |
---|---|
email | 与帐户关联的电子邮件地址(如果存在)。 |
email_verified | 如果用户已验证他们有权访问email 地址,则为true 。一些提供商会自动验证他们拥有的电子邮件地址。 |
phone_number | 与帐户关联的电话号码(如果存在)。 |
name | 用户的显示名称(如果已设置)。 |
sub | 用户的 Firebase UID。这在项目中是独一无二的。 |
firebase.identities | 与该用户帐户关联的所有身份的字典。字典的键可以是以下任意一个: email 、 phone 、 google.com 、 facebook.com 、 github.com 、 twitter.com 。字典的值是与帐户关联的每个身份提供者的唯一标识符的数组。例如, auth.token.firebase.identities["google.com"][0] 包含与该帐户关联的第一个 Google 用户 ID。 |
firebase.sign_in_provider | 用于获取此令牌的登录提供程序。可以是以下字符串之一: custom 、 password 、 phone 、 anonymous 、 google.com 、 facebook.com 、 github.com 、 twitter.com 。 |
firebase.tenant | 与帐户关联的tenantId(如果存在)。例如tenant2-m6tyz |
如果使用自定义身份验证, request.auth.token
还包含开发人员指定的任何自定义声明。
当未经身份验证的用户执行请求时, request.auth
为null
。
// Allow requests from authenticated users allow read, write: if request.auth != null;
path
path
变量包含正在执行request
的路径。
// Allow a request if the first path segment equals "images" allow read, write: if request.path[0] == 'images';
resource
resource
变量包含正在上传的文件的元数据或现有文件的更新元数据。这与resource
变量有关,该变量包含请求路径上的当前文件元数据,而不是新元数据。
// Allow a request if the new value is smaller than 5MB allow read, write: if request.resource.size < 5 * 1024 * 1024;
request.resource
包含resource
中的以下属性:
财产 |
---|
name |
bucket |
metadata |
size |
contentType |
time
time
变量包含一个时间戳,表示正在评估请求的当前服务器时间。您可以使用它来提供对文件的基于时间的访问,例如:仅允许在特定日期之前上传文件,或者仅允许在上传后一小时内读取文件。
// Allow a read if the file was created less than one hour ago allow read: if request.time < resource.timeCreated + duration.value(1, 'h');
资源
resource
变量包含 Cloud Storage 中文件的文件元数据,例如文件名、大小、创建时间和自定义元数据。
特性
name
包含文件全名的字符串,包括文件的路径。
// Allow reads if the resource name is "path/to/object" allow read: if resource.name == 'path/to/object'
bucket
包含存储此文件的Google Cloud Storage存储桶的字符串。
// Allow reads of all resources in your bucket allow read: if resource.bucket == '<your-cloud-storage-bucket>'
generation
包含文件的 Google Cloud Storage对象生成的int。用于对象版本控制。
// Allow reads if the resource matches a known object version allow read: if resource.generation == <known-generation>
metageneration
包含文件的 Google Cloud Storage对象元生成的int。用于对象版本控制。
// Allow reads if the resource matches a known object metadata version allow read: if resource.metageneration == <known-generation>
size
包含文件大小(以字节为单位)的 int。
// Allow reads if the resource is less than 10 MB allow read: if resource.size < 10 * 1024 * 1024;
timeCreated
表示文件创建时间的时间戳。
// Allow reads if the resource was created less than an hour ago allow read: if resource.timeCreated < request.time + duration.value(60, "m")
updated
表示文件上次更新时间的时间戳。
// Allow reads if the resource was updated less than an hour ago allow read: if resource.updated < request.time + duration.value(60, "m")
md5Hash
包含文件MD5 哈希值的字符串。
// Allow writes if the hash of the uploaded file is the same as the existing file allow write: if request.resource.md5Hash == resource.md5Hash;
crc32c
包含文件的crc32c 哈希值的字符串。
// Allow writes if the hash of the uploaded file is the same as the existing file allow write: if request.resource.crc32c == resource.crc32c;
etag
包含文件etag的字符串。
// Allow writes if the etag matches a known object etag allow write: if resource.etag == <known-generation>
contentDisposition
包含文件内容配置的字符串。
// Allow reads if the content disposition matches a certain value allow read: if resource.contentDisposition == 'inlined';
contentEncoding
包含文件内容编码的字符串。
// Allow reads if the content is encoded with gzip allow read: if resource.contentEncoding == 'gzip';
contentLanguage
包含文件内容语言的字符串。
// Allow reads if the content language is Japanese allow read: if resource.contentLanguage == 'ja';
contentType
包含文件内容类型的字符串。
// Allow reads if the content type is PNG. allow read: if resource.contentType == 'image/png';
metadata
包含开发人员提供的其他元数据字段的Map<String, String>
。
// Allow reads if a certain metadata field matches a desired value allow read: if resource.metadata.customProperty == 'customValue';
firestore.get 和 firestore.exists
firestore.get()
和firestore.exists()
函数允许您访问 Cloud Firestore 中的文档以评估复杂的授权标准。
firestore.get()
和firestore.exists()
函数都需要完全指定的文档路径。当使用变量构造firestore.get()
和firestore.exists()
的路径时,需要使用$(variable)
语法显式转义变量。
firestore.get
获取 Cloud Firestore 文档的内容。
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.uid)).data.memberships } } }
firestore.存在
检查 Cloud Firestore 文档是否存在。
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.uid)) } } }
服务
该service
是云存储安全规则文件中的第一个声明,并指定这些规则将应用于哪个服务。
姓名
name
将应用服务规则的名称。当前唯一的值是firebase.storage
。
// Specify the service name service firebase.storage { match /b/{bucket}/o { ... } }
数据类型
规则语言允许您使用is
运算符检查类型。
// For example
a is null
a is string
null
null
数据类型表示不存在的值。
allow read: if request.auth != null;
bool
bool
类型表示布尔true
或false
值。
allow read: if true; // always succeeds allow write: if false; // always fails
比较
可以使用==
运算符!=
来比较布尔值。
布尔运算
手术 | 表达 |
---|---|
AND | x && y |
OR | x || y |
NOT | !x |
操作短路,并且可以返回true
、 false
或Error 。
allow read: if true || false; // always succeeds, short circuits at true allow write: if false && true; // always fails, short circuits at false
int
和float
int
和float
类型表示数字。整数为: 0
、 1
、 -2
等,而浮点数为: 1.0
、 -2.0
、 3.33
等。
整数是带符号的 64 位值,浮点数是符合 IEEE 754 标准的 64 位值。当用于float
值的比较和算术运算时, int
类型的值将被强制转换为float
型。
比较
可以使用==
、 !=
、 >
、 <
、 >=
和<=
运算符对整数和浮点数进行比较和排序。
算术
整数和浮点数可以进行加、减、乘、除、取模和取反:
手术 | 表达 |
---|---|
添加 | x + y |
减法 | x - y |
乘法 | x * y |
分配 | x / y |
模数 | x % y |
否定 | -x |
数学函数
Firebase Cloud Storage 安全规则还提供了许多数学辅助函数来简化表达式:
功能 | 描述 |
---|---|
math.ceil(x) | 数值上限 |
math.floor(x) | 数值下限 |
math.round(x) | 将输入值舍入为最接近的 int |
math.abs(x) | 输入的绝对值 |
math.isInfinite(x) | 测试值是否为±∞ ,返回bool |
math.isNaN(x) | 测试该值是否不是数字NaN ,返回bool |
string
比较
可以使用==
、 !=
、 >
、 <
、 >=
和<=
运算符对字符串进行字典顺序比较和排序。
级联
可以使用+
运算符连接字符串。
// Concatenate a file name and extension 'file' + '.txt'
指数及范围
index
运算符string[]
返回一个字符串,其中包含字符串中提供的索引处的字符。
// Allow reads of files that begin with 'a' match /{fileName} { allow read: if fileName[0] == 'a'; }
range
运算符string[i:j]
返回一个字符串,其中包含指定索引之间的字符,从i
(包含)到j
(不包含)。如果未指定i
或j
,则它们分别默认为 0 和字符串的大小,但至少必须指定i
或j
才能使范围有效。
// Allow reads of files that begin with 'abcdef' match /{fileName} { allow read: if fileName[0:6] == 'abcdef'; }
如果提供的索引超出字符串范围, index
和range
运算符将产生错误。
size
返回字符串中的字符数。
// Allow files with names less than 10 characters match /{fileName} { allow write: if fileName.size() < 10; }
matches
执行正则表达式匹配,如果字符串与给定的正则表达式匹配,则返回true
。使用Google RE2 语法。
// Allow writes to files which end in ".txt" match /{fileName} { allow write: if fileName.matches('.*\\.txt') }
split
根据提供的正则表达式拆分字符串并返回字符串list
。使用Google RE2 语法。
// Allow files named "file.*" to be uploaded match /{fileName} { allow write: if fileName.split('.*\\..*')[0] == 'file' }
path
路径是类似目录的名称,具有可选的模式匹配。正斜杠/
的存在表示路径段的开始。
path
将string
参数转换为path
。
// Allow reads on a specific file path match /{allFiles=**} { allow read: if allFiles == path('/path/to/file'); }
timestamp
时间戳采用 UTC 格式,可能的值从 0001-01-01T00.00.00Z 开始,到 9999-12-31T23.59.59Z 结束。
比较
可以使用==
、 !=
、 >
、 <
、 >=
和<=
运算符对时间戳进行比较和排序。
算术
时间戳支持时间戳和时长之间的加法和减法,如下:
表达 | 结果 |
---|---|
timestamp + duration | timestamp |
duration + timestamp | timestamp |
timestamp - duration | timestamp |
timestamp - timestamp | duration |
duration + duration | duration |
duration - duration | duration |
date
仅包含year
、 month
和day
的timestamp
值。
// Allow reads on the same day that the resource was created. allow read: if request.time.date() == resource.timeCreated.date()
year
年份值是一个整数,从 1 到 9999。
// Allow reads on all requests made before 2017 allow read: if request.time.year() < 2017
month
月份值,为整数,从 1 到 12。
// Allow reads on all requests made during the month of January allow read: if request.time.month() == 1;
day
本月的当前日期,为整数,从 1 到 31。
// Allow reads on all requests made during the first day of each month allow read: if request.time.day() == 1;
time
包含当前时间的duration
值。
// Allow reads on all requests made before 12PM allow read: if request.time.time() < duration.time(12, 0, 0, 0);
hours
小时值是一个整数,从 0 到 23。
// Allow reads on all requests made before 12PM allow read: if request.time.hours() < 12;
minutes
分钟值是一个整数,从 0 到 59。
// Allow reads during even minutes of every hour allow read: if request.time.minutes() % 2 == 0;
seconds
秒值作为 int,从 0 到 59。
// Allow reads during the second half of each minute allow read: if request.time.seconds() > 29;
nanos
以纳秒为单位的整数秒小数部分。
// Allow reads during the first 0.1 seconds of each second allow read: if request.time.nanos() < 100000000;
dayOfWeek
一周中的某一天,从 1(星期一)到 7(星期日)。
// Allow reads on weekdays (Monday to Friday) allow read: if request.time.dayOfWeek() < 6;
dayOfYear
当前年份中的第几天,从 1 到 366。
// Allow reads every fourth day allow read: if request.time.dayOfYear() % 4 == 0;
toMillis
返回自 Unix 纪元以来的当前毫秒数。
// Allow reads if the request is made before a specified time allow read: if request.time.toMillis() < <milliseconds>;
duration
持续时间值表示为秒加上以纳秒为单位的小数秒。
比较
可以使用==
、 !=
、 >
、 <
、 >=
和<=
运算符对持续时间进行比较和排序。
算术
持续时间支持时间戳和持续时间之间的加法和减法,如下所示:
表达 | 结果 |
---|---|
timestamp + duration | timestamp |
duration + timestamp | timestamp |
timestamp - duration | timestamp |
timestamp - timestamp | duration |
duration + duration | duration |
duration - duration | duration |
seconds
当前持续时间的秒数。必须介于 -315,576,000,000 和 +315,576,000,000 之间(含)。
nanos
当前持续时间的小数秒数(以纳秒为单位)。必须介于 -999,999,999 和 +999,999,999 之间(含)。对于非零秒和非零纳秒,两者的符号必须一致。
duration.value
可以使用duration.value(int magnitude, string units)
函数创建持续时间,该函数根据给定的幅度和单位创建持续时间。
// All of these durations represent one hour: duration.value(1, "h") duration.value(60, "m") duration.value(3600, "s")
可能的unit
有:
期间 | unit |
---|---|
周数 | w |
天 | d |
小时 | h |
分钟 | m |
秒数 | s |
毫秒 | ms |
纳秒 | ns |
duration.time
可以使用duration.time(int hours, int minutes, int seconds, int nanoseconds)
函数创建持续时间,该函数创建给定小时、分钟、秒和纳秒的持续时间。
// Create a four hour, three minute, two second, one nanosecond duration duration.time(4, 3, 2, 1)
list
列表包含值的有序数组,其类型可以是: null
、 bool
、 int
、 float
、 string
、 path
、 list
、 map
、 timestamp
或duration
。
给定list
类型的x
和y
以及int
类型的i
和j
创建
要创建列表,请在括号之间添加值:
// Create a list of strings ['apples', 'grapes', 'bananas', 'cheese', 'goats']
比较
可以使用==
运算符!=
来比较列表。两个列表的相等要求所有值都相等。
指数及范围
index
运算符list[]
返回列表中提供的索引处的项目。
// Allow reads of all files that begin with 'a' match /{fileName} { allow read: if fileName[0] == 'a'; }
range
运算符list[i:j]
返回列表中指定索引之间的所有项目,从i
(包含)到j
(不包含)。如果未指定i
或j
,则它们分别默认为 0 和列表的大小,但至少必须指定i
或j
才能使范围有效。
// Allow reads of all files that begin with 'abcdef' match /{fileName} { allow read: if fileName[0:6] == 'abcdef'; }
in
如果列表中存在所需值,则返回true
;如果不存在,则返回false
。
// Allow read if a filename has the string 'txt' in it match /{fileName} { allow read: if 'txt' in fileName.split('\\.'); }
join
将字符串列表组合成单个字符串,并用给定字符串分隔。
// Allow reads if the joined array is 'file.txt' allow read: if ['file', 'txt'].join('.') == 'file.txt';
size
列表中的项目数。
// Allow read if there are three items in our list allow read: if ['foo', 'bar', 'baz'].size() == 3;
hasAll
如果列表中存在所有值,则返回true
。
// Allow read if one list has all items in the other list allow read: if ['file', 'txt'].hasAll(['file', 'txt']);
map
映射包含键/值对,其中键是字符串,值可以是以下任意值: null
、 bool
、 int
、 float
、 string
、 path
、 list
、 map
、 timestamp
或duration
。
创建
要创建映射,请在大括号之间添加键/值对:
// Create a map of strings to strings { 'mercury': 'mars', 'rain': 'cloud', 'cats': 'dogs', }
比较
可以使用==
运算符!=
来比较地图。两个映射相等要求两个映射中都存在所有键并且所有值都相等。
指数
映射中的值可以通过使用括号或点表示法来访问:
// Access custom metadata properties allow read: if resource.metadata.property == 'property' allow write: if resource.metadata['otherProperty'] == 'otherProperty'
如果密钥不存在,将返回error
。
in
如果映射中存在所需的键,则返回true
;如果不存在,则返回false
。
// Allow reads if a property is present in the custom metadata allow read: if property in resource.metadata;
size
地图中键的数量。
// Allow reads if there's exactly one custom metadata key allow read: if resource.metadata.size() == 1;
keys
地图中所有键的列表。
// Allow reads if the first metadata key is 'myKey' allow read: if resource.metadata.keys()[0] == 'myKey';
values
映射中所有值的列表(按键顺序排列)。
// Allow reads if the first metadata value is 'myValue' allow read: if resource.metadata.values()[0] == 'myValue';
错误
误差评估
遇到错误时,Firebase Cloud Storage 安全规则会继续评估。这很有用,因为条件&&
和||
如果条件分别短路为false
或true
,则表达式可能会吸收错误。例如:
表达 | 结果 |
---|---|
error && true | error |
error && false | false |
error || true | true |
error || false | error |
引发错误的常见位置有:除以零、访问列表或映射中不存在的值以及将错误类型的值传递给函数。
// Error if resource.size is zero allow read: if 1000000 / resource.size; // Error, key doesn't exist allow read: if resource.metadata.nonExistentKey == 'value'; // Error, no unit 'y' exists allow read: if request.time < resource.timeCreated + duration.value(1, 'y');