JWT 其全稱爲:JSON Web Token,簡單地說就是 JSON 在 Web 上的一種帶簽名的標記形式。官方的定義以下:html
JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.算法
即:JSON Web Token (JWT)是一個開放標準(RFC 7519),它定義了一種緊湊的、自包含的方式,用於做爲JSON對象在各方之間安全地傳輸信息。api
對信息進行簽名以後再進行傳輸有什麼做用,JWT 就有什麼做用。它能起的做用,決定了在項目的需求中是否有必要使用它,它自身的本質決定了它適合的場景。數組
本質上,JWT 跟本身對信息加個簽名沒有區別。安全
那使用它的理由是什麼呢?ide
(1)它創建了一個標準併爲多數人認識和接受,這樣一來就能夠造成標準庫,使用者能夠共享。函數
(2)它造成了一些最佳實踐,這種實踐過程包括了參數安全傳遞的諸多常見方面,如 exp 到期時間屬性的定義來規定簽名有效期等。按照最佳實踐中對一些 JSON 屬性的明肯定義,再加上標準庫對它的貫徹實現,會帶來不少便利。工具
(3)將其做爲 Token 放在請求的 header 中,做爲無狀態的鑑權方式很適合目前多站點應用的場景。測試
但最佳實踐和其特性不能混爲一談,具體到應用場景,仍然能夠利用其特性做適合該場景的其它發揮。編碼
(1)直接傳參
這種方式,不進行訪問的權限的判斷,公開可直接訪問。
(2)帶KEY傳參
這種方式須要知道正確的 KEY 才能訪問,但 KEY 明文附在後面易泄露。
(3)帶簽名傳參
這種方式,將 KEY 做爲簽名算法的加密條件,不明文顯示,不知道 KEY 則沒法生成相應的簽名,感受不錯。不足在於,簽名一次以後訪問連接一直爲有效會帶有風險。
其中籤名部分,如採用 md5 方式,key 做爲運算的一部分。
sign=md5(p1+p2+key)
(4)帶時間戳簽名
參數中帶上簽名時的時間戳,時間戳會參與簽名算法,服務端不只檢測簽名的有效性,還會比較時間是否在合理範圍內,如 5 分鐘之內,如此一來,連接在一段時間以後就會失效。
http://*/api?p1=*&p2=*×tamp=*&sign=
其中籤名部分,如採用 md5 方式,time,key 均做爲運算的一部分。
sign=md5(p1+p2+time+key)
(5)獨立鑑權參數簽名
將鑑權部分獨立出來簽名,這樣的好處就是鑑權部分獨立的判斷過程,其它形參再也不須要參與這個簽名與判斷過程。
參數可以使用 JSON 形式,因而可讓其變成如下形式:
鑑權傳輸部分形式如:{p1:abcd,p2:abcd}.sign
其中,簽名部分,如採用 md5 方式,將 JSON 字符串與 key 拼接運算,而且使用鏈接符.點,以下。
sign=md5({p1:abcd,p2:abcd}.key)
(6)帶頭部的獨立鑑權部分
爲了更加靈活的,將鑑權部分加個頭部。頭部用來幹什麼呢,能夠指定簽名算法,或之後可能要更多擴展參數用,如如下形式。
{alg:MD5}.{p1:abcd,p2:abcd}.sign
簽名部分,爲前兩部分再鏈接上 key 一塊兒運算。
sign=md5({alg:MD5}.{p1:,p2:}.key)
(7)最終標準化爲 JWT 形式
頭部稱之爲 header,數據部分稱之爲 payload,簽名部分爲 signature。
(7.1) header 不使用明文,採用其 base64 形式
(7.2) payload 不使用明文,採用其 base64 形式
(7.3) signature 爲前二者(都是 base64 形式)經過 . 點鏈接,再採用 header 中指定的簽名算法簽名的結果。
(7.4) 最終形式爲 base64(header).base64(payload).signature
(7.5) base64 考慮到URL編碼,將=去掉,+號變成-,/變成_ 處理。
(7.6) 最終字符串經過做爲請求 header 進行傳輸。
給定一個簽名用的 sercretKey 和 payload,生成成符合要求的 JWT 字符串。多數時候,需求可能就是這樣簡單,至於簽名算法,這裏就使用通常默認的HS256。則須要的功能函數大體是:
func getJwt (payload){ var content = base64({"alg":"HS256","typ":"JWT"}) + . + base64(payload) var signature = base46( sign(content, sercretKey) ) return content + . + sign }
C# 的實例代碼,這裏給出一個 C# 的 JWT 輔助類,其中 JObject 引用了 Newtonsoft.Json 包。
public class JWTHelper { #region 工具函數準備 /// <summary> /// 對字符串 Base64 編碼,而且替換 = + / 爲 "" - _ /// </summary> /// <param name="str"></param> /// <returns></returns> public static string Base64URL(string str) { return Convert.ToBase64String(Encoding.UTF8.GetBytes(str)).Replace("=", "").Replace("+", "-").Replace("/", "_"); } /// <summary> /// 對字節數組 Base64 編碼,而且替換 = + / 爲 "" - _ /// </summary> /// <param name="bs"></param> /// <returns></returns> public static string Base64URL(byte[] bs) { return Convert.ToBase64String(bs).Replace("=", "").Replace("+", "-").Replace("/", "_"); } /// <summary> /// HMAC SHA256 /// </summary> /// <param name="str"></param> /// <returns></returns> public static string HS256(string str, string key) { var encoding = new System.Text.UTF8Encoding(); byte[] keyByte = encoding.GetBytes(key); byte[] messageBytes = encoding.GetBytes(str); using (var hmacsha256 = new HMACSHA256(keyByte)) { byte[] hashmessage = hmacsha256.ComputeHash(messageBytes); return Base64URL(hashmessage); } } #endregion /// <summary> /// 生成簽名後的 JWT 最終字符串。 /// 爲了簡化示例,這裏使用簽名算法就設定爲:HS256 /// header = {"alg":"HS256","typ":"JWT"} /// </summary> /// <param name="payload"></param> /// <param name="key"></param> /// <returns></returns> public static string Sign(JObject payload, String key) { JObject header = new JObject(); header["alg"] = "HS256"; header["typ"] = "JWT"; string h = Base64URL(header.ToString(Formatting.None)); string p = Base64URL(payload.ToString(Formatting.None)); string s = HS256(h + "." + p, key); return String.Format("{0}.{1}.{2}", h, p, s); } }
使用如下代碼測試一下:
JObject payload = new JObject(); payload["username"] = "xxx"; Console.Write(JWTHelper.Sign(payload, "123fd"));
獲得結果
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Inh4eCJ9.1C28A5CqMa70FLtUQh4pwSZWPlZhbQ-ZeYs38K_sqks
在 https://jwt.io/ 上,能夠驗證一下,獲得了一樣的結果。
顯然,它也會存在一些問題,如經過 base64 解碼看到明文,或者是在有效期內取得整個 token 進行訪問等。因此使用是根據須要來的。並且,也能夠在 JWT 上進一步加入自定義的新機制來應對更多的場景。
如下這篇文章列出了一些問題與趨勢,可供參考。