HTTP是一個「無狀態協議 Stateless Protocol」,也就是說,每次從客戶端(Client)對伺服器(Server)發出的請求都是獨立的,這一次的請求無法得知上一次請求的內容與資訊。
那我們要如何確認使用者是否先前已登入過,或是已通過驗證?
以下會介紹三種作法
- Cookie
- Cookie + Session
- JWT
Cookie
將登入狀態寫在 Cookie 中回傳給前端,每次前端要打API求資料,都帶上這個Cookie。
缺點
不安全,Cookie 可被修改
Cookie + Session
既然Cookie可被修改,那就把登入狀態放在DB(或記憶體中)吧。
首先,我們把使用者的每一段連線,例如進入網站到離開網站,稱為一個Session。DB中記錄每個Session的狀態 (例如登入者、登入狀態、自動登出時間) ,並且每筆Session資料都給一個 Session ID。
當使用者進入網站後,就會從後端得到一個帶有Session ID 的 Cookie,每次打API時,後端就可以透過Cookie中的Session ID 去 DB 查詢這個使用者的登入狀態。同理,當使用者登入時,後端也只是去改DB中這個Session的資料。
優點
後端回給前端的Cookie就只帶Session ID,因此不會有登入狀態被前端修改的疑慮
缺點
每次API都要到Session查詢這個使用者的狀態,增加消耗
JWT (JSON Web Token)
上面兩種作法不是不安全就是要透過額外查詢來檢查使用者的狀態,所以 JWT 誕生了,JWT可以同時解決上面兩種方法的缺點,既安全、又不用將狀態存在DB中,更符合HTTP的無狀態設計概念。
JWT 的流程簡單來說跟 cookie 很像,一樣是將登入狀態紀錄在token中並存放在前端,但是在資料上做了加密及驗證,可檢查token是否有被竄改過。
JWT 組成
JWT 是一組字串,透過小數點(.)切分成三個 Base64 編碼的部分:
- Header: Token 的種類及產生 signature 使用的雜湊演算法
- Payload:儲存的資訊(例如 user, isLogin)
- Signature:secret + Header + Payload,再hash後產生
把產出來的 token 丟到 JWT 解碼網站,可以發現Header和Payload兩個部分都是可以被輕鬆解碼回來的,並沒有加密,所以這兩部分都不適合放重要資訊。
而Signature是唯一有做加密的,同時也是檢查前兩塊部分是否有被竄改的重要依據。
token檢查
優點
-
可擴充性佳
在服務擴充的情況下,session需要做多機資料共享,而jwt不需要
-
無狀態
jwt 不在 Server 存任何狀態,更加符合 RESTful API 的設計原則
缺點
-
安全性
jwt 的 payload 只是使用 base64 編碼的,並沒有加密,因此不能儲存敏感資料 (Session存在DB就可以放比較多東西)
-
效能差
隨著需要儲存的資料越多,JWT佔用空間會越來越大,若超過4K就不能放在cookie中,要存在local storage
-
較難控制有效性
無法控制已發出的 token,即使你知道 token 被盜了,也沒辦法主動將其作廢。或是發現 Token 快過期時,也只能重新發一個新 Token 並拋棄原有的 Token。