轉自於:http://www.jianshu.com/p/576dbf44b2aeweb
Json web token(JWT)是爲了網絡應用環境間傳遞聲明而執行的一種基於JSON的開發標準(RFC 7519),該token被設計爲緊湊且安全的,特別適用於分佈式站點的單點登錄(SSO)場景。JWT的聲明通常被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,以便於從資源服務器獲取資源,也能夠增長一些額外的其它業務邏輯所必須的聲明信息,該token也可直接被用於認證,也可被加密。算法
提及JWT,咱們應該來談一談基於token的認證和傳統的Session認證的區別。json
咱們知道,http協議自己是一種無狀態的協議,而這就意味着若是用戶向咱們的應用提供了用戶名和密碼來進行用戶認證,那麼下一次請求時,用戶還要再一次進行用戶認證才行,由於根據http協議,咱們並不能知道是哪一個用戶發送的請求,因此爲了讓咱們的應用能識別是哪一個用戶發出的,咱們只能在服務器存儲一份用戶登錄的信息,這份登錄信息會在響應時傳遞給服務器,告訴其保存爲cookie,以便下次請求時發送給咱們的應用,這樣咱們的英喲個就能識別請求來自哪一個用戶了,這就是傳統的基於sessino認證api
可是這種基於session的認證使應用自己很可貴擴展,隨着不用客戶端的增長,獨立的服務器已沒法承載更多的用戶,而這個時候基於session認證應用的問題就會暴露出來安全
Session:每一個用戶通過咱們的應用認證以後,咱們的應用都要在服務端作一次記錄,以便用戶下次請求的鑑別,一般而言session都是保存在內存中,而隨着認證用戶的增多,服務端的開銷會明顯增大服務器
擴展性:用戶認證以後,服務端作認證記錄,若是認證的記錄被保存在內存的話,這意味着用戶下次請求還必需要請求在這臺服務器上,這樣才能拿到受權的資源,這樣在分佈式的應用上,響應的限制了負載均衡器的能力,也意味着限制了應用的擴展性cookie
CSRF:由於是基於cookie來進行用戶識別的,cookie若是被截獲,用戶就會很容易受到跨站請求僞造的攻擊。網絡
基於token的鑑權機制相似於http協議也是無狀態的,它不須要在服務端去保留用戶的認證信息或會話信息。這也就意味着機遇tokent認證機制的應用不須要去考慮用戶在哪一臺服務器登錄了,這就爲應用的擴展提供了便利session
流程是這樣的負載均衡
這個token必需要在每次請求時發送給服務器,它應該保存在請求頭中,另外,服務器要支持CORS(跨來源資源共享)策略,通常咱們在服務端這麼作就能夠了 Access-Control-Allow-Origin:*
JWT是由三部分構成,將這三段信息文本用連接構成了JWT字符串。就像這樣
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJVc2VySWQiOjEyMywiVXNlck5hbWUiOiJhZG1pbiJ9.Qjw1epD5P6p4Yy2yju3-fkq28PddznqRj3ESfALQy_U
第一部分咱們稱它爲頭部(header)第二部分咱們稱其爲載荷(payload,相似於飛機上承載的物品),第三部分是簽證(signature)
JWT的頭部承載的兩部分信息:
完整的頭部就像下面這樣的JSON
{ 'typ':'JWT', 'alg':'HS256' }
而後將頭部進行base64加密(該加密是能夠對稱解密的),構成了第一部分
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
載荷就是存放有效信息的地方。這個名字像是特指飛機上承載的貨品,這些有效信息包含三個部分
標註中註冊的聲明(建議不強制使用)
公共的聲明:
公共的聲明能夠添加任何的信息,通常添加用戶的相關信息或其它業務須要的必要信息,但不建議添加敏感信息,由於該部分在客戶端可解密;
私有的聲明
私有的聲明是提供者和消費者功能定義的聲明,通常不建議存放敏感信息,由於base64是對稱解密的,意味着該部分信息能夠歸類爲名文信息。
定義一個payload
{ "sub": "1234567890", "name": "John Doe", "admin": true }
而後將其base64加密,獲得jwt的一部分
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
Signature
jwt的第三部分是一個簽證信息,這個簽證信息由三部分組成:
這個部分須要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,若是驗證經過就會返回相應的資源,整個流程就是這樣
總結
優勢:
安全相關