SpringSceurity(6)---JWT詳解


SpringSceurity(6)---JWT詳解

在JWT之前我們在做用戶認證的時候,基本上會考慮sessiontoken,所以在講jwt之前,我們先來回顧下這個兩個

一、傳統的session認證

1、原理流程

session 是基於 cookie 實現的,session 存儲在服務器端,sessionId 會被存儲到客戶端的cookie 中,具體流程如下

session 認證流程

1、用戶第一次請求服務器的時候,服務器根據用戶提交的相關信息,創建對應的 Session

2、請求返回時將此 Session 的唯一標識信息 SessionID 返回給瀏覽器。

3、瀏覽器接收到服務器返回的 SessionID 信息后,會將此信息存入到 Cookie 中,同時 Cookie 記錄此 SessionID 屬於哪個域名。

4、當用戶第二次訪問服務器的時候,請求會自動判斷此域名下是否存在 Cookie 信息,如果存在自動將 Cookie 信息也發送給服務端,服務端會 從 Cookie 中獲取 SessionID,

再根據 SessionID 查找對應的 Session 信息,如果沒有找到說明用戶沒有登錄或者登錄失效,如果找到 Session 證明用戶已經登錄可執行后面操作。

總結 根據以上流程可知,SessionID 是連接 Cookie 和 Session 的一道橋梁,大部分系統也是根據此原理來驗證用戶登錄狀態。

2、session 時需要考慮的問題

1、將 session 存儲在服務器里面,當用戶同時在線量比較多時,這些 session 會占據較多的內存,需要在服務端定期的去清理過期的 session。

2、當網站采用集群部署的時候,會遇到多台 web 服務器之間如何做 session 共享的問題。因為 session 是由單個服務器創建的,但是處理用戶請求

的服務器不一定是那個創建 session 的服務器,那么該服務器就無法拿到之前已經放入到 session 中的登錄憑證之類的信息了。

3、當多個應用要共享 session 時,除了以上問題,還會遇到跨域問題,因為不同的應用可能部署的主機不一樣,需要在各個應用做好 cookie 跨域的處理。

4、sessionId 是存儲在 cookie 中的,假如瀏覽器禁止 cookie 或不支持 cookie 怎么辦?

5、移動端對 cookie 的支持不是很好,而 session 需要基於 cookie 實現,所以移動端常用的是 token。

6、CSRF: 因為是基於cookie來進行用戶識別的, cookie如果被截獲,用戶就會很容易受到跨站請求偽造的攻擊。

二、Token(令牌)

1、原理流程

基於token的鑒權機制類似於http協議也是無狀態的,它不需要在服務端去保留用戶的認證信息或者會話信息

這個不是標准時序圖,能看懂大致意思就行。

token認證流程

1、客戶端使用用戶名跟密碼請求登錄。

2、服務端收到請求,去認證服務器驗證用戶名與密碼。

3、驗證成功后,服務端會簽發一個 token 並把這個 token 發送給客戶端。

4、客戶端收到 token 以后,會把它存儲起來,比如放在 cookie 里或者 localStorage 里。

5、客戶端每次向服務端請求資源的時候需要帶着服務端簽發的 token。

6、服務端收到請求,然后去驗證客戶端請求里面帶着的 token ,如果驗證成功,就向客戶端返回請求的數據。

2、token特點

1、如果你認為用數據庫來存儲 token 會導致查詢時間太長,可以選擇放在內存當中。比如 redis 很適合你對 token 查詢的需求。

2、token 完全由應用管理,所以它可以避開同源策略。

3、token 可以避免 CSRF 攻擊(因為不需要 cookie 了)

4、移動端對 cookie 的支持不是很好,而 session 需要基於 cookie 實現,所以移動端常用的是 token

3、Token 和 Session 的區別

Session 是一種記錄服務器和客戶端會話狀態的機制,使服務端有狀態化,可以記錄會話信息。而 Token 是令牌,訪問資源接口(API)時所需要的資源憑證。Token

使服務端無狀態化,不會存儲會話信息。

有關Token 和 Session可以看下這篇博客 徹底理解cookie、session、token


三、JWT

我們在使用token的時候會發現,前端給我們傳了token之后,我們還需要拿者這個token去數據庫查詢用戶信息,並返回。這樣數據庫的操作肯定會影響一定的性能,

那jwt就是來解決這個的。

1、原理流程

JWT 認證流程

1、用戶輸入用戶名/密碼登錄,服務端認證成功后,會返回給客戶端一個 JWT。

2、客戶端將 jwt 保存到本地(通常使用 localstorage,也可以使用 cookie)。

3、當用戶希望訪問一個受保護的路由或者資源的時候,需要請求頭的 Authorization 字段中使用Bearer 模式添加 JWT,其內容看起來是下面這樣 Authorization: Bearer jwt

4、服務端的保護路由將會檢查請求頭 Authorization 中的 JWT 信息,如果合法,則允許用戶的行為。

因為 JWT 是自包含的(內部包含了一些會話信息),因此減少了需要查詢數據庫的需要,因為 JWT 並不使用 Cookie 的,所以你可以使用任何域名提供你的

