JWT 和 Session 皆為常見的認證(authentication)模式。值得注意的是,實作上來說,JWT、Session也可以達成授權(authorization),允許使用者獲取的特定資源。但這端看操作,授權也可以由伺服器端另外控制。
認證主要指的是身份認證。目的是讓使用者在一開始登入後,一定時間內不必重複認證身份,提升使用者體驗,同時簡化伺服端工作。有的像是進入遊樂園,暫時出園時只需在手臂上蓋個章,下次入園時便可快速通過,不必再拿出票卷或是身份證照,節省大家時間。當然此章也會有時間限制,過了特定時間後就作廢。網路的認證機制大抵如此,Session authentication 和 Token authentication 都是其中方法。
當使用者登入時,伺服器在確認身份後,生出一個Session來紀錄此使用者目前正登入,並傳送相應的Session id到前端,一般存在瀏覽器的cookies裡。也因為伺服端儲存某些資料,此方法被歸類為stateful.
隨後客戶端的請求都會夾帶上此Session id,而伺服器就會拿這個id來比對現存的Session,確認此Request背後的身份是否正確。
JWT (Json Web Token)是token authentication的一種,不過由於它是目前最常見,因此以討論他為主。
使用者登入時,伺服器會在確認身份後生出一個JWT,並直接回傳給使用者。和Session不一樣的是,JWT並不會存任何副本或是資料在伺服端,因此他也被稱為Stateless method。
JWT包含了三塊:Header、Payload、Verify Signature三個部份。
JWT 樣板:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
代表含義:
//Header: algorithm and datatype
{
"alg": "HS256",
"typ": "JWT"
}
//Payload: data
{
"sub": "1234567890", //使用者id
"name": "John Doe",
"iat": 1516239022 //簽發時間
//另外常見 'exp',表示到期時間
}
//Verify Signature:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
SECRET_KEY//secret key
)
其中SECRET KEY 由伺服端掌握。只要當使用者回傳時,用選定的演算法解密,比對SECRET KEY是否與伺服器的相同,即可認證身份。
此網站針對JWT執行細節有滿不錯的解釋
由於JWT完全儲存於前端,被盜走後風險較大。因此常見防範手法包含縮短JWT的有效時間,這樣盜走後能亂用的時間較短。
但為了維持使用者體驗,免除反覆登入之擾,這時會另存一個refresh token到前端,並且在JWT失效時傳此refresh token到後端請求一個新的JWT。
或許你已經發現了,但refresh token此時就需要一個對照的東西存於後端,不然他本身就只是另一個JWT,意義不大。因此這個方法也常被稱作Session + JWT,因為 Refresh token此時跟Session 的操作模式非常類似。
這裡指的是用戶端存在哪。
對Session 而言,大部分Session id 都會放在Session cookie裡。由於發送請求時,伺服器會將同網域的cookie內容一起送出,所以十分適合。不過cookie就會需要擔心csrf,這時候就要再加上Http cookie有諸多保護措施,例如httponly, samesite等等設置。勁量降低惡意攻擊的風險。
相較之下,許多人會將JWT裝在localStorage,發送請求時自己用JS抓來送。這樣的問題就是可能會受到XSS攻擊,導致JWT外流。尤其假如掛到含有惡意腳本的CDN時。不過就像前面提到的refreseh token,總有很多辦法可以解決。而存在localstorage好過cookie的理由之一在於,cookie的容量相較於localstorage小很多。另外,要對cookie內容進行操作也比localstorage麻煩上許多。
功能 | Session | JWT |
---|---|---|
伺服端儲存 | 需要有特定專區儲存Session資訊 | 無須另闢專區,只要有SECRET KEY即可 |
使認證失效 | 後端可以直接設定 | 很不方便 |
擴張 | Session必須存在一個共用的地方(例如redis),多個伺服器都要連到它。越是多伺服器,延遲的問題越可能發生 | 相對簡單,只要多個伺服器都有同個SECRET KEY 即可 |