1. 程式人生 > >JSON WEB TOKEN詳解

JSON WEB TOKEN詳解

定義:JSON Web Token(JWT)是一個非常輕巧的規範。這個規範允許我們使用JWT在使用者和伺服器之間傳遞安全可靠的資訊。

適用場景:
1、用於向Web應用傳遞一些非敏感資訊。例如完成加好友、下訂單的操作等等。
2、用於設計使用者認證和授權系統。
3、實現Web應用的單點登入。

JWT的資訊組成:
     JWT由三部分組成:頭部header、載荷payload、簽名signature。
JWT的基本格式:
    XX.XX.XX

頭部(header):JWT還需要一個頭部,頭部用於描述關於該JWT的最基本的資訊,例如其型別以及簽名所用的演算法等。這也可以被表示成一個JSON物件。

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

header頭部是一個JSON串,其中包含兩部分的資訊。
一個是type:宣告型別,表示是JWT型別
一個是alg:表名加密演算法,一般使用HMAC SHA256
然後將頭部進行base64編碼(base64不是加密演算法,是一種編碼手段,可以通過反編碼出來),編碼之後的字串為:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

載荷(payload):JWT這個加密字串中可以存放資訊的地方,這些有效的資訊一共包含三個部分.

 1. 公共的宣告
 2. 私有的宣告
 3. 標準中註冊的宣告

公共的宣告:
可以新增任何的資訊,一般新增使用者的相關資訊或者其他業務相關的必要資訊。
一般不新增私密的資訊,因為公共的宣告客戶端可以解開。

私有的宣告:
私有宣告是提供者和消費者所共同定義的宣告,一般不建議存放敏感資訊,因為base64是對稱解密的,意味著該部分資訊可以歸類為明文資訊.總之payload裡面不要存有任何的私密資訊.

    標準中註冊的宣告 (建議但不強制使用) :
        iss: jwt簽發者
        sub: jwt所面向的使用者
        aud: 接收jwt的一方
        exp: jwt的過期時間,這個過期時間必須要大於簽發時間
        nbf: 定義在什麼時間之前,該jwt都是不可用的.
        iat: jwt的簽發時間
        jti: jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊。
定義一個payload:
    {
    "iss": "POC999",
    "iat": 2152132152,
    "exp": 2212321523,
    "aud": "www.demo.com",
    "sub": "spark@demo.com",
    }
將上面的JSON字串進行BASE64編碼得到payload:
ewogICAgImlzcyI6ICJQT0M5OTkiLAogICAgImlhdCI6IDIxNTIxMzIxNTIsCiAgICAiZXhwIjogMjIxMjMyMTUyMywKICAgICJhdWQiOiAid3d3LmRlbW8uY29tIiwKICAgICJzdWIiOiAic3BhcmtAZGVtby5jb20iLAoJfQ==

簽名(signature):
將上面的兩個編碼後的字串都用句號.連線在一起(頭部在前),就形成了

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.ewogICAgImlzcyI6ICJQT0M5OTkiLAogICAgImlhdCI6IDIxNTIxMzIxNTIsCiAgICAiZXhwIjogMjIxMjMyMTUyMywKICAgICJhdWQiOiAid3d3LmRlbW8uY29tIiwKICAgICJzdWIiOiAic3BhcmtAZGVtby5jb20iLAoJfQ==

最後,我們將上面拼接完的字串用HS256演算法進行加密。在加密的時候,我們還需要提供一個金鑰(secret)。如果我們用Spark作為金鑰的話,那麼就可以得到我們加密後的內容

rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM

最後的JWT串為:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.ewogICAgImlzcyI6ICJQT0M5OTkiLAogICAgImlhdCI6IDIxNTIxMzIxNTIsCiAgICAiZXhwIjogMjIxMjMyMTUyMywKICAgICJhdWQiOiAid3d3LmRlbW8uY29tIiwKICAgICJzdWIiOiAic3BhcmtAZGVtby5jb20iLAoJfQ==.rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM

到這裡我們就生成了最終的JWT字串,但是還有幾個疑問需要我來解答清楚:
1. 簽名的目的是什麼?
2. Base64是一種編碼,是可逆的,那麼我的資訊不就被暴露了嗎?

簽名的目的?
最後一步簽名的過程,實際上是對頭部以及載荷內容進行簽名。一般而言,加密演算法對於不同的輸入產生的輸出總是不一樣的。對於兩個不同的輸入,產生同樣的輸出的概率極其地小(有可能比我成世界首富的概率還小)。所以,我們就把“不一樣的輸入產生不一樣的輸出”當做必然事件來看待吧。

所以,如果有人對頭部以及載荷的內容解碼之後進行修改,再進行編碼的話,那麼新的頭部和載荷的簽名和之前的簽名就將是不一樣的。而且,如果不知道伺服器加密的時候用的金鑰的話,得出來的簽名也一定會是不一樣的。
伺服器應用在接受到JWT後,會首先對頭部和載荷的內容用同一演算法再次簽名。那麼伺服器應用是怎麼知道我們用的是哪一種演算法呢?別忘了,我們在JWT的頭部中已經用alg欄位指明瞭我們的加密演算法了。

如果伺服器應用對頭部和載荷再次以同樣方法簽名之後發現,自己計算出來的簽名和接受到的簽名不一樣,那麼就說明這個Token的內容被別人動過的,我們應該拒絕這個Token,返回一個HTTP 401 Unauthorized響應。

資訊會暴露?
是的。
所以,在JWT中,不應該在載荷裡面加入任何敏感的資料。在上面的例子中,我們傳輸的是使用者的User ID。這個值實際上不是什麼敏感內容,一般情況下被知道也是安全的。
但是像密碼這樣的內容就不能被放在JWT中了。如果將使用者的密碼放在了JWT中,那麼懷有惡意的第三方通過Base64解碼就能很快地知道你的密碼了。

JSON Web Token令牌如何工作?
在身份驗證中,當用戶使用他們的憑證成功登入時,將返回一個JSON Web Token,並且必須儲存在本地(通常在本地儲存中,但也可以使用Cookie),而不是在傳統方法中建立會話伺服器並返回一個cookie。

無論何時使用者想要訪問受保護的路由或資源,使用者代理都應使用承載模式來發送JWT,通常在授權標頭中。標題的內容應該如下所示:

Authorization: Bearer <token>

這是一種無狀態身份驗證機制,因為使用者狀態永遠不會儲存在伺服器記憶體中。伺服器受保護的路由將在授權頭中檢查有效的JWT,如果存在,則允許使用者訪問受保護的資源。由於JWT是獨立的,所有必要的資訊都在那裡,減少了多次查詢資料庫的需要。

這使您可以完全依賴無狀態的資料API,甚至向下遊服務發出請求。無論哪個域正在為您的API提供服務,跨源資源共享(CORS)將不會成為問題,因為它不使用cookie。

最後,如果你想了解JWT的更多介紹可以點選JSON Web令牌官網介紹