iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 31
0
Modern Web

Angular Firebase 入門與實做系列 第 31

[Angular Firebase 入門與實做] Day-30 Secure Data with Firestore 資料安全與驗證

每日一句來源:Daily English

Home is the place where, when you have to go there, they have to take you in. --無論何時何地,家永遠是向游子敞開大門的地方。

我們今天來說說要怎麼讓資料安全性上更加安全,並且加入身分的驗證,讓只有通過身分審核的成員可以讀取資料。

首先我們到我們firestore管理中心,點到規則的頁簽,這是我們目前的規則,筆者這裡講解一下

service cloud.firestore {
  match /databases/{database}/documents { //這裡指的是我們的整個firestore最大的document,我們不需要修改他
    match /{document=**} {  // 下面是就符合所有的document我們都給他可讀寫
      allow read, write;
    }
  }
}

修改規則

我們有兩種修該規則的方法

  1. 直接在平台上面改
  2. 用firebase tool的deploy功能 firebase deploy --only firestore:rules

筆者這邊直接使用平台,因為在檔案內,他是firestore.rules檔案,並沒有顏色,在觀看上不是很方便,筆者習慣在上面做修改,修改好在複製回專案。

所有方法都是由match開始,
然後根據allow的屬性給予權限

allow有幾種種類,以大分類來區分的話,分別有

  1. read 讀取
  2. write 寫入

read區分

  1. get 讀取單一資料
  2. list 讀取整個清單

write區分

  1. create 新增
  2. update 刪除
  3. delete 修改

接著我們看一個基本的權限防堵

接著我們測試看看防止沒有認證的使用者

service cloud.firestore {
  match /databases/{database}/documents {
  	match /messages/{message}{
    	allow read, write: if request.auth.uid != null;
    }
  }
}

上面這一段是否跟在寫functions有種熟悉的感覺呢?他們的邏輯有點像基本上也是透過{id}的方式找到list,而上面的意思是:messages這個collection底下的message只有當唯有認證的使用者才能通過。

Authentication

關於auth的屬性,大家可以看官方的API文件,文件中有列出有哪些可用的屬性
https://firebase.google.com/docs/firestore/reference/security/?authuser=1#properties

當然我們可以把read、write切得更細,如下

service cloud.firestore {
  match /databases/{database}/documents {
  	match /messages/{message}{    	
    	allow read: if true;
      allow write: if request.auth.uid != null;
    }
  }
}

如此就是只有登入的人可以寫,大家都能讀。

甚至你可以切得更細把寫的部分切開試試

service cloud.firestore {
  match /databases/{database}/documents {
    match /messages/{roomId}{
    	allow read: if true;
      
      allow create: if true;
      allow update: if request.auth.uid != null;
      allow delete: if request.auth.uid != null;
    }
  }
}

把使用者登出你會發現我們沒辦法修改、刪除了,只能新增,非常的酷!而且我們的程式內部完全不需做處理,只需在這裡加上就能防堵了。

這是一層的防賭,那如果是多層呢?一樣沒問題,我們可以一層一層的寫下來
我們直接使用users來實做

service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{uid} {
      allow read, write: if request.auth.uid == uid;
      
      match /{document=**} {
        allow read, write: if request.auth.uid == uid;
      }
    }
  }
}

其中{document=**}這個寫法代表的是底下所有的collection、subcollections都套用這個判斷。

上面的意思是要讀寫users的資料時,必續request.auth.uid跟我們的uid為相同的才能讀取,並且底下的所有subcollections也是一樣要是相同的uid才可以,我們可以階層式的寫下來,當然我們也能直接在外部指定到,向下面這樣。

service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{uid}/{document=**} {
      allow read, write: if request.auth.uid == uid;
    }
  }
}

但是要注意上面這樣的話,在上層就完全沒防堵了,大家都能讀取。

目標資料驗證

接著我們除了auth的防堵外我們還能針對內容作判斷,我們用剛剛的message來做示範,如下

service cloud.firestore {
  match /databases/{database}/documents {
    match /messages/{roomId}{
    	allow read: if true;
      
      allow create: if true;
      allow update: if resource.data.content == 'abc'; 
      allow delete: if request.auth.uid != null;
    }
  }
}

只有當原本的資料是'abc'字串的時候才能修改,另外要注意creat的時候是沒有resource的。

送出資料驗證

那如果是送過去的資料的判斷呢?也沒問題,如下

service cloud.firestore {
  match /databases/{database}/documents {
    match /messages/{roomId}{
    	allow read: if true;
      
      allow create: if request.resource.data.content == '123';
      allow update: if resource.data.content == 'abc';
      allow delete: if request.auth.uid != null;
    }
  }
}

