Firebase 教學 - Firestore Rules 安全規則
Rules ( 安全規則 ) 可以讓 firestore 具有多一層的安全保護,firestore 的規則除了可以指定哪些資料可以讀取或寫入,也能更進一步的區分讀取與寫入的細部規則 ( 單一檔案、全部檔案、創建新檔案、更新檔案或刪除檔案...等 ),透過安全規則的設置,資料庫的使用也更靈活更安全。
延伸閱讀:
基本寫法
所有 Firestore 的安全規則都包含了match
和allow
的語法,match
負責識別哪些文件與資料夾,allow
則負責相對應的權限,又分為read
( 讀取 ) 和write
( 寫入 ) 兩種,read
可再細分為get
和list
,wrtie
也可再細分為create
、update
和 delete
,如果單純指定read
或wrtie
的權限,則細分的規則就會全部按照父層的定義進行,透過這些規則的交互搭配,就能提高資料庫的彈性與安全性。
類型 | 說明 |
---|---|
(read) get | 知道文件路徑就可讀取 |
(read) list | 讀取所有文件時就可被讀取 |
(write) create | 建立文件 |
(write) update | 更新文件 |
(wrtie) delete | 刪除文件 |
基本的寫法如下,最外層的兩行不需要更動,因它代表這整份資料庫的路徑,,內層透過match
篩選指定的路徑,並由 if 條件判斷 true 或 false 來決定讀取或寫入的權限。
service cloud.firestore { // 不需更動
match /databases/{database}/documents { // 不需更動
match /路徑 {
allow read, write: if 條件判斷;
}
}
}
當我們建立了一個 firestore 的資料庫,預設的規則會像下面這個樣子,內層的match
透過「萬用字元」指定資料庫下「所有的文件」都可以寫入和讀取,也就等同於只要知道資料庫路徑的所有人都可以操作這個資料庫。
service cloud.firestore { // 不需更動
match /databases/{database}/documents { // 不需更動
match /{document=**} {
allow read, write;
}
}
}
定義規則
以下規則將使用「Firebase 教學 - Firestore 安裝、寫入和讀取」裡的專案作為範例,專案結構如下圖為一個包含五種水果名字文件的 fruit 集合。
如果我們把規則寫成match /fruit/{document}
,因為只有單一個集合,那麼它的效果幾乎等同於match /{document=**}
,使用模擬工具測試可以看到,針對 fruit 裡的 orange 文件,可以允許寫入資料。
service cloud.firestore {
match /databases/{database}/documents {
match /fruit/{document} {
allow read, write;
}
}
}
如果 allow 後方不加入 if 條件判斷,預設就會 true,如果不寫 allow,預設則是 false,舉例來說,下面這兩段規則的結果相同,只能讀取資料庫,而不能寫入資料庫。
service cloud.firestore {
match /databases/{database}/documents {
match /fruit/{document} {
allow read;
}
}
}
service cloud.firestore {
match /databases/{database}/documents {
match /fruit/{document} {
allow read;
allow write: if false;
}
}
}
規則的分層與交集
撰寫規則不限有幾個match
,但若匹配到同一個文件,只要任何一個allow
的判斷回傳 true,就擁有權限可以允許讀取或寫入,以下方的規則來說,雖然我們定義了 /fruit/apple 不可以寫入,但因為 /fruit/{document} 回傳了 true,所以 apple 文件仍然保有寫入的權限。
service cloud.firestore {
match /databases/{database}/documents {
match /fruit/apple {
allow read;
allow write: if false;
}
match /fruit/{document} {
allow read, write;
}
}
}
延續上面所提到的「萬用字元」( 被{}
包起來的變數 ),規則裡的{document}
表示「該層」所有的文件,如果指令更深層的文件規則,不同層的規則無法互相覆蓋,舉例來說,下方的規則雖然把 /fruit/{document} 定義為可以寫入,但因更深層 /fruit/{document}/{col}/{doc} 的規則定義為不能寫入,因此就無法存入資料。
service cloud.firestore {
match /databases/{database}/documents {
match /fruit/{document} {
allow read, write;
}
match /fruit/{document}/{col}/{doc} {
allow read;
allow write: if false;
}
}
}
第一層 apple 可以寫入資料。
apple 的下一層則無法寫入資料。
不過如果我們把萬用字元改成{document=**}
,表示該層以下所有的文件,也因此後方的判斷就會無效,變成可以寫入資料了。
簡單實現資料的保護
最基本保護資料的方法有兩種,一種是不能修改已有的檔案,一種就是透過簡單的使用者認證來保護,首先看到第一種「不能修改已有的檔案」,在 firestore 裡要實作還滿簡單的,只要把 read 和 write 再細分規範,就能實現對應的保護,下面的規則會限制不能更新與刪除已存在的檔案,但仍然可以新建檔案。
service cloud.firestore {
match /databases/{database}/documents {
match /fruit/{document=**} {
allow read, create;
allow delete: if false;
allow update: if false;
}
}
}
至於下面這段規則,表示「如果知道該文件的路徑」就能讀取該文件,但若要讀取整份文件則會被規則拒絕。
service cloud.firestore {
match /databases/{database}/documents {
match /fruit/{document=**} {
allow get;
allow list: if false;
allow create;
allow delete: if false;
allow update: if false;
}
}
}
而我們也可以單純透過資料本身的屬性來定義,下方的規則定義了「如果資料有 visibility 為 public 才可以讀取」。
service cloud.firestore {
match /databases/{database}/documents {
match /fruit/{document=**} {
allow read: if resource.data.visibility == 'public';
}
}
}
規則也可以強制使用者登入後才能讀取或寫入,下方的規則使用了request.auth.uid
的方法,取得註冊用戶的 uid,如果沒有 uid 則禁止讀取或寫入。
註冊使用者的方法和 RealTime Database 一模一樣,只需把資料庫的部分換成 Firestore 即可,請參考:Firebase 教學 - 簡單的使用者註冊功能
service cloud.firestore {
match /databases/{database}/documents {
match /fruit/{document=**} {
allow read, write: if request.auth.uid != null;
}
}
}
當然我們也可以把規則寫得更完整,下方的規則可以指定使用者僅能讀取或寫入屬於自己的文件。
service cloud.firestore {
match /databases/{database}/documents {
match /fruit/{document=**} {
allow read, update, delete: if request.auth.uid == resource.data.author_id;
allow create: if request.auth.uid != null;
}
}
}
小結
整體來說,我覺得 Firestore 的規則比 RealTime Database 的規則還要有彈性,實作起來在理解上也差不多,本篇文章所介紹的規則應該可以滿足大多數的使用情境,不過如果有更複雜的流程,就得在規則上多加琢磨了。