API 服務而不需要擔心跨域資源共享問題(CORS),因為用戶的狀態不再存儲在服務端的內存中,所以這是一種無狀態的認證機制。

2、Token 和 JWT 的區別

相同

1、都是訪問資源的令牌

2、都可以記錄用戶的信息

3、都是使服務端無狀態化

4、都是只有驗證成功后,客戶端才能訪問服務端上受保護的資源

區別

Token 服務端驗證客戶端發送過來的 Token 時,還需要查詢數據庫獲取用戶信息,然后驗證 Token 是否有效。

JWT 將 Token 和 Payload 加密后存儲於客戶端,服務端只需要使用密鑰解密進行校驗(校驗也是 JWT 自己實現的)即可,不需要查詢或者減少查詢數據庫,

因為 JWT 自包含了用戶信息和加密的數據。

3、JWT特點

從優點來講,它最大的優點就是,當服務端拿到JWT之后,我們不需要向token樣還需去查詢數據庫校驗信息,因為JWT中就包含用戶信息,所以減少一次數據的查詢,

但這樣做也會帶來很明顯的問題

1、無法滿足修改密碼場景

因為上面說過,服務端拿到jwt是不會在去查詢數據庫的,所以就算你改了密碼,服務端還是未知的。那么假設號被到了,修改密碼(是用戶密碼,不是 jwt 的 secret)之后,

盜號者在原 jwt 有效期之內依舊可以繼續訪問系統,所以僅僅清空 cookie 自然是不夠的,這時,需要強制性的修改 secret。

2、無法滿足注銷場景

傳統的 session+cookie 方案用戶點擊注銷,服務端清空 session 即可,因為狀態保存在服務端。但 jwt 的方案就比較難辦了,因為 jwt 是無狀態的,服務端通過計算來校驗

有效性。沒有存儲起來,所以即使客戶端刪除了 jwt,但是該 jwt 還是在有效期內,只不過處於一個游離狀態。

3、無法滿足token續簽場景

我們知道微信只要你每天使用是不需要重新登錄的,因為有token續簽,因為傳統的 cookie 續簽方案一般都是框架自帶的,session 有效期 30 分鍾,30 分鍾內如果有訪問

,session 有效期被刷新至 30 分鍾。但是 jwt 本身的 payload 之中也有一個 exp 過期時間參數,來代表一個 jwt 的時效性,而 jwt 想延期這個 exp 就有點身不由己了,因為

payload 是參與簽名的,一旦過期時間被修改,整個 jwt 串就變了,jwt 的特性天然不支持續簽!


四、JWT的結構

JWT由三部分組成,分別是 頭信息有效載荷,簽名 中間以 點(.) 分隔,具體如下

  xxxxx.yyyyy.zzzzz

1、header(頭信息)

由兩部分組成,令牌類型(即:JWT)、散列算法(HMAC、RSASSA、RSASSA-PSS等),例如:

{
  "alg": "HS256",
  "typ": "JWT"
}

然后,這個JSON被編碼為Base64Url,形成JWT的第一部分。

2、Payload(有效載荷)

JWT的第二部分是payload,其中包含claims。claims是關於實體(常用的是用戶信息)和其他數據的聲明,claims有三種類型: registered, public, and private claims。

Registered claims:這些是一組預定義的claims,非強制性的,但是推薦使用, iss(發行人), exp(到期時間), sub(主題), aud(觀眾)等;

Public claims: 自定義claims,注意不要和JWT注冊表中屬性沖突,這里可以查看JWT注冊表。

Private claims: 這些是自定義的claims,用於在同意使用這些claims的各方之間共享信息,它們既不是Registered claims,也不是Public claims。

示例

 {
   "sub": "1234567890",
   "name": "John Doe",
   "admin": true
 }

然后,再經過Base64Url編碼,形成JWT的第二部分;

注意:對於簽名令牌,此信息雖然可以防止篡改,但任何人都可以讀取。除非加密,否則不要將敏感信息放入到Payload或Header元素中。

3、Signature(簽名)

jwt的第三部分是一個簽證信息,這個簽證信息由三部分組成:header (base64后的)payload (base64后的)secret

這個部分需要base64加密后的header和base64加密后的payload使用.連接組成的字符串,然后通過header中聲明的加密方式進行加鹽secret組合加密,然后就構成了jwt的第三部分。

var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);

var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

將這三部分用.連接成一個完整的字符串,構成了最終的jwt:

 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

注意 secret是保存在服務器端的,jwt的簽發生成也是在服務器端的,secret就是用來進行jwt的簽發和jwt的驗證,所以,它就是你服務端的私鑰,

在任何場景都不應該流露出去。一旦客戶端得知這個secret, 那就意味着客戶端是可以自我簽發jwt了。

4、如何應用

一般是在請求頭里加入Authorization,並加上Bearer標注:

fetch('api/user/1', {
  headers: {
    'Authorization': 'Bearer ' + token
  }
})

參考

1、Cookie、Session、Token、JWT



別人罵我胖,我會生氣,因為我心里承認了我胖。別人說我矮,我就會覺得好笑,因為我心里知道我不可能矮。這就是我們為什么會對別人的攻擊生氣。
攻我盾者,乃我內心之矛(24)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM