之前分享了一個(gè)使用 Spring Security 實(shí)現(xiàn) JWT 身份認(rèn)證的 Demo况褪,文章地址:適合初學(xué)者入門 Spring Security With JWT 的 Demo。 Demo 非常簡(jiǎn)單耙旦,沒(méi)有介紹到 JWT 存在的一些問(wèn)題。所以沃饶,單獨(dú)抽了一篇文章出來(lái)介紹母廷。為了完成這篇文章,我查閱了很多資料和文獻(xiàn)糊肤,我覺(jué)得應(yīng)該對(duì)大家有幫助琴昆。
相關(guān)閱讀:
- 《一問(wèn)帶你區(qū)分清楚Authentication,Authorization以及Cookie、Session馆揉、Token》
- 適合初學(xué)者入門 Spring Security With JWT 的 Demo
- Spring Boot 使用 JWT 進(jìn)行身份和權(quán)限驗(yàn)證
Token 認(rèn)證的優(yōu)勢(shì)
相比于 Session 認(rèn)證的方式來(lái)說(shuō)业舍,使用 token 進(jìn)行身份認(rèn)證主要有下面三個(gè)優(yōu)勢(shì):
1.無(wú)狀態(tài)
token 自身包含了身份驗(yàn)證所需要的所有信息,使得我們的服務(wù)器不需要存儲(chǔ) Session 信息升酣,這顯然增加了系統(tǒng)的可用性和伸縮性舷暮,大大減輕了服務(wù)端的壓力。但是噩茄,也正是由于 token 的無(wú)狀態(tài)下面,也導(dǎo)致了它最大的缺點(diǎn):當(dāng)后端在token 有效期內(nèi)廢棄一個(gè) token 或者更改它的權(quán)限的話,不會(huì)立即生效绩聘,一般需要等到有效期過(guò)后才可以沥割。另外,當(dāng)用戶 Logout 的話凿菩,token 也還有效机杜。除非,我們?cè)诤蠖嗽黾宇~外的處理邏輯衅谷。
2.有效避免了CSRF 攻擊
CSRF(Cross Site Request Forgery)一般被翻譯為 跨站請(qǐng)求偽造椒拗,屬于網(wǎng)絡(luò)攻擊領(lǐng)域范圍。相比于 SQL 腳本注入、XSS等等安全攻擊方式蚀苛,CSRF 的知名度并沒(méi)有它們高在验。但是,它的確是每個(gè)系統(tǒng)都要考慮的安全隱患,就連技術(shù)帝國(guó) Google 的 Gmail 在早些年也被曝出過(guò)存在 CSRF 漏洞枉阵,這給 Gmail 的用戶造成了很大的損失译红。
那么究竟什么是 跨站請(qǐng)求偽造 呢?說(shuō)簡(jiǎn)單用你的身份去發(fā)送一些對(duì)你不友好的請(qǐng)求兴溜。舉個(gè)簡(jiǎn)單的例子:
小壯登錄了某網(wǎng)上銀行侦厚,他來(lái)到了網(wǎng)上銀行的帖子區(qū),看到一個(gè)帖子下面有一個(gè)鏈接寫著“科學(xué)理財(cái)拙徽,年盈利率過(guò)萬(wàn)”刨沦,小壯好奇的點(diǎn)開(kāi)了這個(gè)鏈接,結(jié)果發(fā)現(xiàn)自己的賬戶少了10000元膘怕。這是這么回事呢想诅?原來(lái)黑客在鏈接中藏了一個(gè)請(qǐng)求,這個(gè)請(qǐng)求直接利用小壯的身份給銀行發(fā)送了一個(gè)轉(zhuǎn)賬請(qǐng)求,也就是通過(guò)你的 Cookie 向銀行發(fā)出請(qǐng)求岛心。
<a src="http://www.mybank.com/Transfer?bankId=11&money=10000">科學(xué)理財(cái)来破,年盈利率過(guò)萬(wàn)</a>
導(dǎo)致這個(gè)問(wèn)題很大的原因就是: Session 認(rèn)證中 Cookie 中的 session_id 是由瀏覽器發(fā)送到服務(wù)端的,借助這個(gè)特性忘古,攻擊者就可以通過(guò)讓用戶誤點(diǎn)攻擊鏈接徘禁,達(dá)到攻擊效果。
那為什么 token 不會(huì)存在這種問(wèn)題呢髓堪?
我是這樣理解的:一般情況下我們使用 JWT 的話送朱,在我們登錄成功獲得 token 之后,一般會(huì)選擇存放在 local storage 中干旁。然后我們?cè)谇岸送ㄟ^(guò)某些方式會(huì)給每個(gè)發(fā)到后端的請(qǐng)求加上這個(gè) token,這樣就不會(huì)出現(xiàn) CSRF 漏洞的問(wèn)題驶沼。因?yàn)椋词褂袀€(gè)你點(diǎn)擊了非法鏈接發(fā)送了請(qǐng)求到服務(wù)端争群,這個(gè)非法請(qǐng)求是不會(huì)攜帶 token 的回怜,所以這個(gè)請(qǐng)求將是非法的。
但是這樣會(huì)存在 XSS 攻擊中被盜的風(fēng)險(xiǎn)换薄,為了避免 XSS 攻擊玉雾,你可以選擇將 token 存儲(chǔ)在標(biāo)記為httpOnly
的cookie 中。但是专控,這樣又導(dǎo)致了你必須自己提供CSRF保護(hù)抹凳。
具體采用上面哪兩種方式存儲(chǔ) token 呢遏餐,大部分情況下存放在 local storage 下都是最好的選擇伦腐,某些情況下可能需要存放在標(biāo)記為httpOnly
的cookie 中會(huì)更好。
3.適合移動(dòng)端應(yīng)用
使用 Session 進(jìn)行身份認(rèn)證的話失都,需要保存一份信息在服務(wù)器端柏蘑,而且這種方式會(huì)依賴到 Cookie(需要 Cookie 保存 SessionId)幸冻,所以不適合移動(dòng)端。
但是咳焚,使用 token 進(jìn)行身份認(rèn)證就不會(huì)存在這種問(wèn)題洽损,因?yàn)橹灰?token 可以被客戶端存儲(chǔ)就能夠使用,而且 token 還可以跨語(yǔ)言使用革半。
4.單點(diǎn)登錄友好
使用 Session 進(jìn)行身份認(rèn)證的話碑定,實(shí)現(xiàn)單點(diǎn)登錄,需要我們把用戶的 Session 信息保存在一臺(tái)電腦上又官,并且還會(huì)遇到常見(jiàn)的 Cookie 跨域的問(wèn)題延刘。但是,使用 token 進(jìn)行認(rèn)證的話六敬, token 被保存在客戶端碘赖,不會(huì)存在這些問(wèn)題。
Token 認(rèn)證常見(jiàn)問(wèn)題以及解決辦法
1.注銷登錄等場(chǎng)景下 token 還有效
與之類似的具體相關(guān)場(chǎng)景有:
- 退出登錄;
- 修改密碼;
- 服務(wù)端修改了某個(gè)用戶具有的權(quán)限或者角色外构;
- 用戶的帳戶被刪除/暫停普泡。
- 用戶由管理員注銷;
這個(gè)問(wèn)題不存在于 Session 認(rèn)證方式中审编,因?yàn)樵?Session 認(rèn)證方式中撼班,遇到這種情況的話服務(wù)端刪除對(duì)應(yīng)的 Session 記錄即可。但是割笙,使用 token 認(rèn)證的方式就不好解決了权烧。我們也說(shuō)過(guò)了,token 一旦派發(fā)出去伤溉,如果后端不增加其他邏輯的話般码,它在失效之前都是有效的。那么乱顾,我們?nèi)绾谓鉀Q這個(gè)問(wèn)題呢板祝?查閱了很多資料,總結(jié)了下面幾種方案:
- 將 token 存入內(nèi)存數(shù)據(jù)庫(kù):將 token 存入 DB 中走净,redis 內(nèi)存數(shù)據(jù)庫(kù)在這里是是不錯(cuò)的選擇券时。如果需要讓某個(gè) token 失效就直接從 redis 中刪除這個(gè) token 即可。但是伏伯,這樣會(huì)導(dǎo)致每次使用 token 發(fā)送請(qǐng)求都要先從 DB 中查詢 token 是否存在的步驟橘洞,而且違背了 JWT 的無(wú)狀態(tài)原則。
- 黑名單機(jī)制:和上面的方式類似说搅,使用內(nèi)存數(shù)據(jù)庫(kù)比如 redis 維護(hù)一個(gè)黑名單炸枣,如果想讓某個(gè) token 失效的話就直接將這個(gè) token 加入到 黑名單 即可。然后,每次使用 token 進(jìn)行請(qǐng)求的話都會(huì)先判斷這個(gè) token 是否存在于黑名單中适肠。
- 修改密鑰 (Secret) : 我們?yōu)槊總€(gè)用戶都創(chuàng)建一個(gè)專屬密鑰霍衫,如果我們想讓某個(gè) token 失效,我們直接修改對(duì)應(yīng)用戶的密鑰即可侯养。但是敦跌,這樣相比于前兩種引入內(nèi)存數(shù)據(jù)庫(kù)帶來(lái)了危害更大,比如: ① 如果服務(wù)是分布式的逛揩,則每次發(fā)出新的 token 時(shí)都必須在多臺(tái)機(jī)器同步密鑰柠傍。為此,你需要將必須將機(jī)密存儲(chǔ)在數(shù)據(jù)庫(kù)或其他外部服務(wù)中辩稽,這樣和 Session 認(rèn)證就沒(méi)太大區(qū)別了携兵。② 如果用戶同時(shí)在兩個(gè)瀏覽器打開(kāi)系統(tǒng),或者在手機(jī)端也打開(kāi)了系統(tǒng)搂誉,如果它從一個(gè)地方將賬號(hào)退出徐紧,那么其他地方都要重新進(jìn)行登錄,這是不可取的炭懊。
- 保持令牌的有效期限短并經(jīng)常輪換 :很簡(jiǎn)單的一種方式并级。但是,會(huì)導(dǎo)致用戶登錄狀態(tài)不會(huì)被持久記錄侮腹,而且需要用戶經(jīng)常登錄嘲碧。
對(duì)于修改密碼后 token 還有效問(wèn)題的解決還是比較容易的,說(shuō)一種我覺(jué)得比較好的方式:使用用戶的密碼的哈希值對(duì) token 進(jìn)行簽名父阻。因此愈涩,如果密碼更改,則任何先前的令牌將自動(dòng)無(wú)法驗(yàn)證加矛。
2.token 的續(xù)簽問(wèn)題
token 有效期一般都建議設(shè)置的不太長(zhǎng)履婉,那么 token 過(guò)期后如何認(rèn)證,如何實(shí)現(xiàn)動(dòng)態(tài)刷新 token斟览,避免用戶經(jīng)常需要重新登錄毁腿?
我們先來(lái)看看在 Session 認(rèn)證中一般的做法:假如 session 的有效期30分鐘,如果 30 分鐘內(nèi)用戶有訪問(wèn)苛茂,就把 session 有效期被延長(zhǎng)30分鐘已烤。
- 類似于 Session 認(rèn)證中的做法:這種方案滿足于大部分場(chǎng)景。假設(shè)服務(wù)端給的 token 有效期設(shè)置為30分鐘妓羊,服務(wù)端每次進(jìn)行校驗(yàn)時(shí)胯究,如果發(fā)現(xiàn) token 的有效期馬上快過(guò)期了,服務(wù)端就重新生成 token 給客戶端躁绸≡Q客戶端每次請(qǐng)求都檢查新舊token丙猬,如果不一致,則更新本地的token费韭。這種做法的問(wèn)題是僅僅在快過(guò)期的時(shí)候請(qǐng)求才會(huì)更新 token ,對(duì)客戶端不是很友好。
- 每次請(qǐng)求都返回新 token :這種方案的的思路很簡(jiǎn)單庭瑰,但是星持,很明顯,開(kāi)銷會(huì)比較大弹灭。
- token 有效期設(shè)置到半夜 :這種方案是一種折衷的方案督暂,保證了大部分用戶白天可以正常登錄,適用于對(duì)安全性要求不高的系統(tǒng)穷吮。
- 用戶登錄返回兩個(gè) token :第一個(gè)是 acessToken 逻翁,它的過(guò)期時(shí)間 token 本身的過(guò)期時(shí)間比如半個(gè)小時(shí),另外一個(gè)是 refreshToken 它的過(guò)期時(shí)間更長(zhǎng)一點(diǎn)比如為1天捡鱼“嘶兀客戶端登錄后,將 accessToken和refreshToken 保存在本地驾诈,每次訪問(wèn)將 accessToken 傳給服務(wù)端缠诅。服務(wù)端校驗(yàn) accessToken 的有效性,如果過(guò)期的話乍迄,就將 refreshToken 傳給服務(wù)端管引。如果有效,服務(wù)端就生成新的 accessToken 給客戶端闯两。否則褥伴,客戶端就重新登錄即可。該方案的不足是:① ?需要客戶端來(lái)配合漾狼;② ?用戶注銷的時(shí)候需要同時(shí)保證兩個(gè) token 都無(wú)效重慢;③ 重新請(qǐng)求獲取 token 的過(guò)程中會(huì)有短暫 token 不可用的情況(可以通過(guò)在客戶端設(shè)置定時(shí)器,當(dāng)accessToken 快過(guò)期的時(shí)候逊躁,提前去通過(guò) refreshToken 獲取新的accessToken)伤锚。
總結(jié)
JWT 最適合的場(chǎng)景是不需要服務(wù)端保存用戶狀態(tài)的場(chǎng)景,比如如果考慮到 token 注銷和 token 續(xù)簽的場(chǎng)景話志衣,沒(méi)有特別好的解決方案屯援,大部分解決方案都給 token 加上了狀態(tài),這就有點(diǎn)類似 Session 認(rèn)證了念脯。
Reference
- JWT 超詳細(xì)分析
- https://medium.com/devgorilla/how-to-log-out-when-using-jwt-a8c7823e8a6
- https://medium.com/@agungsantoso/csrf-protection-with-json-web-tokens-83e0f2fcbcc
- Invalidating JSON Web Tokens
作者:Snailclimb
鏈接:JWT 身份認(rèn)證優(yōu)缺點(diǎn)分析以及常見(jiàn)問(wèn)題解決方案
來(lái)源:github