天天看點

什麼是JWT

什麼是JWT 

     Json web token(JWT)是為了網絡應用環境間傳遞聲明而執行的一種基于JSON的開發标準(RFC 7519),該token被設計為緊湊且安全的,特别适用于分布式站點的單點登陸(SSO)場景。JWT的聲明一般被用來在身份提供者和服務提供者間傳遞被認證的使用者身份資訊,以便于從資源伺服器擷取資源,也可以增加一些額外的其它業務邏輯所必須的聲明資訊,該token也可直接被用于認證,也可被加密。

  起源

   說起JWT,我們應該來談一談基于token的認證和傳統的Session認證的差別。

  傳統的session認證

    我們知道,http協定本身是一種無狀态的協定,而這就意味着如果使用者向我們的應用提供了使用者名和密碼來進行使用者認證,那麼下一次請求時,使用者還要再一次進行使用者認證才行,因為根據http協定,我們并不能知道是哪個使用者發送的請求,是以為了讓我們的應用能識别是哪個使用者發出的,我們隻能在伺服器存儲一份使用者登陸的資訊,這份登陸資訊會在響應時傳遞給伺服器,告訴其儲存為cookie,以便下次請求時發送給我們的應用,這樣我們的英喲個就能識别請求來自哪個使用者了,這就是傳統的基于sessino認證

       但是這種基于session的認證使應用本身很難得擴充,随着不用用戶端的增加,獨立的伺服器已無法承載更多的使用者,而這個時候基于session認證應用的問題就會暴露出來

  基于session認證所顯露的問題

       Session:每個使用者經過我們的應用認證之後,我們的應用都要在服務端做一次記錄,以便使用者下次請求的鑒别,通常而言session都是儲存在記憶體中,而随着認證使用者的增多,服務端的開銷會明顯增大

        擴充性:使用者認證之後,服務端做認證記錄,如果認證的記錄被儲存在記憶體的話,這意味着使用者下次請求還必須要請求在這台伺服器上,這樣才能拿到授權的資源,這樣在分布式的應用上,響應的限制了負載均衡器的能力,也意味着限制了應用的擴充性

        CSRF:因為是基于cookie來進行使用者識别的,cookie如果被截獲,使用者就會很容易受到跨站請求僞造的攻擊。

基于token的鑒權機制

    基于token的鑒權機制類似于http協定也是無狀态的,它不需要在服務端去保留使用者的認證資訊或會話資訊。這也就意味着機遇tokent認證機制的應用不需要去考慮使用者在哪一台伺服器登陸了,這就為應用的擴充提供了便利

     流程是這樣的

  • 使用者使用使用者名密碼請求伺服器
  • 伺服器進行驗證使用者資訊
  • 伺服器通過驗證發送給使用者一個token
  • 用戶端存儲token,并在每次請求時附加這個token值
  • 伺服器驗證token,并傳回資料

      這個token必須要在每次請求時發送給伺服器,它應該儲存在請求頭中,另外,伺服器要支援CORS(跨來源資源共享)政策,一般我們在服務端這麼做就可以了 Access-Control-Allow-Origin:*

JWT的構成

      JWT是由三部分構成,将這三段資訊文本用連結構成了JWT字元串。就像這樣

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJVc2VySWQiOjEyMywiVXNlck5hbWUiOiJhZG1pbiJ9.Qjw1epD5P6p4Yy2yju3-fkq28PddznqRj3ESfALQy_U      

    第一部分我們稱它為頭部(header)第二部分我們稱其為載荷(payload,類似于飛機上承載的物品),第三部分是簽證(signature)

   header

      JWT的頭部承載的兩部分資訊:

  • 聲明類型,這裡是jwt
  • 聲明加密的算法,通常直接使用HMAC SHA256

   完整的頭部就像下面這樣的JSON

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

    然後将頭部進行base64加密(該加密是可以對稱解密的),構成了第一部分

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9      

    plyload

      載荷就是存放有效資訊的地方。這個名字像是特指飛機上承載的貨品,這些有效資訊包含三個部分

  • 标準中注冊的聲明
  • 公共的聲明
  • 私有的聲明 

     标注中注冊的聲明(建議不強制使用)

  • iss:jwt簽發者
  • sub:jwt所面向的使用者
  • aud:接收jwt的一方
  • exp:jwt的過期時間,這個過期時間必須大于簽發時間
  • nbf:定義在什麼時間之前,該jwt都是不可用的
  • iat:jwt的簽發時間
  • jti:jwt的唯一身份辨別,主要用來作為一次性token,進而回避重播攻擊 

    公共的聲明:

       公共的聲明可以添加任何的資訊,一般添加使用者的相關資訊或其它業務需要的必要資訊,但不建議添加敏感資訊,因為該部分在用戶端可解密;

     私有的聲明

         私有的聲明是提供者和消費者功能定義的聲明,一般不建議存放敏感資訊,因為base64是對稱解密的,意味着該部分資訊可以歸類為名文資訊。

     定義一個payload

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

    然後将其base64加密,得到jwt的一部分

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9      

Signature

    jwt的第三部分是一個簽證資訊,這個簽證資訊由三部分組成:

  • header(base64後的)
  • payload(base64後的)
  • secred     

       這個部分需要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了

 應用 

      一般是在請求頭裡加入Authorization,并加上Bearer标注:

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

       服務端會驗證token,如果驗證通過就會傳回相應的資源,整個流程就是這樣

什麼是JWT

   總結

      優點:

  • 因為json的通用性,是以JWT是可以跨語言支援的,像C#,JavaScript,NodeJS,PHP等許多語言都可以使用
  • 因為由了payload部分,是以JWT可以在自身存儲一些其它業務邏輯所必要的非敏感資訊
  • 便于傳輸,jwt的構成非常簡單,位元組占用很小,是以它是非常便于傳輸的
  • 它不需要在服務端儲存會話資訊,是以它易于應用的擴充

       安全相關

  • 不應該在jwt的payload部分存儲敏感資訊,因為該部分是用戶端可解密的部分
  • 保護好secret私鑰。該私鑰非常重要
  • 如果可以,請使用https協定