現在婿屹,JSON Web Tokens (JWT) 是非常流行的灭美。尤其是 Web 開發(fā)領域。
- 流行
- 安全
- 穩(wěn)定
- 易用
- 支持 JSON
所有這些因素昂利,令 JWT 名聲大振届腐。
但是,今天我要來說說使用 JWT 的缺點蜂奸。也就是為什么說將 JWT 用于會話控制是多么的糟糕犁苏。
為什么使用 JWT?
如果你不了解 JWT扩所,不要緊張围详,它并不可怕。
JWT 只是用于網絡間傳遞聲明而執(zhí)行一種基于 JSON 的標準祖屏。
例如助赞,我是個盲人,而且聽力也不好袁勺。你上周幫我買了午餐雹食,現在我需要你的收款賬號,把錢還給你期丰。如果我詢問你的賬號群叶,但是其他人高呼他們的賬號,由于我把別人的賬號誤認為是你的钝荡,我可能會不小心把錢打給別人街立。
JWT 旨在防止這種情況發(fā)生。JWT 提供了一種簡單的方法化撕,在彼此傳遞數據時几晤,驗證是由誰先創(chuàng)建了數據。
所以植阴,像上述的例子蟹瘾,即使我收到了超過 100 萬個 JWT 返回的賬號信息圾浅,我也很容易可以辨別出來哪個真實來自于你。
JWT 如何運行憾朴?
JWT 是 JSON 格式的被加密了的字符串狸捕。
JWT 的核心是密鑰,就是 JSON 數據众雷。這是你關心的灸拍,并希望安全傳遞出去的數據。JWT 如何做到這一點砾省,并使你信任它鸡岗,就是加密簽名。
比如說编兄,我寫了一封信轩性,當我署名這封信時,意味著只要讀過這封信的人狠鸳,都知道是我寫了這封信揣苏。而且我的簽名是獨一無二的,所以不會被懷疑真實性件舵。加密簽名的方式大致相同卸察,JWT 有兩種加密方式:對稱加密和非對稱加密,兩種方式有同等的效用铅祸。
JWT 內容加密
其實坑质,JWT 的內容(內部的 JSON 數據)通常是不加密的。這意味著临梗,即使沒有密鑰洪乍,也可以查看 JWT 內的數據。JWT 默認并不會加密你的數據夜焦,它只是幫助你驗證是你信任的一方創(chuàng)建了它。
如果你確實需要加密 JWT 內容岂贩,可以使用 JWE 進行加密茫经,但這種做法并不常見。請確保使用了正確的方法萎津。
如今人們是如何使用 JWT 的卸伞?
JWT 最常見的用途是身份驗證。有大量的 Web 安全庫使用 JWT 創(chuàng)建會話控制锉屈,API 令牌等荤傲。
這種做法通常是,當需要對網站/ API 進行身份驗證時颈渊,服務器生成一個包含用戶 ID 的 JWT遂黍,以及其它一些關鍵性的信息终佛,然后再將其發(fā)送給瀏覽器/客戶端等,存儲為會話令牌雾家。
例如铃彰,當用戶訪問網站上的另一個頁面時,瀏覽器會自動將該 JWT 發(fā)送到服務器芯咧,服務器驗證 JWT 確認和最初創(chuàng)建的令牌相同牙捉,然后允許用戶執(zhí)行后續(xù)的操作。
從理論上看敬飒,還不錯邪铲,因為:
- 當服務器收到 JWT 時,可以驗證其是否是合法的无拗,是否是信任用戶的令牌
- 可以在服務器本地驗證带到,而不需要任何其它的網絡請求,與數據庫的通信等蓝纲。這可能令管理會話更高效阴孟,因為無需從數據庫(緩存)加載用戶信息,只需要在本地運行一小部分代碼税迷。這可能是人們喜歡用 JWT 的最大原因永丝。
似乎很棒,既可以提高 Web 應用的性能箭养,又可以減少緩存服務器和數據庫服務器的負載慕嚷,提供更好的體驗。另外你還可以在 JWT 中存儲用戶權限信息毕泌、用戶個人信息等等更多的額外信息進一步減少數據庫壓力喝检。
為什么 JWT 不是最好的會話令牌?
我們已經了解了 JWT 如何用于身份驗證撼泛,讓我們進入本篇的中心話題:為什么 JWT 不是最好的會話令牌挠说,為什么普通的舊會話方式在幾乎各方面都優(yōu)于 JWT。
背景
我們先了解一些背景知識愿题。開發(fā)人員構建的大多數網站都相對比較簡單:
- 用戶注冊
- 用戶登錄
- 用戶點擊執(zhí)行操作
- 網站使用用戶信息進行創(chuàng)建损俭、刪除、更新潘酗、查閱信息
對于這類網站杆兵,要知道用戶進行交互的每個頁面都會包含某些動態(tài)數據。比如:你正在訪問一個需要用戶登錄才能進一步操作的網站仔夺,你經常會在數據庫中對用戶進行這些操作:
- 記錄用戶正在執(zhí)行的操作
- 將用戶的某些信息添加到數據庫中
- 檢查用戶的權限看其是否可以執(zhí)行某項操作
- 等等
數據
我們來看兩種方案:
- 在 Cookie 中存儲用戶 ID(abc123)
- 在 JWT 中存儲用戶 ID(abc123)
如果我們將 ID 存儲在 Cookie 中琐脏,需要 6 個字節(jié)。如果將 ID 存儲在 JWT 中(設置基本的請求頭字段,以及一些其它信息)日裙,需要幾百字節(jié)甚至更多吹艇。對于簡單的會話控制,每個頁面的請求就增大了幾十倍阅签。
假如你的網站每月有 10 萬次的瀏覽器掐暮,就意味著要多開銷幾十兆的流量。聽起來并不多政钟,但日積月累也是不小一筆開銷路克。實際上,許多人會在 JWT 中存儲的信息會更多养交。
無論如何你需要操作數據庫
如上所述精算,大多數需要用戶登錄的網站主要是 CRUD 操作(增查改刪)生成動態(tài)內容。
在網站上使用 JWT碎连,對于用戶加載的幾乎所有頁面灰羽,都需要從緩存/數據庫中加載用戶信息,可能出現下列情況:
- 需要用戶關鍵性信息查詢(例如:判斷用戶賬號是否有足夠的資金完成交易鱼辙?)
- 需要將一些信息保存進數據庫(例如:用戶相關的唯一信息廉嚼,需要根據該信息對用戶進行檢索)
- 必須從緩存/數據庫中查詢完整的信息,方便網站生成完整的動態(tài)頁面內容倒戏。
想想你的網站是否會遇到上述情形怠噪。這意味著大多數網站不適用 JWT 的無狀態(tài)特性。為了解決這個問題杜跷,就需要 JWT 變得更大傍念,而且需要使用 CPU 來計算簽名,就會導致比傳統(tǒng)會話慢許多葛闷。
其實憋槐,幾乎每個 Web 框架支持在每次請求傳入用戶信息,這包括 Django淑趾,Rails阳仔,Express.js 等(如果有用到身份驗證功能)。另外扣泊,如果你使用 Memcached/Redis 等緩存服務器對用戶信息進行緩存驳概,檢索會變得非常快旷赖。
多余的簽名
JWT 的賣點之一就是加密簽名,由于這個特性更卒,接收方得以驗證 JWT 是否有效且被信任等孵。
但是,其實在過去 20 年中幾乎每一個框架對于普通會話 Cookie 都可以獲得很好的加密簽名處理蹂空。這意味著你可以獲得與 JWT 完全一致的效果俯萌,況且大多數 Web 身份認證應用中果录,JWT 都會被存儲到 Cookie 中,這就是說你有了兩個層面的簽名咐熙。
聽著似乎很贊弱恒,但是沒有任何優(yōu)勢,為此棋恼,你需要花費兩倍的 CPU 開銷來驗證簽名返弹。對于有著嚴格性能要求的 Web 應用,這并不理想爪飘,尤其對于單線程環(huán)境义起。
更好的解決方案是什么?
如果你正在構建上述類型的網站师崎,那么最好選擇舊的默终,簡單且安全的服務器端會話。而不是將用戶 ID 存儲到 JWT 中犁罩,然后再將 JWT 存儲到 Cooike 中齐蔽。只需將用戶 ID 直接存儲到 Cookie 中即可。
如果你的網站很受歡迎床估,有著大的訪問量含滴,可以將會話緩存到 Memcached/Redis,同時也有利于擴展你的服務顷窒。
什么時候使用 JWT蛙吏?
JWT 雖然對于大多數網站都沒有用逼裆,但是有幾種情況它是很有用的捉偏。
如果你正在構建從服務器到服務器或客戶端到服務器(如:移動應用 APP 或單頁面應用)的 API 服務,那么使用 JWT 是非常明智的贝或。比如:
- 你的客戶端需要通過 API 進行身份驗證谓着,并返回 JWT
- 然后泼诱,客戶端使用返回的 JWT 經過身份驗證去請求其它的 API 服務
- 這些其它 API 服務通過客戶端的 JWT 驗證客戶端是可信的,并且可以執(zhí)行某些操作無需再次驗證
對于這類 API 服務赊锚,JWT 非常適合治筒,因為客戶端需要頻繁進行請求,并且權限是可控的舷蒲,通常認證數據以無狀態(tài)方式持久存在耸袜,不需要過多依賴用戶信息。
如果你正在構建的應用類似單點登錄或 OpenID Connect 認證牲平,JWT 同樣十分適合堤框,就是實現一種通過第三方驗證用戶的方法。
總結
當你準備構建下一個網站時,只需要使用 Web 框架默認的身份認證功能即可蜈抓,不需要再集成 JWT 方式启绰。
關注公眾號「展白說」,獲取更多有價值的信息沟使。