退出登錄時如何借助外力使JWT令牌失效?
文章的目錄如下:
圖片
解決方案
JWT最大的一個優(yōu)勢在于它是無狀態(tài)的,自身包含了認(rèn)證鑒權(quán)所需要的所有信息,服務(wù)器端無需對其存儲,從而給服務(wù)器減少了存儲開銷。
但是無狀態(tài)引出的問題也是可想而知的,它無法作廢未過期的JWT。舉例說明注銷場景下,就傳統(tǒng)的cookie/session認(rèn)證機(jī)制,只需要把存在服務(wù)器端的session刪掉就OK了。
但是JWT呢,它是不存在服務(wù)器端的啊,好的那我刪存在客戶端的JWT行了吧。額,社會本就復(fù)雜別再欺騙自己了好么,被你在客戶端刪掉的JWT還是可以通過服務(wù)器端認(rèn)證的。
使用JWT要非常明確的一點:JWT失效的唯一途徑就是等待時間過期。
但是可以借助外力保存JWT的狀態(tài),這時就有人問了:你這不是打臉嗎?用JWT就因為它的無狀態(tài)性,這時候又要保存它的狀態(tài)?
其實不然,這不被逼上梁山了嗎?不使用外力保存JWT的狀態(tài),你說如何實現(xiàn)注銷失效?
常用的方案有兩種,白名單和黑名單方式。
1、白名單
白名單的邏輯很簡單:認(rèn)證通過時,將JWT存入redis中,注銷時,將JWT從redis中移出。這種方式和cookie/session的方式大同小異。
2、黑名單
黑名單的邏輯也非常簡單:注銷時,將JWT放入redis中,并且設(shè)置過期時間為JWT的過期時間;請求資源時判斷該JWT是否在redis中,如果存在則拒絕訪問。
白名單和黑名單這兩種方案都比較好實現(xiàn),但是黑名單帶給服務(wù)器的壓力遠(yuǎn)遠(yuǎn)小于白名單,畢竟注銷不是經(jīng)常性操作。
黑名單方式實現(xiàn)
下面以黑名單的方式介紹一下如何在網(wǎng)關(guān)層面實現(xiàn)JWT的注銷失效。
究竟向Redis中存儲什么?
如果直接存儲JWT令牌可行嗎?當(dāng)然可行,不過JWT令牌可是很長的哦,這樣對內(nèi)存的要求也是挺高的。
熟悉JWT令牌的都知道,JWT令牌中有一個jti字段,這個字段可以說是JWT令牌的唯一ID了,如下:
圖片
因此可以將這個jti字段存入redis中,作為唯一令牌標(biāo)識,這樣一來是不是節(jié)省了很多的內(nèi)存?
如何實現(xiàn)呢? 分為兩步:
- 網(wǎng)關(guān)層的全局過濾器中需要判斷黑名單是否存在當(dāng)前JWT
- 注銷接口中將JWT的jti字段作為key存放到redis中,且設(shè)置了JWT的過期時間
1、網(wǎng)關(guān)層解析JWT的jti、過期時間放入請求頭中
在網(wǎng)關(guān)的全局過濾器GlobalAuthenticationFilter中直接從令牌中解析出jti和過期時間。
這里的邏輯分為如下步驟:
- 解析JWT令牌的jti和過期時間
- 根據(jù)jti從redis中查詢是否存在黑名單中,如果存在則直接攔截,否則放行
- 將解析的jti和過期時間封裝到JSON中,傳遞給下游微服務(wù)
關(guān)鍵代碼如下:
圖片
2、下游微服務(wù)的過濾器修改
AuthenticationFilter這個過濾器用來解密網(wǎng)關(guān)層傳遞的JSON數(shù)據(jù),并將其封裝到Request中,這樣在業(yè)務(wù)方法中便可以隨時獲取到想要的用戶信息。
這里我是把JWT相關(guān)的信息同時封裝到了Request中,實體類為JwtInformation,如下:
圖片
LoginVal繼承了JwtInformation,如下:
圖片
此時AuthenticationFilter這個過濾器修改起來就很簡單了,只需要將jti和過期時間封裝到LoginVal中即可,關(guān)鍵代碼如下:
圖片
邏輯很簡單,上圖都有標(biāo)注。
3、注銷接口實現(xiàn)
之前文章中并沒有提供注銷接口,因為無狀態(tài)的JWT根本不需要退出登錄,傻等著過期唄。
當(dāng)然為了實現(xiàn)注銷登錄,借助了Redis,那么注銷接口必不可少了。
邏輯很簡單,直接將退出登錄的JWT令牌的jti設(shè)置到Redis中,過期時間設(shè)置為JWT過期時間即可。代碼如下:
圖片
OK了,至此已經(jīng)實現(xiàn)了JWT注銷登錄的功能.......
涉及到的三個模塊的改動,分別如下:
名稱 | 功能 |
oauth2-cloud-auth-server | OAuth2.0認(rèn)證授權(quán)服 |
oauth2-cloud-gateway | 網(wǎng)關(guān)服務(wù) |
oauth2-cloud-auth-common | 公共模塊 |
圖片
總結(jié)
思想很簡單,JWT既然是無狀態(tài)的,只能借助Redis記錄它的狀態(tài),這樣才能達(dá)到使其失效的目的。
測試
業(yè)務(wù)基本完成了,下面走一個流程測試一下,如下:
1、登錄,申請令牌
圖片
2、拿著令牌訪問接口
該令牌并沒有注銷,因此可以正常訪問,如下:
圖片
3、調(diào)用接口注銷登錄
請求如下:
圖片
4、拿著注銷的令牌訪問接口
由于令牌已經(jīng)注銷了,因此肯定訪問不通接口,返回如下: