jwt驗證的思考

erlanp發表於2020-10-20


jwt驗證對後端的壓力比用session小很多, 畢竟只用對稱加密或者hash計算,金鑰小可以都存在各臺web伺服器, 不需要請求redis或者mysql之類的資料庫產生io。

[登入踢出]
有時一些應用需要只讓一個賬號只能在一個瀏覽器或手機客戶端線上,一個手機登入,另一個手機的相同賬號需要登入踢出,jwt就比session麻煩一點,不想存資料庫需要通知客戶端自己刪除。
如果客戶端出問題沒有刪除,繼續請求,那麼應該怎麼解決。
簡單的解決辦法是session和jwt一起用(jwt的refresh token是怎麼實現的), jwt設定為5分鐘過期,每次請求不續,登入踢出後只有5分鐘可用。
還有一種直接在後臺管理踢出,一般這種的資料比較少,由每臺web伺服器存就好了,使用consul配置中心之類的通知進行更新。
或者再簡單一點jwt只支援get請求,一般情況下也是get請求居多,一般應該get請求只查詢,沒大的危害,再嚴格一點jwt對ip繫結,一個ip請求多的閘道器會被禁的。更嚴格一點可以隨機幾十次請求有一次用session驗證。

對於安全有較高要求的應用,jwt是輔助session減少和資料庫間的io請求的。
現在流行前後端分離,有時前端要從多個介面取資料再顯示到一個介面,進行多次認證的話,jwt效率高很多——多個介面取資料也可以合併,聽說阿里有相關的技術

管理後臺一般用的人不多,很多也沒有前後端分離,對安全的要求也高,只用session就可以了,沒有必要為使用新技術而用jwt。

[jwt金鑰洩漏]
比如每過一小時自動生成金鑰,但這時對session的請求會增加,相當於所有jwt的請求同時過期。
這個需要研究。
簡單解決方案,凌晨自動更新金鑰。

[remember]
php框架laravel remember_token的資訊存在關聯式資料庫的user表
這個是laravel實現的jwt記住我的功能的方式。用session也是一樣,存redis的過期了,再到資料庫裡取認證,實現冷熱資料的分離。
但有不方便的地方:
比如我在一個客戶端退出了,對這個remember_token怎麼辦?

方案1:
退出後我更新user表的remember_token, 再通知別的客戶端更新remember_token。
問題如下:
如果更新時沒有網,電腦或者瀏覽器是關的,就通知不到。
通知的形式一般是不加密的,所以不能直接告訴客戶端remember_token, 客戶端收到通知後再用https請求伺服器,這時客戶端的remember_token的又是過期的,只能看session過期沒。

方案2:
只刪除客戶端remember_token 的cookie, 自然別的客戶端還可以用remember_token。直接在後臺管理踢出的情況,把所有客戶端的remember_token失效問題也不大。

改善1:
所以要新增一個user_remember表
由使用者id、客戶端型別、token、過期時間 等組成。使用者id和客戶端型別為主鍵, 或者使用者id和token為主鍵。
相比直接用user表的情況可以解決上面的問題, 但要判斷主鍵是否存在,麻煩了一點。
改善2:
或者, user表裡存一個json, mysql和pgsql都支援,只是主流orm都不支援json,要手寫比較麻煩點。
json 以token為key時,還要對這個json加個專門的version進行樂觀鎖管理,加個最早的過期日期, 定時刪除過期的token。

結論:
方案2最簡單, 但非常不安全。
改善1合適於一個賬號可以在很多客戶端無限制登入
改善2合適於只讓一個賬號只能在一個瀏覽器或手機客戶端線上的情況。

[從opencart學到的]
opencart的session裡只存了使用者的id, 和使用者相關的資訊是通過這個id再從資料庫取得的。
這裡的好處很明顯,session是用於驗證許可權的,存別的資訊的話會增加許可權驗證時的io。
當前使用者的一些狀態資訊可以基於session_id存快取,不重要的存cookie的話問題也不大,如果加密存cookie的話io會比較大一點——經過加密和base64轉碼。
和使用者相關的資訊如果經常要取,也可以基於使用者id存快取裡,快取找不到再取資料庫。

相關文章