jwt教程

未完待續(xù)

JWT是什么挺据?

JWT是JSON Web Token的縮寫取具,即JSON Web令牌

<a target="_blank">JWT規(guī)范</a>中對其所作的描述是:

JSON Web令牌(JWT)是一種緊湊的扁耐、URL安全的方式暇检,用來表示要在雙方之間傳遞的“聲明”。JWT中的聲明被編碼為JSON對象婉称,用作JSON Web簽名(JWS)結構的有效內容或JSON Web加密(JWE)結構的明文块仆,使得聲明能夠被:數字簽名、或利用消息認證碼(MAC)保護完整性王暗、加密悔据。

JWT的聲明(Claims)就是一小段信息,用“鍵-值”對表示俗壹。

想要詳細了解<a target="_blank">JSON Web簽名(JWS)</a>和<a target="_blank">JSON Web加密(JWE)</a>科汗,可以自行去IETF的網站查閱規(guī)范,下文中我會簡單的介紹它們绷雏。

JWT的構成

JWT由三部分組成:

  • Header:頭部头滔,即JOSE Header
  • Claims:聲明,即JWS Paylaod
  • Signature:簽名涎显,即JWS Signature

JWT由這三部分組成坤检,每一部分都是使用base64url編碼的,并使用句點(.)連接起來期吓。這里使用base64url編碼而不是普通的base64早歇,是因為base64編碼會產生+/,這兩個字符在URL中是有特殊意義的讨勤,會導致JWT不是URL安全的箭跳。

下面以<a target="_blank">JWT.io</a>首頁的一個例子介紹JWT的組成。再用Golang通過這些JSON對象生成JWT潭千,最后用<a target="_blank">jwt-go</a>包比對生成的JWT衅码。

JWT標準并沒有規(guī)定必須清除JSON結構中開頭結尾的空白符和換行,但是為了消除歧義脊岳,一般在使用JSON對象時不用換行逝段,并去掉多余的空白符垛玻,這會在我們的代碼中有所體現。

為了方便查看奶躯,下面展示代碼時使用的都是格式化后的JSON對象帚桩。

頭部(JOSE Header)

JSOEJSON Object Signing and Encryption,即JSON對象簽名與加密的縮寫嘹黔。

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

示例中給出了兩個聲明:

  • typ: (Type)類型账嚎。在JOSE Header中這是個可選參數,但這里我們需要指明類型是JWT儡蔓。
  • alg: (Algorithm)算法郭蕉,必須是JWS支持的算法,算法列表可以在<a target="_blank">JSON Web算法(JWA)</a>喂江。這里指定算法為HS256

例子中只列舉了兩個聲明召锈,更多的聲明和其具體定義可以到<a target="_blank">JSON Web簽名(JWS)</a>中查看。

Golang代碼:

...
header := []byte(`{
  "typ": "JWT",
  "alg": "HS256"
}`)

buffer := new(bytes.Buffer)
//去掉多余的換行和空白符
json.Compact(buffer, header)
//Base64URL編碼
jwtHeader := base64.URLEncoding.EncodeToString(buffer.Bytes())
//eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
fmt.Println(jwtHeader)
...

上述代碼片段會輸出eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9获询,這就是編碼后的JWT頭部涨岁。

聲明(JWT Claims)

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

例子中給的是一個注冊的聲明(sub),和兩個私有的聲明(nameadmin)吉嚣。

注冊的梢薪、公開的、私有的

在一個聲明集當中尝哆,一般會有如下注冊的聲明名字

  • iss: (Issuer)簽發(fā)者
  • iat: (Issued At)簽發(fā)時間秉撇,用Unix時間戳表示
  • exp: (Expiration Time)過期時間,用Unix時間戳表示
  • aud: (Audience)接收該JWT的一方
  • sub: (Subject)該JWT的主題
  • nbf: (Not Before)不要早于這個時間
  • jti: (JWT ID)用于標識JWT的唯一ID

