Blazor和Vue對比學習(進階2.2.4):狀態管理之持久化保存(2),Cookie/Session/jwt

註:本節涉及到前後端,這個系列的對比學習,還是專註在前端VueBlazor技術,所以就不擼碼了,下面主要學習概念。

 

我們知道,Http是無狀態協議,客戶端請求服務端,認證一次後,如果再次請求,又要重新認證,因為對服務端來說,客戶端的每次請求都是無差別的!另外,服務端的授權體系,一般使用基於RBAC角色許可權模型。角色資訊,我們可以在客戶端每次請求都,都查詢一次,但這樣比較消耗資源。最好的方式,是客戶端第一次請求時,就將角色傳給客戶端,之後客戶端每次請求,直接攜帶角色資訊。而這些問題,都需要使用Cookie、Session或者jwt來解決。

 

1、先說Cookie。下圖為Cookie在客戶端與服務端的應用邏輯圖。

 

 

如上圖所示:

  • 客戶端首次請求伺服器時,攜帶參數(如用戶名和密碼),伺服器根據參數判斷是否合法。如合法,則在Response中頒發Cookies,如在.NET中,寫入Cookie,【Response.Cookies[name].value = functionMC;Response.Cookies[name].Expires=DateTime.Now.AddDays(1);】,其中Expires屬性,為服務端設置的過期時間。
  • 客戶端收到伺服器的Response後,將Cookie以健值對的方式保存到瀏覽器中,如使用js直接操作DOM,【document.Cookie=”username=John Doe; expires=Thu, 18 Dec 2043 12:00:00 GMT”;】,客戶端也可以設置過期時間。如不設置,保存在記憶體中,瀏覽器關閉時自動清除;如設置,則保存在本地硬碟中,到期後自動清除。
  • 之後,客戶端每次訪問伺服器,都會在請求頭中攜帶相應站點的Cookie。服務端就可以讀取Cookie,如在.NET中,讀取Cookie,【string name = Request.Cookies[name].value
  • Cookie在安全性方面,易出現Cookie劫持和欺騙,大小和數量方面也受限制。所以在Cookie之後,出現了Session

 

2、Session。下圖為Session在客戶端與服務端的應用邏輯圖

 

如上圖所示

  • 客戶端首次訪問伺服器後,判斷是否合法,如果合法,則在伺服器的快取中建立一個鍵值對,鍵為SessionID,同時將SessionID作為Cookie返回給客戶端。客戶端再次請求時,請求頭攜帶SessionID,服務端根據SessionID,查找快取資訊,根據快取資訊進行處理。
  • 使用Session後,客戶端與服務端之間只傳遞SessionID,更多資訊在服務端快取中,這樣可以保存更多資訊。Session可以設置有效期,也可以不設置。如不設置,則只在當前會話中有效,客戶端關閉後就失效,這樣也更安全。
  • Session本身的含義,指客戶端與服務端之間的會話,背後實質指的是一種伺服器快取,如果請求不是很多,效率還是很高的
  • Session可以有效利用服務端資源,不受客戶端限制,安全性更好。但是,當客戶端的並發請求比較多時,會很占伺服器記憶體。如果是分散式,不同的Session存儲在不同的伺服器之間,而客戶端每次請求的路徑是隨配的,要解決分散式,我們就需要在每台伺服器之間同步SessionID和快取資訊。最後,SessionID還是依賴於CookieCookie的跨域、單點登陸難等問題,它也一樣有。所以,token的方案開始出現,它是目前最主流的方案,而其中最重要的標準,就是jwt

 

 

3、jwt。下圖為token/jwt在客戶端和服務端的應用邏輯圖

 

如上圖所示,jwt重新將資訊保存在了客戶端,節省了服務端資源,也沒有分散式的問題,同時在靈活性和安全性方面有了質的提升。

  • 靈活性方面:不再限於Cookie,可以保存到本地的StorageIndexedDB,甚至於遠程資料庫里,更加的自由。同時,它以JSON加密形式保存在客戶端的,是跨語言的,原則上任何web形式都支援。
  • 安全性方面:可以說有了質的提升,這才是jwt的核心。首先它可以完全不依賴於Cookie,它沒有跨域、單點登陸等問題;其次,它將加密和解密的過程,放在了伺服器上,即使資訊被截獲,也無法篡改。
  • 更好的支援分散式:在Session方案中,我們需要在每個伺服器都進行快取同步。而jwt中,我們只要在每個伺服器都拷備一份密鑰即可,或者可以將密鑰統一保存到一個Redis伺服器中,每個伺服器統一向Redis請求密鑰,這樣連拷貝密鑰的工作也可省去

 

下面簡單說明一下jwt的構成、生成、加密和解密過程:

  • jwtHeaderPlayloadSignature三部分組成,以符號「.」連接成字元串。其中Header是一個json對象的Base64編碼,主要有簽名的演算法資訊;Playload也是一個json對象的Base64編碼,這是傳輸的主體內容,比如用戶角色、所在部門等資訊都可以存放在裡面。我們知道Base64不具有加密能力,可以說HeaderPlayload是明文傳輸的,在Playload裡面,千萬不能放敏感資訊。而Signature才是jwt安全體系的核心
  • jwt是在服務端生成的,我們定義好HeaderPlayload後,在服務端我們有一個密鑰Secret,這個密鑰是一個沒有規律的字元串。我們使用Header里的哈希演算法,加上密鑰、HeaderPlayload,就可以算出簽名SignatureSignature是不可匿的,無法破解,破解也沒意義。
  • 客戶端再次請求時,服務端獲得請求頭攜帶的jwt後,取出HeaderPlayloadSignature(A)。對Header進行Base64解碼,獲取其中的哈希演算法,然後重新使用這個哈希演算法,加上密鑰、HeaderPlayload重新算出一個簽名Signature(B),然後比對Signature(A)Signature(B)是否一致,如果一致,則請求是合法的,服務端放行,並使用Playload中的資訊進行計算處理。
  • 服務端的密鑰Secret,是計算簽名的安全核心,一定要保存好,最好定期更新。