JWT 使用
前面簡(jiǎn)單介紹了把默認(rèn)的頁面登錄改為前后端分離的接口異步登錄的方法怖亭,可以幫我們實(shí)現(xiàn)基本的前后端分離登錄功能涎显。但是這種基本的登錄和前面的頁面登錄還有一個(gè)一樣的地方,就是使用session和cookie來維護(hù)登錄狀態(tài)兴猩,這種方法的問題在于期吓,擴(kuò)展性不好。單機(jī)當(dāng)然沒有問題倾芝,如果是服務(wù)器集群讨勤,或者是跨域的服務(wù)導(dǎo)向架構(gòu),就要求 session 數(shù)據(jù)共享晨另,每臺(tái)服務(wù)器都能夠讀取 session潭千。
一種解決方案是 session 數(shù)據(jù)持久化,寫入redis或別的持久層借尿。各種服務(wù)收到請(qǐng)求后刨晴,都向持久層請(qǐng)求數(shù)據(jù)屉来。這種方案的優(yōu)點(diǎn)是架構(gòu)清晰,缺點(diǎn)是工程量比較大狈癞。另外茄靠,持久層萬一掛了,就會(huì)單點(diǎn)失敗蝶桶。
另一種方案是服務(wù)器索性不保存 session 數(shù)據(jù)了嘹黔,所有數(shù)據(jù)都保存在客戶端,每次請(qǐng)求都發(fā)回服務(wù)器莫瞬。JWT 就是這種方案的一個(gè)代表儡蔓。關(guān)于JWT的理論知識(shí),建議參考 阮一峰 大神寫的教程 :JSON Web Token 入門教程疼邀,這是我認(rèn)為可能是寫的最清晰的一個(gè)喂江,下面的jwt的實(shí)現(xiàn)也是根據(jù)此教程來實(shí)現(xiàn)。
具體的理論知識(shí)可以參考教程旁振,這里簡(jiǎn)單說下流程获询,用戶登錄成功后,在header中返回用戶一個(gè)token信息拐袜,這個(gè)信息里面包含了加密的用戶信息和數(shù)字簽名吉嚣,最重要的還有過期時(shí)間,客戶端接到后蹬铺,每次訪問接口header中都帶著這個(gè)token尝哆,服務(wù)端驗(yàn)證成功后就表示處于登錄狀態(tài),過期后再從新獲取即可甜攀。
具體的token內(nèi)容包含了頭部(加密信息)秋泄,載體(用戶信息),簽名(簽名兩個(gè)部分的前面)三大塊规阀,三大塊之間用英文句號(hào)(也就是 ".")連接起來恒序,組成一個(gè)完整的token信息
流程設(shè)計(jì)
根據(jù)前面的理論知識(shí),我們來設(shè)計(jì)一下如何使用jwt谁撼。首先我們使用jwt歧胁,就可以不再使用session和cookie,所以第一步就是:
- 在security配置文件中配置session為無狀態(tài)厉碟。
然后考慮構(gòu)建jwt消息體喊巍,有三個(gè)部分,第一個(gè)部分就是頭部墨榄,內(nèi)容是加密類型:
上面代碼中玄糟,alg屬性表示簽名的算法(algorithm),默認(rèn)是 HMAC SHA256(寫成 HS256)袄秩;typ屬性表示這個(gè)令牌(token)的類型(type)阵翎,JWT 令牌統(tǒng)一寫為JWT逢并,最后,將上面的 JSON 對(duì)象使用 Base64URL 算法轉(zhuǎn)成字符串郭卫,作為第一部分砍聊。所以第二步就是:
- 在security配置文件中配置session為無狀態(tài)。
- 確定header信息格式
下一步確定第二部分贰军,消息載體(Payload)玻蝌,這也是一個(gè)json對(duì)象,用來存放實(shí)際需要傳遞的數(shù)據(jù)词疼。JWT 規(guī)定了7個(gè)官方字段俯树,供選用:
當(dāng)然除了這些還可以加一些其它內(nèi)容,比如用戶信息贰盗,這個(gè) JSON 對(duì)象也要使用 Base64URL 算法轉(zhuǎn)成字符串许饿,所以第三步和第四步就是:
- 在security配置文件中配置session為無狀態(tài)。
- 確定header信息格式
- 確定消息體
- 使用 HMAC SHA256 算法 對(duì)header和消息體進(jìn)行簽名作為第三部分
現(xiàn)在token的消息基本組合完成了舵盈,用戶登錄成功和客戶端訪問接口陋率,都要把token放在header里面,名字是 Authorization 秽晚。所以最后一步就是瓦糟,客戶端正常訪問非登錄等接口時(shí),驗(yàn)證token的合法性赴蝇,所以菩浙,總體設(shè)計(jì)流程如下:
- 在security配置文件中配置session為無狀態(tài)。
- 確定header信息格式
- 確定消息體
- 使用 HMAC SHA256 算法 對(duì)header和消息體進(jìn)行簽名作為第三部分
- 添加過濾器扯再,驗(yàn)證token合法性
修改配置類
上面的流程設(shè)計(jì)完了芍耘,下面我們按照流程修改項(xiàng)目,首先修改security配置類:
配置完后熄阻,啟動(dòng)項(xiàng)目,訪問登錄倔约,登錄成功后可以看到秃殉,沒有任何cookie保存下來。
定義JWT工具類
首先來定義幾個(gè)常量:
然后定義Base64URL 算法編碼和解碼方法:
然后定義HmacSHA256 加密算法和獲取簽名的方法:
最后來設(shè)計(jì)一個(gè)簡(jiǎn)單驗(yàn)證token的方法:
這樣jwt工具類就設(shè)計(jì)好了浸剩,目前這幾個(gè)方法足夠操作token內(nèi)容钾军。
定義JWT消息對(duì)象
下面來定義jwt的內(nèi)容,其實(shí)內(nèi)容很簡(jiǎn)單绢要,就三個(gè)部分吏恭,因此,定義三個(gè)字段即可:
來看一下構(gòu)造方法重罪,
這個(gè)構(gòu)造方法很便捷樱哼,使用它創(chuàng)建對(duì)象以后哀九,jwt的三個(gè)部分基本都完成了,header部分和payload部分都編碼了搅幅,簽名也完成了阅束,因此下面重寫toString方法直接可以生成token:
從這里可以看出,token整體默認(rèn)是不加密茄唐,但也是可以加密的息裸。生成原始 Token 以后,可以用密鑰再加密一次沪编。因此不要把密碼等重要信息放入token呼盆。
修改登錄成功處理器
用戶登錄成功后,不再把session發(fā)給用戶蚁廓,而是把jwt發(fā)送給用戶宿亡,因此修改登錄成功處理器如下:
注意上面手動(dòng)把用戶的密碼信息設(shè)置為null。這里為了方便纳令,直接使用fastjson組合對(duì)象挽荠。
修改實(shí)體類
帶著token訪問接口的時(shí)候,需要把token轉(zhuǎn)回登錄用戶對(duì)象平绩,因此我們的用戶實(shí)體類和token中帶的字段名字一致圈匆,來修改一下,先看角色實(shí)體類:
再看用戶實(shí)體類:
可以看到捏雌,基本的原則就是修改的名字和父類的必要字段名字一致就行跃赚,這也是建議的字段名字。
編寫token驗(yàn)證過濾器
我們把security的session改為無狀態(tài)后性湿,雖然不再傳遞session纬傲,但是security的過濾器并沒有失效,因此造成的效果就是登錄成功后肤频,訪問接口顯示未登錄≈才郏現(xiàn)在我們使用token就要在登錄前加一個(gè)驗(yàn)證token的過濾器饲漾,驗(yàn)證通過后直接把信息放到SecurityContextHolder中。這樣每次登錄靠驗(yàn)證token來判斷是否登錄,不再靠session或链。來看這個(gè)過濾器:
這個(gè)過濾器很簡(jiǎn)單隙畜,繼承了 GenericFilterBean 類蒜埋,直接獲取token玖详,判斷token不為空,驗(yàn)證token暑刃,并從token的payload中取出用戶信息厢漩,放入SecurityContextHolder中,驗(yàn)證失敗或者token過期直接返回token錯(cuò)誤岩臣。邏輯很簡(jiǎn)單溜嗜。
最后在security類中宵膨,把這個(gè)過濾器配置到前面:
這樣我們自定義的jwt流程就完成了×皇ぃ可以在postman中測(cè)試一下柄驻,首先是登錄:
登錄成功后,可以看到header中放著token的信息焙压,然后使用token放入另一個(gè)接口的header中訪問接口鸿脓,可以看到訪問成功:
有興趣的可以debug跟蹤一下流程。
JWT的幾個(gè)特點(diǎn)
(1)JWT 默認(rèn)是不加密涯曲,但也是可以加密的野哭。生成原始 Token 以后,可以用密鑰再加密一次幻件。
(2)JWT 不加密的情況下拨黔,不能將秘密數(shù)據(jù)寫入 JWT。
(3)JWT 不僅可以用于認(rèn)證绰沥,也可以用于交換信息篱蝇。有效使用 JWT,可以降低服務(wù)器查詢數(shù)據(jù)庫的次數(shù)徽曲。
(4)JWT 的最大缺點(diǎn)是零截,由于服務(wù)器不保存 session 狀態(tài),因此無法在使用過程中廢止某個(gè) token秃臣,或者更改 token 的權(quán)限涧衙。也就是說,一旦 JWT 簽發(fā)了奥此,在到期之前就會(huì)始終有效弧哎,除非服務(wù)器部署額外的邏輯。
(5)JWT 本身包含了認(rèn)證信息稚虎,一旦泄露撤嫩,任何人都可以獲得該令牌的所有權(quán)限。為了減少盜用祥绞,JWT 的有效期應(yīng)該設(shè)置得比較短非洲。對(duì)于一些比較重要的權(quán)限,使用時(shí)應(yīng)該再次對(duì)用戶進(jìn)行認(rèn)證蜕径。
(6)為了減少盜用,JWT 不應(yīng)該使用 HTTP 協(xié)議明碼傳輸败京,要使用 HTTPS 協(xié)議傳輸兜喻。