上面的聲明都是可選的秋泄,但是一般都達成共識琐馆,
注冊的聲明是在IANA中注冊的,
公開的聲明要保證不引起命名沖突
私有的聲明可以使用

Golang代碼:

...
claims := []byte(`{
    "sub": "1234567890",
    "name": "John Doe",
    "admin": true
}`)

buffer := new(bytes.Buffer)
json.Compact(buffer, claims)
jwtClaims := base64.URLEncoding.EncodeToString(buffer.Bytes())
fmt.Println(jwtClaims)
...

上述代碼片段會輸出eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9印衔,這就是編碼后的JWT聲明。

簽名(Signature)

按照頭部中指定的姥敛,我們要使用HS256算法對上面的編碼后的字符串進行簽名奸焙。
頭部和聲明用.號連接起來:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

我們要做的就是對這個字符串進行簽名。

Golang代碼:

...
//eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
s := strings.Join([]string{jwtHeader, jwtClaims}, ".")
//HS256算法彤敛,key是"secret"
mac := hmac.New(sha256.New, []byte("secret"))
mac.Write([]byte(s))
expectedMAC := mac.Sum(nil)
//TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
signature := strings.TrimRight(base64.URLEncoding.EncodeToString(expectedMAC), "=")
fmt.Println(signature)
...

上述代碼輸出TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ与帆,這就是這個JWT的簽名。

將頭部墨榄、聲明玄糟、簽名用.號連在一起就得到了我們要的JWT。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

驗證

...
//定義
type MyCustomClaims struct {
    Sub   string `json:"sub"`
    Name  string `json:"name"`
    Admin bool   `json:"admin"`
}
//實現Claims接口
func (m MyCustomClaims) Valid() error {
    return nil
}

mySigningKey := []byte("secret")

claims2 := MyCustomClaims{
    "1234567890",
    "John Doe",
    true,
}


token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims2)
ss, err := token.SignedString(mySigningKey)
fmt.Printf("%v %v\n", ss, err)
if ss == s {
    fmt.Println("OK")
}
...
// Encode JWT specific base64url encoding with padding stripped
func EncodeSegment(seg []byte) string {
    return strings.TrimRight(base64.URLEncoding.EncodeToString(seg), "=")
}

// Decode JWT specific base64url encoding with padding stripped
func DecodeSegment(seg string) ([]byte, error) {
    if l := len(seg) % 4; l > 0 {
        seg += strings.Repeat("=", 4-l)
    }
    return base64.URLEncoding.DecodeString(seg)
}

不安全的JWT

簽名為空的JWT

創(chuàng)建JWT

按一下步驟創(chuàng)建:

對UTF-8的八進制序列進行Base64url編碼

一些可以應用JWT的案例

注意:下面的例子設計并不完善袄秩,甚至存在漏洞阵翎。這里僅僅是展示JWT的用途逢并。不要將例子直接用于生產環(huán)境。

驗證用戶

簽發(fā)JWT

1.客戶端發(fā)送帶有用戶名郭卫、密碼的表單到服務器砍聊;
2.服務器驗證用戶名密碼后,將user_id作為JWT Claims中的一個聲明贰军,生成JWT玻蝌;
3.將簽發(fā)的JWT作為cookies的內容發(fā)送給用戶。

這里要注意词疼,JWT作為cookies的一部分俯树,本質上還是cookies,所以還是要遵循一般的安全原則贰盗,防止XSS等攻擊手段许饿。

驗證請求

1.客戶端發(fā)送帶有JWT的請求到服務器;
2.服務器從JWT中提取信息童太;
3.驗證JWT是否合法(簽名是否正確米辐、令牌是否過期、請求時間在nbf之前還是之后书释、簽發(fā)人是否被接受翘贮、服務器是否是真正的接受者等);
4.從聲明中取出user_id

和session的區(qū)別

session需要在服務器中存儲標記用戶的信息爆惧,比如session_id狸页,而JWT則需要。