沒錯使用request底下的resource就是送過來的資料啦~上面就是當為字串123的時候才能接受。

針對其他Document數值驗證

除此之外我們還能針對其他的document的資料來做判斷,
我們有兩種方法

  1. get 取得資料內容作判斷
  2. exists 判斷資料是否存在
    我們一樣透過messages來做展示
exists
service cloud.firestore {
  match /databases/{database}/documents {
    match /messages/{roomId}{
    	allow read: if true;
      
      allow create: if request.resource.data.content == '123';
      allow update: if resource.data.content == 'abc';
      allow delete: if request.auth.uid != null &&
                   exists(/databases/$(database)/documents/users/$(request.auth.uid));
    }
  }
}

上面的刪除就是判斷資料是否存在,並且我們可以用&&或是||串接,其中/databases/$(database)/documents這邊指的就是這個資料庫,跟最上面的match一樣,$()這跟我們在js裡面的``中的${}是一樣的邏輯,就是把這個文字放進來,所以上面的意思就是,當有認證,並且這個認證的人的資料存在於資料庫,那就可以刪除。

get
service cloud.firestore {
  match /databases/{database}/documents {
    match /messages/{roomId}{
    	allow read: if true;
      
      allow create: if request.resource.data.content == '123';
      allow update: if resource.data.content == 'abc';
      allow delete: if request.auth.uid != null &&
         get(/databases/$(database)/documents/users/$(request.auth.uid)).data.uid == request.auth.uid; 
    }
  }
}

上面指的就是取得這筆資料並且資料的內容符合條件,那就通過審核。

最後除了以上那些之外!我們甚至可以寫自己的判斷!

CUSTOM 條件判斷

我們可以自己寫function來把判斷包裝起來,寫起來有點像js,以下展示

service cloud.firestore {
  match /databases/{database}/documents {
    
    function isAuthWithData() {
      return request.auth.uid != null &&
         get(/databases/$(database)/documents/users/$(request.auth.uid)).data.uid == request.auth.uid;
    }

    match /messages/{roomId}{
    	allow read: if true;
      
      allow create: if request.resource.data.content == '123';
      allow update: if resource.data.content == 'abc';
      allow delete: if isAuthWithData(); 
    }
  }
}

記得順序很重要,他是依序執行的,如果function放在後面會找不到喔,我們直接把剛剛的判斷搬上去,下面只要呼叫function就可以了,如此一來我們能夠把重複的判斷搬出去獨立起來,非常的方便!

最後有些細節要注意一下,

如果你在你的資料內有寫道像是allow update: if resource.data.content == 'abc';這樣的內容,那當我們在query list的時候,如果你沒有下任何query條件,這個query是會被拒絕的,因為在驗證上不會通過,但是如果你在query的時候有下where("content", "==", "abc")的話就會成功,像是下面這樣

service cloud.firestore {
  match /databases/{database}/documents {
    match /posts/{post} {
      allow read: if resource.data.owner_id == request.auth.uid;
    }
  }
}

db.collection("posts").orderBy("timestamp"); → 這樣是會失敗的
db.collection("posts").where("owner_id", "==", myUserId).orderBy("timestamp"); → 這樣會成功

本日小節

透過firestore的寫入驗證規則我們可以把很多驗證的邏輯擺在資料庫,一來能減輕我們client的負擔,二來是當我們有跨平台的時候,我們不須寫很多多餘的程式碼,透過資料讀寫的驗證直接達到防賭的功能,且firestore在驗證的部分可說是相當完整,也很容易操作!有了他真的為我們省了很多事。

今天由身分驗證作為結束!也是筆者這系列鐵人賽的最後一篇,透過firebase、Angular實做了一個聊天室,也了解到一個看似簡單的聊天室,其實裡面存在很多複雜的小邏輯的!雖然文章到這裡結束了,但是接下來的實際運用才是真正的開始,歡迎大家可以一起討論,一起成長,筆者真的認為如果我們的後端能如firebase這樣的realtime特性,並且擁有很高的效能,真的對我們前端開發是一個很不一樣的體驗,整個開發的邏輯也有了些許的不同,如果你沒有用過firebase你一定要用用看,不管你是後端還是前端的開發人員。用過realtime的特性,真的會讓你在設計上會有很多不同的邏輯與構思,期待與您再相遇。

參考資料

https://firebase.google.com/docs/firestore/security/overview?authuser=1


上一篇
[Angular Firebase 入門與實做] Day-29 [實做] 使用者已讀狀態 02 HTTP身分驗證 authentication
系列文
Angular Firebase 入門與實做31

尚未有邦友留言

立即登入留言