今日來到 Firebase
介紹的最後一天,此篇講完,鐵人賽也剩最後一天了,那就加把勁最後衝刺吧。
先來回顧一下
前三篇我們學會了:
到這邊其實就可以實際用來開發,但是非常危險,
因為在沒有權限控管的情況下,你的資料就是大家的資料XD
所以我們要來學學怎麼處理這事!
本篇會分為兩個部分,第一部分會介紹 Firebase
使用者帳號建立與常見操作、第二部分會介紹資料讀寫規則常見的寫法。
本文開始
只要專案需要使用者登入,大概就得寫很多程式碼
後端得處理
如果非常巧地,您身兼多職,既是前端也是後端,那...這是個甜蜜的負擔啊...
還好 Firebase
有提供這方面的服務
只需要到主控台,然後點選 Authentication 就能看到。
「不需伺服器端程式碼,也能輕鬆驗證及管理不同供應商的使用者」
這句簡短的話充分說明了這個服務能替你解決什麼問題XD
點選「登入方式」即可看到所有能夠使用的登入方式
但今日篇幅有限,我們只會講如何使用電子郵件在 Firebasee
中實作登入。
Let's go!
首先我們需要一個註冊頁面,但是自己刻的可能不是很美觀,(咦...只有我嗎
那就找一個 login 的 template 來套用吧。
myLogin,這是使用 Bootstrap4 寫的,程式碼也乾淨又不失實用,喜歡的可以幫作者點個 Star。
不喜歡上面這個又不想自己刻的人,也可以考慮使用 Firebase Authentication
專門的 UI 庫
弄好之後記得引入 Firebase
的 CDN 還有專案的設定。
接下來的程式碼或有不清楚的可以直接查看官方文件
也有簡體版本
註冊的方法為:
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(() => {
...
})
.catch((error) => {
console.log(error.message);
});
很簡單,只需帶上 email
與 password
就好,然後可以使用 then
、catch
來處理後續流程。
但是在開始之前,需要先到控制台開通電子郵件登入的功能:
點選鉛筆圖示
啟用
儲存後就能夠看到電子郵件登入功能已經開通
既然是電子郵件,那就會有信箱驗證或是忘記密碼功能,這些 Firebase
也有提供,而且信件的內容也可以客製,有興趣的可以到「範本」的頁面玩玩看。
開通好後來到 register.html
,然後把 JS 打好
接著就可以試著註冊囉!
註冊成功後,就能到使用者頁面看到剛剛新增的帳號
也不一定每次都要用 JS 建立,管理員可以直接在網頁上新增
在註冊時,記得一定要有 catch
來處理錯誤,因為 Firebase
會檢查 Email
是否符合格式、或是密碼
長度是否足夠...等等。
像是這樣
又或者 Email
格式錯誤
有了註冊功能後,接著需要登入的功能:
firebase.auth().signInWithEmailAndPassword(email, password)
.then(() => {
...
})
.catch((error) => {
console.log(error.message);
});
但是怎麼知道有沒有登入成功呢?
可以使用這個方法來查看:
var user = firebase.auth().currentUser;
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
如果有登入狀態的話,會回傳一個包含該使用者資料的物件,沒有則回傳 null
。
那就直接來試試看吧
登入
登入成功後,印出 firebase.auth().currentUser
。
裡面很多東西可能也不知道是什麼,不過有一些像是 Email
、displayName
、Email 有沒有驗證
、uid
等等也可以在裡面看到。
但是 firebase.auth().currentUser
只會取得當前的登入狀態,也就是如果登出後,它也不會改變。
如果想要即時收到使用者當前的登入狀態,可以使用這個方法:
它會在登入狀態變動時執行 callback function,有點像是 Realtime database
的 on
功能。
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});
上一步驟中,我們可以使用 firebase.auth().currentUser
來查看使用者資料,當中比較有用的像是 displayName
或是 photoURL
,這些也可使日後更改。
實務上可以放到個人資料的頁面中,提供使用者修改自己的姓名等等,
使用的方法如下:
var user = firebase.auth().currentUser;
user.updateProfile({
displayName: "new_hbdoy",
photoURL: "https://hbdoy.tw/profile.jpg"
})
.then(() => {
...
})
.catch((error) => {
...
});
這邊只列舉部分,還有剩下幾個功能可以使用
有需要的可以直接查看官方文件。
想要使用
FB
的登入方法都大同小異。
接著進入最後的部分,也就是資料庫存取的限制,總不可能讓讀寫都一直是 true
。
Firebase
也會一直提醒你這是不安全的規則
主要使用這三個:
read
: 限制讀取的權限write
限制寫入的權限validate
: 資料驗證(ex: 要寫入的成績是不是在 0~100 之間)範例一樣舉昨天的成績
像是這樣就是禁止中文成績的讀寫:
{
"rules": {
"chinese": {
".read": false,
".write": false
}
}
}
但是要注意,父層的規則會覆蓋過子層,
這個意思就是,假使 Bob
的讀取權限為 true
,但是其父層 chinese
為 false
,所以還是沒有辦法讀取 Bob
的成績。
{
"rules": {
"chinese": {
".read": false,
"Bob": {
".read": true,
}
}
}
}
記得在設計權限規則的時候,要注意是否會有權限覆蓋的問題。
接下來測試讀寫規則時,不用辛苦地來回網頁與 JS,可以使用規則頁面的模擬工具
再來是可以使用的參數
auth
: 登入狀態EX: 我們可以把規則改成只有登入的人才可以對資料進行讀寫
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
root
: 資料庫頂層child
: 可以指定路徑exists()
: 指定路徑之資料是否存在EX: chinese
成績中,是否存在 Tony
這個人
root.child("chinese/Tony").exists()
newData
: 使用者 set
/push
/update
時帶上的資料。EX: 愈修改 Bob
成績,但是在寫入時先驗證 grade
欄位的值是否在 0 ~ 100
之間
{
"rules": {
"chinese": {
"Bob":{
".write": "newData.child('grade').val() >= 0
&& newData.child('grade').val() <= 100
&& newData.child('grade').isNumber()"
}
}
}
}
或是直接指定更深一層的路徑,這樣 newData
就會取得該路徑名稱的值,不用再加上 child('grade')
。
{
"rules": {
"chinese": {
"Bob":{
"grade":{
".write": "newData.val() >= 0
&& newData.val() <= 100
&& newData.isNumber()"
}
}
}
}
}
嘗試寫入小於 0 的數字,則會提示沒有權限寫入
其他還有 isString
、length
的方法。
data
: 用法和 newData
差不多,只是指向的資料為資料庫。EX: Bob
的國文成績
data.child('chinese/Bob/grade').val()
上面的範例中,我們限制了 Bob
的成績不能寫入 0~100
範圍外的數字,但是我們不確定每次寫入的人是誰,總不可能把資料庫所有的人名都寫上去,所以可以使用 $
來指定不確定的傳入值。
EX: 使用 $student
來接收使用者寫入的人名,就算我們不知道那個人名是誰,但是限制了它 grade
欄位的值必須在 0~100
之間。
{
"rules": {
"chinese":{
"$student": {
".write": "newData.child('grade').val() >= 0
&& newData.child('grade').val() <= 100
&& newData.child('grade').isNumber()"
}
}
}
}
綜合以上所學,我可以一開始註冊的時候,就順便建立一個 user
欄位來存放所有註冊的 Email
、uid
,然後設定他是否為 admin
。
接著限定只有登入
且為 admin
才能夠寫入資料
{
"rules": {
".read": "true",
".write": "auth != null
&& data.child('/user/'+ auth.uid + '/admin').val() == true"
}
}
至此 Firebase
篇也告一個段落,我們學到如何建立帳號系統,也可以限制資料的存取規則。
今日的分享就到這,我們明天見
那如果要限制chinese 裡的 entry 只有本人可以write,是要這樣寫嗎?
首先在chinese 裡的entry 都加上uid的column (類似RDBMS的foreign key)
chinese:{
Alice:{
uid:xxxx,
grade:76
}
Bob:{
uid:oooo,
grade:32
}
...
}
然後
{
"rules": {
"chinese":{
"$student": {
".write": "$student.uid=auth.uid"
}
}
}
}