繼續(xù)這一個(gè)系列,基于Token的WEB后臺(tái)登錄認(rèn)證機(jī)制(并講解cookie和session機(jī)制)。每個(gè)后端不得不解決的認(rèn)證問(wèn)題。
本系列:
(一)J2EE項(xiàng)目系列(三)--Spring Data JPA+Spring+SpringMVC+Maven快速開(kāi)發(fā)(1)項(xiàng)目架構(gòu)
(二) J2EE項(xiàng)目系列(三)--Spring Data JPA+Spring+SpringMVC+Maven快速開(kāi)發(fā)(2)多個(gè)第三方服務(wù)端接入之云旺IM
(三) Java-解決實(shí)現(xiàn)JPA的hibernate自動(dòng)建表的編碼問(wèn)題
文章結(jié)構(gòu):(1)JWT(一種基于 token 的認(rèn)證方案)介紹并介紹其他幾大認(rèn)證機(jī)制;(2)cookie和session機(jī)制;(3)Token機(jī)制相對(duì)于Cookie機(jī)制的好處;(4)JWT的Java實(shí)現(xiàn);
一、JWT(一種基于 token 的認(rèn)證方案)介紹:
(1)概述:JWT就是一種Token的編碼算法,服務(wù)器端負(fù)責(zé)根據(jù)一個(gè)密碼和算法生成Token,然后發(fā)給客戶(hù)端,客戶(hù)端只負(fù)責(zé)后面每次請(qǐng)求都在HTTP header里面帶上這個(gè)Token,服務(wù)器負(fù)責(zé)驗(yàn)證這個(gè)Token是不是合法的,有沒(méi)有過(guò)期等,并可以解析出subject和claim里面的數(shù)據(jù)。
(2)相關(guān)問(wèn)題:
1.為什么用JWT?
JWT只通過(guò)算法實(shí)現(xiàn)對(duì)Token合法性的驗(yàn)證,不依賴(lài)數(shù)據(jù)庫(kù),Memcached的等存儲(chǔ)系統(tǒng),因此可以做到跨服務(wù)器驗(yàn)證,只要密鑰和算法相同,不同服務(wù)器程序生成的Token可以互相驗(yàn)證。
2.JWT Token不需要持久化在任何NoSQL中,不然背離其算法驗(yàn)證的初心
3.在退出登錄時(shí)怎樣實(shí)現(xiàn)JWT Token失效呢?
退出登錄, 只要客戶(hù)端端把Token丟棄就可以了,服務(wù)器端不需要廢棄Token。
4.怎樣保持客戶(hù)端長(zhǎng)時(shí)間保持登錄狀態(tài)?
服務(wù)器端提供刷新Token的接口, 客戶(hù)端負(fù)責(zé)按一定的邏輯刷新服務(wù)器Token。
5.服務(wù)器端是否應(yīng)該從JWT中取出userid用于業(yè)務(wù)查詢(xún)?
REST API是無(wú)狀態(tài)的,意味著服務(wù)器端每次請(qǐng)求都是獨(dú)立的,即不依賴(lài)以前請(qǐng)求的結(jié)果,因此也不應(yīng)該依賴(lài)JWT token做業(yè)務(wù)查詢(xún), 應(yīng)該在請(qǐng)求報(bào)文中單獨(dú)加個(gè)userid 字段。
為了做用戶(hù)水平越權(quán)的檢查,可以在業(yè)務(wù)層判斷傳入的userid和從JWT token中解析出的userid是否一致, 有些業(yè)務(wù)可能會(huì)允許查不同用戶(hù)的數(shù)據(jù)。
(3)其他幾大認(rèn)證機(jī)制:
1.HTTP Basic Auth:
HTTP Basic Auth簡(jiǎn)單點(diǎn)說(shuō)明就是每次請(qǐng)求API時(shí)都提供用戶(hù)的username和password,簡(jiǎn)言之,Basic Auth是配合RESTful API 使用的最簡(jiǎn)單的認(rèn)證方式,只需提供用戶(hù)名密碼即可,但由于有把用戶(hù)名密碼暴露給第三方客戶(hù)端的風(fēng)險(xiǎn),在生產(chǎn)環(huán)境下被使用的越來(lái)越少。因此,在開(kāi)發(fā)對(duì)外開(kāi)放的RESTful API時(shí),盡量避免采用HTTP Basic Auth
2.OAuth(開(kāi)放授權(quán)):
是一個(gè)開(kāi)放的授權(quán)標(biāo)準(zhǔn),允許用戶(hù)讓第三方應(yīng)用訪(fǎng)問(wèn)該用戶(hù)在某一web服務(wù)上存儲(chǔ)的私密的資源(如照片,視頻,聯(lián)系人列表),而無(wú)需將用戶(hù)名和密碼提供給第三方應(yīng)用。
OAuth允許用戶(hù)提供一個(gè)令牌,而不是用戶(hù)名和密碼來(lái)訪(fǎng)問(wèn)他們存放在特定服務(wù)提供者的數(shù)據(jù)。每一個(gè)令牌授權(quán)一個(gè)特定的第三方系統(tǒng)(例如,視頻編輯網(wǎng)站)在特定的時(shí)段(例如,接下來(lái)的2小時(shí)內(nèi))內(nèi)訪(fǎng)問(wèn)特定的資源(例如僅僅是某一相冊(cè)中的視頻)。這樣,OAuth讓用戶(hù)可以授權(quán)第三方網(wǎng)站訪(fǎng)問(wèn)他們存儲(chǔ)在另外服務(wù)提供者的某些特定信息,而非所有內(nèi)容。
這里寫(xiě)圖片描述
這種基于OAuth的認(rèn)證機(jī)制適用于個(gè)人消費(fèi)者類(lèi)的互聯(lián)網(wǎng)產(chǎn)品,如社交類(lèi)APP等應(yīng)用,但是不太適合擁有自有認(rèn)證權(quán)限管理的企業(yè)應(yīng)用;
3.Cookie Auth:
Cookie認(rèn)證機(jī)制就是為一次請(qǐng)求認(rèn)證在服務(wù)端創(chuàng)建一個(gè)Session對(duì)象,同時(shí)在客戶(hù)端的瀏覽器端創(chuàng)建了一個(gè)Cookie對(duì)象;通過(guò)客戶(hù)端帶上來(lái)Cookie對(duì)象來(lái)與服務(wù)器端的session對(duì)象匹配來(lái)實(shí)現(xiàn)狀態(tài)管理的。默認(rèn)的,當(dāng)我們關(guān)閉瀏覽器的時(shí)候,cookie會(huì)被刪除。但可以通過(guò)修改cookie 的expire time使cookie在一定時(shí)間內(nèi)有效;
二、cookie和session機(jī)制:
(1)概述:
Cookie和Session是為了在無(wú)狀態(tài)的HTTP協(xié)議之上維護(hù)會(huì)話(huà)狀態(tài),使得服務(wù)器可以知道當(dāng)前是和哪個(gè)客戶(hù)在打交道。
Session是在服務(wù)端保存的一個(gè)數(shù)據(jù)結(jié)構(gòu),用來(lái)跟蹤用戶(hù)的狀態(tài),這個(gè)數(shù)據(jù)可以保存在集群、數(shù)據(jù)庫(kù)、文件中;
Cookie是客戶(hù)端保存用戶(hù)信息的一種機(jī)制,用來(lái)記錄用戶(hù)的一些信息,也是實(shí)現(xiàn)Session的一種方式。
因?yàn)镠TTP協(xié)議是無(wú)狀態(tài)的,即每次用戶(hù)請(qǐng)求到達(dá)服務(wù)器時(shí),HTTP服務(wù)器并不知道這個(gè)用戶(hù)是誰(shuí)、是否登錄過(guò)等?,F(xiàn)在的服務(wù)器之所以知道我們是否已經(jīng)登錄,是因?yàn)榉?wù)器在登錄時(shí)設(shè)置了瀏覽器的Cookie!Session則是借由Cookie而實(shí)現(xiàn)的更高層的服務(wù)器與瀏覽器之間的會(huì)話(huà)。
(2)cookie實(shí)現(xiàn)機(jī)制:
Cookie是由客戶(hù)端保存的小型文本文件,其內(nèi)容為一系列的鍵值對(duì)。 Cookie是由HTTP服務(wù)器設(shè)置的,保存在瀏覽器中, 在用戶(hù)訪(fǎng)問(wèn)其他頁(yè)面時(shí),會(huì)在HTTP請(qǐng)求中附上該服務(wù)器之前設(shè)置的Cookie。
Cookie的傳遞流程:
1.瀏覽器向某個(gè)URL發(fā)起HTTP請(qǐng)求(可以是任何請(qǐng)求,比如GET一個(gè)頁(yè)面、POST一個(gè)登錄表單等)
2.對(duì)應(yīng)的服務(wù)器收到該HTTP請(qǐng)求,并計(jì)算應(yīng)當(dāng)返回給瀏覽器的HTTP響應(yīng)。(HTTP響應(yīng)包括請(qǐng)求頭和請(qǐng)求體兩部分)
3.在響應(yīng)頭加入Set-Cookie字段,它的值是要設(shè)置的Cookie。
4.瀏覽器收到來(lái)自服務(wù)器的HTTP響應(yīng)。
5.瀏覽器在響應(yīng)頭中發(fā)現(xiàn)Set-Cookie字段,就會(huì)將該字段的值保存在內(nèi)存或者硬盤(pán)中。(Set-Cookie字段的值可以是很多項(xiàng)Cookie,每一項(xiàng)都可以指定過(guò)期時(shí)間Expires。 默認(rèn)的過(guò)期時(shí)間是用戶(hù)關(guān)閉瀏覽器時(shí)。)
6.瀏覽器下次給該服務(wù)器發(fā)送HTTP請(qǐng)求時(shí), 會(huì)將服務(wù)器設(shè)置的Cookie附加在HTTP請(qǐng)求的頭字段Cookie中。(瀏覽器可以存儲(chǔ)多個(gè)域名下的Cookie,但只發(fā)送當(dāng)前請(qǐng)求的域名曾經(jīng)指定的Cookie, 這個(gè)域名也可以在Set-Cookie字段中指定)。)
7.服務(wù)器收到這個(gè)HTTP請(qǐng)求,發(fā)現(xiàn)請(qǐng)求頭中有Cookie字段, 便知道之前就和這個(gè)用戶(hù)打過(guò)交道了.
8.過(guò)期的Cookie會(huì)被瀏覽器刪除。
總之,服務(wù)器通過(guò)Set-Cookie響應(yīng)頭字段來(lái)指示瀏覽器保存Cookie, 瀏覽器通過(guò)Cookie請(qǐng)求頭字段來(lái)告訴服務(wù)器之前的狀態(tài)。 Cookie中包含若干個(gè)鍵值對(duì),每個(gè)鍵值對(duì)可以設(shè)置過(guò)期時(shí)間。
Cookie 的安全隱患:
Cookie提供了一種手段使得HTTP請(qǐng)求可以附加當(dāng)前狀態(tài), 現(xiàn)今的網(wǎng)站也是靠Cookie來(lái)標(biāo)識(shí)用戶(hù)的登錄狀態(tài)的:
1.用戶(hù)提交用戶(hù)名和密碼的表單,這通常是一個(gè)POST HTTP請(qǐng)求。
2.服務(wù)器驗(yàn)證用戶(hù)名與密碼,如果合法則返回200(OK)并設(shè)置Set-Cookie為authed=true。
3.瀏覽器存儲(chǔ)該Cookie。
4.瀏覽器發(fā)送請(qǐng)求時(shí),設(shè)置Cookie字段為authed=true。
5.服務(wù)器收到第二次請(qǐng)求,從Cookie字段得知該用戶(hù)已經(jīng)登錄。 按照已登錄用戶(hù)的權(quán)限來(lái)處理此次請(qǐng)求。
問(wèn)題是什么??風(fēng)險(xiǎn)是什么??
我們知道可以發(fā)送HTTP請(qǐng)求的不只是瀏覽器,很多HTTP客戶(hù)端軟件(包括curl、Node.js)都可以發(fā)送任意的HTTP請(qǐng)求,可以設(shè)置任何頭字段。 假如我們直接設(shè)置Cookie字段為authed=true并發(fā)送該HTTP請(qǐng)求, 服務(wù)器豈不是被欺騙了?這種攻擊非常容易,Cookie是可以被篡改的!
Cookie 防篡改機(jī)制:
服務(wù)器可以為每個(gè)Cookie項(xiàng)生成簽名,由于用戶(hù)篡改Cookie后無(wú)法生成對(duì)應(yīng)的簽名, 服務(wù)器便可以得知用戶(hù)對(duì)Cookie進(jìn)行了篡改。
例子:一個(gè)簡(jiǎn)單的校驗(yàn)過(guò)程:
1.在服務(wù)器中配置一個(gè)不為人知的字符串(我們叫它Secret),比如:x$sfz32。
2.當(dāng)服務(wù)器需要設(shè)置Cookie時(shí)(比如authed=false),不僅設(shè)置authed的值為false, 在值的后面進(jìn)一步設(shè)置一個(gè)簽名,最終設(shè)置的Cookie是authed=false|6hTiBl7lVpd1P。
3.簽名6hTiBl7lVpd1P是這樣生成的:Hash('x$sfz32'+'true')。 要設(shè)置的值與Secret相加再取哈希。
4.用戶(hù)收到HTTP響應(yīng)并發(fā)現(xiàn)頭字段Set-Cookie: authed=false|6hTiBl7lVpd1P。
5.用戶(hù)在發(fā)送HTTP請(qǐng)求時(shí),篡改了authed值,設(shè)置頭字段Cookie: authed=true|???。 因?yàn)橛脩?hù)不知道Secret,無(wú)法生成簽名,只能隨便填一個(gè)。
6.服務(wù)器收到HTTP請(qǐng)求,發(fā)現(xiàn)Cookie: authed=true|???。服務(wù)器開(kāi)始進(jìn)行校驗(yàn): Hash('true'+'x$sfz32'),便會(huì)發(fā)現(xiàn)用戶(hù)提供的簽名不正確。
通過(guò)給Cookie添加簽名,使得服務(wù)器得以知道Cookie被篡改。但是?。∵€是有風(fēng)險(xiǎn)!
因?yàn)镃ookie是明文傳輸的, 只要服務(wù)器設(shè)置過(guò)一次authed=true|xxxx我不就知道true的簽名是xxxx了么, 以后就可以用這個(gè)簽名來(lái)欺騙服務(wù)器了。因此Cookie中最好不要放敏感數(shù)據(jù)。 一般來(lái)講Cookie中只會(huì)放一個(gè)Session Id,而Session存儲(chǔ)在服務(wù)器端。
(3)session的實(shí)現(xiàn)機(jī)制:
1.概述:Session 是存儲(chǔ)在服務(wù)器端的,避免了在客戶(hù)端Cookie中存儲(chǔ)敏感數(shù)據(jù)。 Session 可以存儲(chǔ)在HTTP服務(wù)器的內(nèi)存中,也可以存在內(nèi)存數(shù)據(jù)庫(kù)(如redis)中, 對(duì)于重量級(jí)的應(yīng)用甚至可以存儲(chǔ)在數(shù)據(jù)庫(kù)中。
例子:存儲(chǔ)在redis中的Session為例,考察如何驗(yàn)證用戶(hù)登錄狀態(tài)的問(wèn)題。
1.用戶(hù)提交包含用戶(hù)名和密碼的表單,發(fā)送HTTP請(qǐng)求。
2.服務(wù)器驗(yàn)證用戶(hù)發(fā)來(lái)的用戶(hù)名密碼。
3.如果正確則把當(dāng)前用戶(hù)名(通常是用戶(hù)對(duì)象)存儲(chǔ)到redis中,并生成它在redis中的ID。
這個(gè)ID稱(chēng)為Session ID,通過(guò)Session ID可以從Redis中取出對(duì)應(yīng)的用戶(hù)對(duì)象, 敏感數(shù)據(jù)(比如authed=true)都存儲(chǔ)在這個(gè)用戶(hù)對(duì)象中。
4.設(shè)置Cookie為sessionId=xxxxxx|checksum并發(fā)送HTTP響應(yīng), 仍然為每一項(xiàng)Cookie都設(shè)置簽名。
5.用戶(hù)收到HTTP響應(yīng)后,便看不到任何敏感數(shù)據(jù)了。在此后的請(qǐng)求中發(fā)送該Cookie給服務(wù)器。
6.服務(wù)器收到此后的HTTP請(qǐng)求后,發(fā)現(xiàn)Cookie中有SessionID,進(jìn)行放篡改驗(yàn)證。
7.如果通過(guò)了驗(yàn)證,根據(jù)該ID從Redis中取出對(duì)應(yīng)的用戶(hù)對(duì)象, 查看該對(duì)象的狀態(tài)并繼續(xù)執(zhí)行業(yè)務(wù)邏輯。
實(shí)現(xiàn)上述過(guò)程,在Web應(yīng)用中可以直接獲得當(dāng)前用戶(hù)。 相當(dāng)于在HTTP協(xié)議之上,通過(guò)Cookie實(shí)現(xiàn)了持久的會(huì)話(huà)。這個(gè)會(huì)話(huà)便稱(chēng)為Session。
三、Token認(rèn)證機(jī)制相對(duì)于Cookie等機(jī)制的好處:
1. 支持跨域訪(fǎng)問(wèn): Cookie是不允許垮域訪(fǎng)問(wèn)的,這一點(diǎn)對(duì)Token機(jī)制是不存在的,前提是傳輸?shù)挠脩?hù)認(rèn)證信息通過(guò)HTTP頭傳輸。(垮域訪(fǎng)問(wèn):兩個(gè)域名之間不能跨過(guò)域名來(lái)發(fā)送請(qǐng)求或者請(qǐng)求數(shù)據(jù))
2.無(wú)狀態(tài)(也稱(chēng):服務(wù)端可擴(kuò)展行):Token機(jī)制在服務(wù)端不需要存儲(chǔ)session信息,因?yàn)門(mén)oken 自身包含了所有登錄用戶(hù)的信息,只需要在客戶(hù)端的cookie或本地介質(zhì)存儲(chǔ)狀態(tài)信息.
3.更適用CDN: 可以通過(guò)內(nèi)容分發(fā)網(wǎng)絡(luò)請(qǐng)求你服務(wù)端的所有資料(如:javascript,HTML,圖片等),而你的服務(wù)端只要提供API即可.
4.去耦: 不需要綁定到一個(gè)特定的身份驗(yàn)證方案。Token可以在任何地方生成,只要在你的API被調(diào)用的時(shí)候,你可以進(jìn)行Token生成調(diào)用即可.
5.更適用于移動(dòng)應(yīng)用: 當(dāng)你的客戶(hù)端是一個(gè)原生平臺(tái)(iOS, Android,Windows 8等)時(shí),Cookie是不被支持的(你需要通過(guò)Cookie容器進(jìn)行處理),這時(shí)采用Token認(rèn)證機(jī)制就會(huì)簡(jiǎn)單得多。
6. CSRF:因?yàn)椴辉僖蕾?lài)于Cookie,所以你就不需要考慮對(duì)CSRF(跨站請(qǐng)求偽造)的防范。
7.性能: 一次網(wǎng)絡(luò)往返時(shí)間(通過(guò)數(shù)據(jù)庫(kù)查詢(xún)session信息)總比做一次HMACSHA256計(jì)算 的Token驗(yàn)證和解析要費(fèi)時(shí)得多.
8.不需要為登錄頁(yè)面做特殊處理: 如果你使用Protractor 做功能測(cè)試的時(shí)候,不再需要為登錄頁(yè)面做特殊處理.
9.基于標(biāo)準(zhǔn)化:你的API可以采用標(biāo)準(zhǔn)化的 JSON Web Token (JWT). 這個(gè)標(biāo)準(zhǔn)已經(jīng)存在多個(gè)后端庫(kù)(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft).
四、JWT的Java實(shí)現(xiàn):
概述:一個(gè)JWT實(shí)際上就是一個(gè)字符串,它由三部分組成,頭部、載荷與簽名。這里我們只使用簡(jiǎn)單的載荷,并將JSON對(duì)象進(jìn)行base64編碼得到token
過(guò)程:登錄為例子
第一次認(rèn)證:第一次登錄,用戶(hù)從瀏覽器輸入用戶(hù)名/密碼,提交后到服務(wù)器的登錄處理的Action層(controller)
Login Action調(diào)用認(rèn)證服務(wù)進(jìn)行用戶(hù)名密碼認(rèn)證,如果認(rèn)證通過(guò),Login Action層調(diào)用用戶(hù)信息服務(wù)獲取用戶(hù)信息(包括完整的用戶(hù)信息及對(duì)應(yīng)權(quán)限信息);
返回用戶(hù)信息后,Login Action從配置文件再經(jīng)過(guò)工具類(lèi)處理獲取Token簽名生成的秘鑰信息,進(jìn)行Token的生成
生成Token的過(guò)程中可以調(diào)用第三方的JWT Lib生成簽名后的JWT數(shù)據(jù);
完成JWT數(shù)據(jù)簽名后,將其設(shè)置到COOKIE對(duì)象中,并重定向到首頁(yè),完成登錄過(guò)程;
請(qǐng)求認(rèn)證:
使用:基于Token的認(rèn)證機(jī)制會(huì)在每一次請(qǐng)求中都帶上完成簽名的Token信息,這個(gè)Token信息可能在COOKIE中,也可能在HTTP的Authorization頭中;
注意:
客戶(hù)端(APP客戶(hù)端或?yàn)g覽器)通過(guò)GET或POST請(qǐng)求訪(fǎng)問(wèn)資源(頁(yè)面或調(diào)用API);
認(rèn)證服務(wù)作為一個(gè)Middleware HOOK 對(duì)請(qǐng)求進(jìn)行攔截,首先在cookie中查找Token信息,如果沒(méi)有找到,則在HTTP Authorization Head中查找;
如果找到Token信息,則根據(jù)配置文件中的簽名加密秘鑰,調(diào)用JWT Lib對(duì)Token信息進(jìn)行解密和解碼;
完成解碼并驗(yàn)證簽名通過(guò)后,對(duì)Token中的exp、nbf、aud等信息進(jìn)行驗(yàn)證;
全部通過(guò)后,根據(jù)獲取的用戶(hù)的角色權(quán)限信息,進(jìn)行對(duì)請(qǐng)求的資源的權(quán)限邏輯判斷;
如果權(quán)限邏輯判斷通過(guò)則通過(guò)Response對(duì)象返回;否則則返回HTTP 401;
(1)使用JWT的包:maven導(dǎo)入
<!--JSON WEB TOKEN -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>
(2)一個(gè)生成token的工具類(lèi):
public class JavaWebToken {
private static Logger log = Logger.getLogger(JavaWebToken.class);
private static Key getKeyInstance() {
// return MacProvider.generateKey();
//We will sign our JavaWebToken with our ApiKey secret
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary("APP");
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
return signingKey;
}
public static String createJavaWebToken(Map<String, Object> claims) {
return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS256, getKeyInstance()).compact();
}
public static Map<String, Object> verifyJavaWebToken(String jwt) {
try {
Map<String, Object> jwtClaims =
Jwts.parser().setSigningKey(getKeyInstance()).parseClaimsJws(jwt).getBody();
return jwtClaims;
} catch (Exception e) {
log.error("json web token verify failed");
return null;
}
}
}
(3)一個(gè)從request拿去session,并且解密session得到token得到用戶(hù)id的類(lèi)
public class AuthUtil {
private static Map<String, Object> getClientLoginInfo(HttpServletRequest request) throws Exception {
Map<String, Object> r = new HashMap<>();
String sessionId = request.getHeader("sessionId");
if (sessionId != null) {
r = decodeSession(sessionId);
return r;
}
throw new Exception("session解析錯(cuò)誤");
}
public static Long getUserId(HttpServletRequest request) throws Exception {
return Long.valueOf((Integer)getClientLoginInfo(request).get("userId"));
}
/**
* session解密
*/
public static Map<String, Object> decodeSession(String sessionId) {
try {
return verifyJavaWebToken(sessionId);
} catch (Exception e) {
System.err.println("");
return null;
}
}
}
使用例子:
登錄的時(shí)候把信息放進(jìn)session,存到map里,再交由JWT得到token保存起來(lái)
這里寫(xiě)圖片描述
//登錄
@RequestMapping(value = "/login", method = {RequestMethod.GET, RequestMethod.POST}, produces = "text/html;charset=UTF-8")
public String login(String account) {
User user = userService.login(account);
DTO dto = new DTO();
if (user == null) {
dto.code = "-1";
dto.msg = "Have not registered";
} else {
//把用戶(hù)登錄信息放進(jìn)Session
Map<String, Object> loginInfo = new HashMap<>();
loginInfo.put("userId", user.getId());
String sessionId = JavaWebToken.createJavaWebToken(loginInfo);
System.out.println("sessionID"+sessionId);
dto.data = sessionId;
}
return JSON.toJSONString(dto);
}
用戶(hù)登錄以后,其他的用戶(hù)性知道的操作就可以使用token進(jìn)行了,安全快捷方便:
這里寫(xiě)圖片描述
//修改昵稱(chēng)
@RequestMapping(value = "/updateName", method = {RequestMethod.GET, RequestMethod.POST})
public String updateName(HttpServletRequest request, String name) {
DTO dto = new DTO();
try {
//從session拿到token,再解密得到userid
Long userId = AuthUtil.getUserId(request);
boolean userIsExist = userService.updateName(userId, name);
if (userIsExist == false) {
dto.code = "-1";
dto.msg = "Have not updateAvatar";
}
} catch (Exception e) {
e.printStackTrace();
}
return JSON.toJSONString(dto);
}