20191124 JWT(Json Web Token)瞭解學習

問題——對接方使用的認證機制是什麼

雖然工作中接觸到的新東西不多,可是想學還是很多途徑的。

之前對接接口的時候,他們的登錄接口返回的參數之一是token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJVc2VyMjc3Iiwic2NvcGUiOlsicmVhZCJdLCJleHAiOjE1NzMxNDIwODYsInVzZXIiOiJVc2VyMjc3IiwiYXV0aG9yaXRpZXMiOlsibWluaWFwcHVzZXIiXSwianRpIjoiMTViNWQzMmEtMTJiZS00ZDZjLWEyNzktOTUwNTJmYzA2ZmFiIiwiY2xpZW50X2lkIjoieW55dGFwcCJ9.H6CxjZkzqtvJPn5_YKndmREPIc-R86Q54GlFGptAE-Q。後續調的每個接口都需要在header裏傳這個token。當時大概知道這是個用戶認證,但到底是個什麼技術,也是不清楚,因爲覺得接觸不到對方的代碼,不知道對方用什麼技術也正常。
不過當時仔細觀察了有注意到這個token有一點點特別,它是有三段字符串組成的,並且三段字符串之間用字符“ . ”來隔開。

雖然很好奇到底對方的這個認證機制是怎麼實現的,但是還是沒有去追問。

今天在學習的時候無意中看到了JWT,然後看到了JWT數據的格式,又想到之前對接的登錄接口返回的token的數據格式。恍然大悟,原來對方使用的用戶認證機制是JWT!

用戶認證機制之session會話

在瞭解JWT之前,先想想還有哪些常見的用戶認證機制。

傳統的WEB開發的用戶認證是通過在服務端存儲session(session默認的有效期是30min,可以修改),之後瀏覽器通過在cookie中帶上session_id,如此來維持一次會話。使用Java語言開發,也可以通過session來維持會話。但是會有這麼些問題:

  1. 對於WEB網頁系統,當重新部署服務時,session會失效,那麼所有用戶都要重新登錄。
  2. 對於支持網頁端和移動端的系統,移動端並沒有cookie這樣的東西,當後臺創建session時,移動端並不能直接通過cookie拿到session_id。
  3. 當系統要集羣部署時,用戶的登錄請求和後續請求如果不是對應同一個服務器,就會出現用戶認證失敗的問題。因此集羣系統的session並不共享。

對於第1點和第3點,可以通過使用redis代替session來存儲會話信息,這樣即使重新部署系統,會話信息也不會失效。對於集羣部署的系統,只要訪問的是同一個redis服務器也就不會有問題了。redis操作是內存操作,效率很高。
對於第2點,可以通過接口直接返回sessionId,以及後續接口回傳sessionId來解決。

也就是說,後端可以通過redis來存儲會話信息,在登錄接口返回sessionId並在後續接口回傳,如此實現用戶認證。這種需要在服務端存儲客戶端信息的認證機制,叫做有狀態認證。

用戶認證機制之JWT

而開頭提到的JWT,則是無狀態的用戶認證。無狀態也就是指服務端不存儲會話信息。相比有狀態認證JWT是一種時間換空間的做法。

JWT,即Json Web Token,即Json格式的令牌。

JWT的原理就是服務端認證以後,生成一個Json對象,返回給用戶。用戶與服務器通信時,都要發送這個Json對象。即服務器通過這個令牌來確定用戶身份。

而爲了防止用戶篡改數據,服務端在生成這個Json對象的時候,會加上簽名。

而服務端實際返回給用戶的,並不是Json字符串,而是一段指定格式的字符串:Base64編碼的header + "." + base64編碼的json +"." + 簽名

即JWT分爲三部分:JWT頭+有效荷載+簽名。其中JWT頭和有效荷載就是Base64編碼過的。並且這三部分通過字符“.”連接。

回到最開始的那個JWT字符串,之前是不知道它是一個什麼東西,現在總算知道的:

