HTTP 是一個(gè)無(wú)狀態(tài)的協(xié)議,一次請(qǐng)求結(jié)束后,下次在發(fā)送服務(wù)器就不知道這個(gè)請(qǐng)求是誰(shuí)發(fā)來(lái)的了(同一個(gè) IP 不代表同一個(gè)用戶),在 Web 應(yīng)用中,用戶的認(rèn)證和鑒權(quán)是非常重要的一環(huán),實(shí)踐中有多種可用方案,并且各有千秋。
基于 Session 的會(huì)話管理
在 Web 應(yīng)用發(fā)展的初期,大部分采用基于 Session 的會(huì)話管理方式,邏輯如下。
- 客戶端使用用戶名密碼進(jìn)行認(rèn)證
- 服務(wù)端生成并存儲(chǔ) Session,將 SessionID 通過(guò) Cookie 返回給客戶端
- 客戶端訪問(wèn)需要認(rèn)證的接口時(shí)在 Cookie 中攜帶 SessionID
- 服務(wù)端通過(guò) SessionID 查找 Session 并進(jìn)行鑒權(quán),返回給客戶端需要的數(shù)據(jù)
基于 Session 的方式存在多種問(wèn)題。
- 服務(wù)端需要存儲(chǔ) Session,并且由于 Session 需要經(jīng)常快速查找,通常存儲(chǔ)在內(nèi)存或內(nèi)存數(shù)據(jù)庫(kù)中,同時(shí)在線用戶較多時(shí)需要占用大量的服務(wù)器資源。
- 當(dāng)需要擴(kuò)展時(shí),創(chuàng)建 Session 的服務(wù)器可能不是驗(yàn)證 Session 的服務(wù)器,所以還需要將所有 Session 單獨(dú)存儲(chǔ)并共享。
- 由于客戶端使用 Cookie 存儲(chǔ) SessionID,在跨域場(chǎng)景下需要進(jìn)行兼容性處理,同時(shí)這種方式也難以防范 CSRF 攻擊。
基于 Token 的會(huì)話管理
鑒于基于 Session 的會(huì)話管理方式存在上述多個(gè)缺點(diǎn),無(wú)狀態(tài)的基于 Token 的會(huì)話管理方式誕生了,所謂無(wú)狀態(tài),就是服務(wù)端不再存儲(chǔ)信息,甚至是不再存儲(chǔ) Session,邏輯如下。
- 客戶端使用用戶名密碼進(jìn)行認(rèn)證
- 服務(wù)端驗(yàn)證用戶名密碼,通過(guò)后生成 Token 返回給客戶端
- 客戶端保存 Token,訪問(wèn)需要認(rèn)證的接口時(shí)在 URL 參數(shù)或 HTTP Header 中加入 Token
- 服務(wù)端通過(guò)解碼 Token 進(jìn)行鑒權(quán),返回給客戶端需要的數(shù)據(jù)
基于 Token 的會(huì)話管理方式有效解決了基于 Session 的會(huì)話管理方式帶來(lái)的問(wèn)題。
- 服務(wù)端不需要存儲(chǔ)和用戶鑒權(quán)有關(guān)的信息,鑒權(quán)信息會(huì)被加密到 Token 中,服務(wù)端只需要讀取 Token 中包含的鑒權(quán)信息即可
- 避免了共享 Session 導(dǎo)致的不易擴(kuò)展問(wèn)題
- 不需要依賴 Cookie,有效避免 Cookie 帶來(lái)的 CSRF 攻擊問(wèn)題
- 使用 CORS 可以快速解決跨域問(wèn)題
JWT 介紹
JWT 是 JSON Web Token 的縮寫,JWT 本身沒(méi)有定義任何技術(shù)實(shí)現(xiàn),它只是定義了一種基于 Token 的會(huì)話管理的規(guī)則,涵蓋 Token 需要包含的標(biāo)準(zhǔn)內(nèi)容和 Token 的生成過(guò)程。
一個(gè) JWT Token 長(zhǎng)這樣。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDQ1MTE3NDMsImp0aSI6IjYxYmVmNjkyLTE4M2ItNGYxYy1hZjE1LWUwMDM0MTczNzkxOSJ9.CZzB2-JI1oPRFxNMaoFz9-9cKGTYVXkOC2INMoEYNNA
仔細(xì)辨別會(huì)發(fā)現(xiàn)它由 A.B.C
三部分組成,這三部分依次是頭部(Header)、負(fù)載(Payload)、簽名(Signature),頭部和負(fù)載以 JSON 形式存在,這就是 JWT 中的 JSON,三部分的內(nèi)容都分別單獨(dú)經(jīng)過(guò)了 Base64 編碼,以 .
拼接成一個(gè) JWT Token。
JWT 的 Header 中存儲(chǔ)了所使用的加密算法和 Token 類型。
{
"alg": "HS256",
"typ": "JWT"
}
Payload 是負(fù)載,JWT 規(guī)范規(guī)定了一些字段,并推薦使用,開發(fā)者也可以自己指定字段和內(nèi)容,例如下面的內(nèi)容。
{
username: 'yage',
email: 'sa@simpleapples.com',
role: 'user',
exp: 1544602234
}
需要注意的是,Payload的內(nèi)容只經(jīng)過(guò)了 Base64 編碼,對(duì)客戶端來(lái)說(shuō)當(dāng)于明文存儲(chǔ),所以不要放置敏感信息。
Signature 部分用來(lái)驗(yàn)證 JWT Token 是否被篡改,所以這部分會(huì)使用一個(gè) Secret 將前兩部分加密,邏輯如下。
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
JWT 優(yōu)勢(shì) & 問(wèn)題
JWT 擁有基于 Token 的會(huì)話管理方式所擁有的一切優(yōu)勢(shì),不依賴 Cookie,使得其可以防止 CSRF 攻擊,也能在禁用 Cookie 的瀏覽器環(huán)境中正常運(yùn)行。
而 JWT 的最大優(yōu)勢(shì)是服務(wù)端不再需要存儲(chǔ) Session,使得服務(wù)端認(rèn)證鑒權(quán)業(yè)務(wù)可以方便擴(kuò)展,避免存儲(chǔ) Session 所需要引入的 Redis 等組件,降低了系統(tǒng)架構(gòu)復(fù)雜度。但這也是 JWT 最大的劣勢(shì),由于有效期存儲(chǔ)在 Token 中,JWT Token 一旦簽發(fā),就會(huì)在有效期內(nèi)一直可用,無(wú)法在服務(wù)端廢止,當(dāng)用戶進(jìn)行登出操作,只能依賴客戶端刪除掉本地存儲(chǔ)的 JWT Token,如果需要禁用用戶,單純使用 JWT 就無(wú)法做到了。
基于 JWT 的實(shí)踐
既然 JWT 依然存在諸多問(wèn)題,甚至無(wú)法滿足一些業(yè)務(wù)上的需求,但是我們依然可以基于 JWT 在實(shí)踐中進(jìn)行一些改進(jìn),來(lái)形成一個(gè)折中的方案,畢竟,在用戶會(huì)話管理場(chǎng)景下,沒(méi)有銀彈。
前面講的 Token,都是 Access Token,也就是訪問(wèn)資源接口時(shí)所需要的 Token,還有另外一種 Token,Refresh Token,通常情況下,Refresh Token 的有效期會(huì)比較長(zhǎng),而 Access Token 的有效期比較短,當(dāng) Access Token 由于過(guò)期而失效時(shí),使用 Refresh Token 就可以獲取到新的 Access Token,如果 Refresh Token 也失效了,用戶就只能重新登錄了。
在 JWT 的實(shí)踐中,引入 Refresh Token,將會(huì)話管理流程改進(jìn)如下。
- 客戶端使用用戶名密碼進(jìn)行認(rèn)證
- 服務(wù)端生成有效時(shí)間較短的 Access Token(例如 10 分鐘),和有效時(shí)間較長(zhǎng)的 Refresh Token(例如 7 天)
- 客戶端訪問(wèn)需要認(rèn)證的接口時(shí),攜帶 Access Token
- 如果 Access Token 沒(méi)有過(guò)期,服務(wù)端鑒權(quán)后返回給客戶端需要的數(shù)據(jù)
- 如果攜帶 Access Token 訪問(wèn)需要認(rèn)證的接口時(shí)鑒權(quán)失敗(例如返回 401 錯(cuò)誤),則客戶端使用 Refresh Token 向刷新接口申請(qǐng)新的 Access Token
- 如果 Refresh Token 沒(méi)有過(guò)期,服務(wù)端向客戶端下發(fā)新的 Access Token
- 客戶端使用新的 Access Token 訪問(wèn)需要認(rèn)證的接口
將生成的 Refresh Token 以及過(guò)期時(shí)間存儲(chǔ)在服務(wù)端的數(shù)據(jù)庫(kù)中,由于 Refresh Token 不會(huì)在客戶端請(qǐng)求業(yè)務(wù)接口時(shí)驗(yàn)證,只有在申請(qǐng)新的 Access Token 時(shí)才會(huì)驗(yàn)證,所以將 Refresh Token 存儲(chǔ)在數(shù)據(jù)庫(kù)中,不會(huì)對(duì)業(yè)務(wù)接口的響應(yīng)時(shí)間造成影響,也不需要像 Session 一樣一直保持在內(nèi)存中以應(yīng)對(duì)大量的請(qǐng)求。
上述的架構(gòu),提供了服務(wù)端禁用用戶 Token 的方式,當(dāng)用戶需要登出或禁用用戶時(shí),只需要將服務(wù)端的 Refresh Token 禁用或刪除,用戶就會(huì)在 Access Token 過(guò)期后,由于無(wú)法獲取到新的 Access Token 而再也無(wú)法訪問(wèn)需要認(rèn)證的接口。這樣的方式雖然會(huì)有一定的窗口期(取決于 Access Token 的失效時(shí)間),但是結(jié)合用戶登出時(shí)客戶端刪除 Access Token 的操作,基本上可以適應(yīng)常規(guī)情況下對(duì)用戶認(rèn)證鑒權(quán)的精度要求。
總結(jié)
JWT 的使用,提高了開發(fā)者開發(fā)用戶認(rèn)證鑒權(quán)功能的效率,降低了系統(tǒng)架構(gòu)復(fù)雜度,避免了大量的數(shù)據(jù)庫(kù)和緩存查詢,降低了業(yè)務(wù)接口的響應(yīng)延遲。然而 JWT 的這些優(yōu)點(diǎn)也增加了 Token 管理上的難度,通過(guò)引入 Refresh Token,既能繼續(xù)使用 JWT 所帶來(lái)的優(yōu)勢(shì),又能使得 Token 管理的精度符合業(yè)務(wù)的需求。
作者:simpleapples
鏈接:http://www.lxweimin.com/p/25ab2f456904
來(lái)源:簡(jiǎn)書
簡(jiǎn)書著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請(qǐng)聯(lián)系作者獲得授權(quán)并注明出處。