JWT在服務器端需要一定量的計算扯再,而session方式一般不需要芍耘。

在分布式系統中,使用Session的方式熄阻,需要在多臺服務器之間session id斋竞,增加了服務器的內存和IO壓力。而JWT方式則免去了同步的麻煩秃殉。因為用戶的狀態(tài)已經存儲在客戶端中了坝初,雖然增加了一些計算開銷,但是與IO開銷比起來钾军,還是要好很多的鳄袍。

單點登錄

Set-Cookie: jwt=header.claims.signature; HttpOnly; max-age=980000; domain=.yourdomain.com

我們將域名設置為頂級域名(域名前要加.),這樣yourdomain.com*.yourdomain.com都能接收這個cookies了吏恭。

免登陸退訂訂閱郵件功能

我們的郵箱中經常會收到一些訂閱郵件拗小,有一些

一些有用的鏈接

<a target="_blank">JWT.io</a>
<a target="_blank">Using JSON Web Tokens with Node.js</a>

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市樱哼,隨后出現的幾起案子哀九,更是在濱河造成了極大的恐慌剿配,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勾栗,死亡現場離奇詭異惨篱,居然都是意外死亡,警方通過查閱死者的電腦和手機围俘,發(fā)現死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門砸讳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人界牡,你說我怎么就攤上這事簿寂。” “怎么了宿亡?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵常遂,是天一觀的道長。 經常有香客問我挽荠,道長克胳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任圈匆,我火速辦了婚禮漠另,結果婚禮上,老公的妹妹穿的比我還像新娘跃赚。我一直安慰自己笆搓,他們只是感情好,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布纬傲。 她就那樣靜靜地躺著满败,像睡著了一般。 火紅的嫁衣襯著肌膚如雪叹括。 梳的紋絲不亂的頭發(fā)上算墨,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音汁雷,去河邊找鬼净嘀。 笑死,一個胖子當著我的面吹牛摔竿,可吹牛的內容都是我干的面粮。 我是一名探鬼主播少孝,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼继低,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了稍走?” 一聲冷哼從身側響起袁翁,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤柴底,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后粱胜,有當地人在樹林里發(fā)現了一具尸體柄驻,經...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年焙压,在試婚紗的時候發(fā)現自己被綠了鸿脓。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡涯曲,死狀恐怖野哭,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情幻件,我是刑警寧澤拨黔,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站绰沥,受9級特大地震影響篱蝇,放射性物質發(fā)生泄漏。R本人自食惡果不足惜徽曲,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一零截、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧疟位,春花似錦瞻润、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至得院,卻和暖如春傻铣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背祥绞。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工非洲, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蜕径。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓两踏,卻偏偏與公主長得像,于是被迫代替她去往敵國和親兜喻。 傳聞我的和親對象是個殘疾皇子梦染,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內容

  • 概述 JSON Web令牌(JWT)是一個緊湊的采用URL安全表示方法的聲明,用于在兩方之間傳輸。JWT的聲明被編...
    御淺永夜閱讀 5,177評論 0 0
  • 轉載本文需注明出處:微信公眾號EAWorld帕识,違者必究泛粹。 本文目錄: 一、單體應用 VS 微服務 二肮疗、微服務常見安...
    72a1f772fe47閱讀 8,556評論 3 25
  • 本文目錄:一晶姊、單體應用 VS 微服務二、微服務常見安全認證方案三伪货、JWT介紹四们衙、OAuth 2.0 介紹五、思考總...
    挨踢的懶貓閱讀 17,972評論 5 29
  • 1. 微服務架構介紹 1.1 什么是微服務架構碱呼? 形像一點來說砍艾,微服務架構就像搭積木,每個微服務都是一個零件巍举,并使...
    靜修佛緣閱讀 6,643評論 0 39
  • JWT是一種用于雙方之間傳遞安全信息的簡潔的脆荷、URL安全的表述性聲明規(guī)范。JWT作為一個開放的標準(RFC 751...
    Renky閱讀 11,340評論 0 14