`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJVc2VyMjc3Iiwic2NvcGUiOlsicmVhZCJdLCJleHAiOjE1NzMxNDIwODYsInVzZXIiOiJVc2VyMjc3IiwiYXV0aG9yaXRpZXMiOlsibWluaWFwcHVzZXIiXSwianRpIjoiMTViNWQzMmEtMTJiZS00ZDZjLWEyNzktOTUwNTJmYzA2ZmFiIiwiY2xpZW50X2lkIjoieW55dGFwcCJ9.H6CxjZkzqtvJPn5_YKndmREPIc-R86Q54GlFGptAE-Q`

對於這個token,第三部分是簽名,簽名是對前面兩部分使用簽名算法生成的一個字符串就夠了,其中使用的簽名算法在JWT頭中。

而第一部分是base64處理過的JWT頭,第二部分是base64處理過的有效荷載,那麼我們可以對它進行一個解析,還原它原本的信息,通過base64的decode方法解析後:

JWT頭:{"alg":"HS256","typ":"JWT"}
有效荷載:{"user_name":"User277","scope":["read"],"exp":1573142086,"user":"User277","authorities":["miniappuser"],"jti":"15b5d32a-12be-4d6c-a279-95052fc06fab","client_id":"ynytapp"}

可以看到,效荷載中的exp就是有效期,服務端在認證通過後還會對有效期進行判斷,如果過期,也無法繼續處理。

JWT的詳細介紹可以參考這幾篇文章:

1、10分鐘瞭解JSON Web令牌(JWT)
2、阮一峯的JSON Web Token 入門教程

JWT的不足之處

前面說了,使用JWT,服務端是不會保存會話信息的,因此需要客戶端每次調用接口都帶上該參數。原理就是服務端對簽名進行驗證,驗證通過說明該token是本服務端下發的,然後就可以從有效荷載中提取相關用戶的信息。

JWT的這種無狀態機制使得系統有很好的擴展性,當系統從單機版改成集羣版,這個JWT用戶認證機制也不需要有任何變動。JWT的無狀態機制也給服務器端節省了存儲空間。

但是JWT這種用戶認證機制也有它的不足的地方:

第一個問題是每個接口都要傳這個長度不算短的令牌,相比傳統WEB的session認證機制只需要傳一個sessionId開銷大很多,並且服務端對每個請求就要進行一次驗籤操作。

第二個問題是隻要令牌不過期就還能使用,也就是說即使用戶改了密碼甚至服務端發現某個請求是惡意請求也無能爲力。因爲JWT機制是決定了服務端只能通過驗籤方式判斷該令牌是否自己下發的,只要是自己下發的都是合法的。一個解決方法是服務端額外存儲一些信息,如記錄token是否有效,但是這樣一來,就打破了JWT的無狀態機制了。對於JWT的令牌,通常會把令牌的有效期設置得比較短,如30分鐘,這樣會更加安全。

第三個問題是無法避免用戶多地登錄,比如用戶在網頁端登錄後進行操作,這時他再從移動端進行登錄也不會對網頁端造成影響。同樣的是因爲JWT是無狀態的用戶認證機制。或者說,JWT這種用戶認證機制,本身就是用於允許用戶多地登錄,所以這不能算是使用JWT的問題。

第四個問題是返回的令牌中的有效荷載只是做了Base64編碼處理,因此不宜存放用戶敏感信息,否則不安全。不過可以結合HTTPS使用,把令牌存放到HTTP header裏,使信息可以加密傳輸。

最後

相比之下,感覺使用redis來存儲會話信息會更加方便、安全以及可控。接口傳輸的數據更小,可以限制多地登錄,還可以讓指定的用戶登錄狀態失效,甚至可以讓全部用戶的登錄狀態失效,使之重新登錄驗證。但是這種有狀態的認證機制也決定了它在集羣時有數據一致性的問題。而JWT的無狀態機制就因爲無狀態,因此集羣系統中也不會有所影響。各有利弊吧。

參考文章

https://blog.csdn.net/weixin_41153791/article/details/82291144
http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
https://baijiahao.baidu.com/s?id=1608021814182894637&wfr=spider&for=pc

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章