Token 認(rèn)證的來龍去脈

幾個文章:
https://segmentfault.com/a/1190000013010835
http://www.cnblogs.com/Ant-soldier/p/5038601.html
http://www.reibang.com/p/97c193ee1a09

不久前桑包,我在在前后端分離實(shí)踐中提到了基于 Token 的認(rèn)證哑了,現(xiàn)在我們稍稍深入一些弱左。

通常情況下炕淮,我們在討論某個技術(shù)的時候涂圆,都是從問題開始。那么第一個問題:

為什么要用 Token模狭?

而要回答這個問題很簡單——因?yàn)樗芙鉀Q問題嚼鹉!

可以解決哪些問題呢驱富?

  1. Token 完全由應(yīng)用管理萌朱,所以它可以避開同源策略
  2. Token 可以避免 CSRF 攻擊
  3. Token 可以是無狀態(tài)的晶疼,可以在多個服務(wù)間共享

Token 是在服務(wù)端產(chǎn)生的又憨。如果前端使用用戶名/密碼向服務(wù)端請求認(rèn)證蠢莺,服務(wù)端認(rèn)證成功零如,那么在服務(wù)端會返回 Token 給前端考蕾。前端可以在每次請求的時候帶上 Token 證明自己的合法地位肖卧。如果這個 Token 在服務(wù)端持久化(比如存入數(shù)據(jù)庫)塞帐,那它就是一個永久的身份令牌葵姥。

于是,又一個問題產(chǎn)生了:需要為 Token 設(shè)置有效期嗎允乐?

需要設(shè)置有效期嗎喳篇?

對于這個問題麸澜,我們不妨先看兩個例子奏黑。一個例子是登錄密碼熟史,一般要求定期改變密碼,以防止泄漏碘菜,所以密碼是有有效期的忍啸;另一個例子是安全證書。SSL 安全證書都有有效期悄晃,目的是為了解決吊銷的問題妈橄,對于這個問題的詳細(xì)情況眷蚓,來看看知乎的回答反番。所以無論是從安全的角度考慮恬口,還是從吊銷的角度考慮祖能,Token 都需要設(shè)有效期蛾洛。

那么有效期多長合適呢轧膘?

只能說,根據(jù)系統(tǒng)的安全需要鳞滨,盡可能的短拯啦,但也不能短得離譜——想像一下手機(jī)的自動熄屏?xí)r間熔任,如果設(shè)置為 10 秒鐘無操作自動熄屏疑苔,再次點(diǎn)亮需要輸入密碼,會不會瘋抢韭?如果你覺得不會篮绰,那就親自試一試季惯,設(shè)置成可以設(shè)置的最短時間吠各,堅(jiān)持一周就好(不排除有人適應(yīng)這個時間,畢竟手機(jī)廠商也是有用戶體驗(yàn)研究的)勉抓。

然后新問題產(chǎn)生了贾漏,如果用戶在正常操作的過程中,Token 過期失效了藕筋,要求用戶重新登錄……用戶體驗(yàn)豈不是很糟糕纵散?

為了解決在操作過程不能讓用戶感到 Token 失效這個問題,有一種方案是在服務(wù)器端保存 Token 狀態(tài)隐圾,用戶每次操作都會自動刷新(推遲) Token 的過期時間——Session 就是采用這種策略來保持用戶登錄狀態(tài)的伍掀。然而仍然存在這樣一個問題暇藏,在前后端分離蜜笤、單頁 App 這些情況下,每秒種可能發(fā)起很多次請求盐碱,每次都去刷新過期時間會產(chǎn)生非常大的代價把兔。如果 Token 的過期時間被持久化到數(shù)據(jù)庫或文件,代價就更大了瓮顽。所以通常為了提升效率县好,減少消耗,會把 Token 的過期時保存在緩存或者內(nèi)存中暖混。

還有另一種方案缕贡,使用 Refresh Token,它可以避免頻繁的讀寫操作儒恋。這種方案中善绎,服務(wù)端不需要刷新 Token 的過期時間,一旦 Token 過期诫尽,就反饋給前端禀酱,前端使用 Refresh Token 申請一個全新 Token 繼續(xù)使用。這種方案中牧嫉,服務(wù)端只需要在客戶端請求更新 Token 的時候?qū)?Refresh Token 的有效性進(jìn)行一次檢查剂跟,大大減少了更新有效期的操作减途,也就避免了頻繁讀寫。當(dāng)然 Refresh Token 也是有有效期的曹洽,但是這個有效期就可以長一點(diǎn)了鳍置,比如,以天為單位的時間送淆。

時序圖表示

使用 Token 和 Refresh Token 的時序圖如下:

1)登錄

2485669377-5a69c9068ee26_articlex.png

2)業(yè)務(wù)請求

2908667125-5a69c973b4732_articlex.png

3)Token 過期税产,刷新 Token

4264150590-5a69ca2f6703d_articlex.png

上面的時序圖中并未提到 Refresh Token 過期怎么辦。不過很顯然偷崩,Refresh Token 既然已經(jīng)過期辟拷,就該要求用戶重新登錄了。

當(dāng)然還可以把這個機(jī)制設(shè)計(jì)得更復(fù)雜一些阐斜,比如衫冻,Refresh Token 每次使用的時候,都更新它的過期時間谒出,直到與它的創(chuàng)建時間相比隅俘,已經(jīng)超過了非常長的一段時間(比如三個月),這等于是在相當(dāng)長一段時間內(nèi)允許 Refresh Token 自動續(xù)期笤喳。

到目前為止为居,Token 都是有狀態(tài)的,即在服務(wù)端需要保存并記錄相關(guān)屬性杀狡。那說好的無狀態(tài)呢颜骤,怎么實(shí)現(xiàn)?

無狀態(tài) Token

如果我們把所有狀態(tài)信息都附加在 Token 上捣卤,服務(wù)器就可以不保存。但是服務(wù)端仍然需要認(rèn)證 Token 有效八孝。不過只要服務(wù)端能確認(rèn)是自己簽發(fā)的 Token董朝,而且其信息未被改動過,那就可以認(rèn)為 Token 有效——“簽名”可以作此保證干跛。平時常說的簽名都存在一方簽發(fā)子姜,另一方驗(yàn)證的情況,所以要使用非對稱加密算法楼入。但是在這里哥捕,簽發(fā)和驗(yàn)證都是同一方,所以對稱加密算法就能達(dá)到要求嘉熊,而對稱算法比非對稱算法要快得多(可達(dá)數(shù)十倍差距)遥赚。更進(jìn)一步思考,對稱加密算法除了加密阐肤,還帶有還原加密內(nèi)容的功能凫佛,而這一功能在對 Token 簽名時并無必要——既然不需要解密讲坎,摘要(散列)算法就會更快±⒀Γ可以指定密碼的散列算法晨炕,自然是 HMAC。

上面說了這么多毫炉,還需要自己去實(shí)現(xiàn)嗎瓮栗?不用!JWT 已經(jīng)定義了詳細(xì)的規(guī)范瞄勾,而且有各種語言的若干實(shí)現(xiàn)费奸。

不過在使用無狀態(tài) Token 的時候在服務(wù)端會有一些變化,服務(wù)端雖然不保存有效的 Token 了丰榴,卻需要保存未到期卻已注銷的 Token货邓。如果一個 Token 未到期就被用戶主動注銷,那么服務(wù)器需要保存這個被注銷的 Token四濒,以便下次收到使用這個仍在有效期內(nèi)的 Token 時判其無效换况。有沒有感到一點(diǎn)沮喪廷没?

在前端可控的情況下(比如前端和服務(wù)端在同一個項(xiàng)目組內(nèi))抵碟,可以協(xié)商:前端一但注銷成功,就丟掉本地保存(比如保存在內(nèi)存队塘、LocalStorage 等)的 Token 和 Refresh Token喳资【蹩裕基于這樣的約定,服務(wù)器就可以假設(shè)收到的 Token 一定是沒注銷的(因?yàn)樽N之后前端就不會再使用了)仆邓。

如果前端不可控的情況鲜滩,仍然可以進(jìn)行上面的假設(shè),但是這種情況下节值,需要盡量縮短 Token 的有效期徙硅,而且必須在用戶主動注銷的情況下讓 Refresh Token 無效。這個操作存在一定的安全漏洞搞疗,因?yàn)橛脩魰J(rèn)為已經(jīng)注銷了嗓蘑,實(shí)際上在較短的一段時間內(nèi)并沒有注銷。如果應(yīng)用設(shè)計(jì)中匿乃,這點(diǎn)漏洞并不會造成什么損失桩皿,那采用這種策略就是可行的。

在使用無狀態(tài) Token 的時候幢炸,有兩點(diǎn)需要注意:

  1. Refresh Token 有效時間較長泄隔,所以它應(yīng)該在服務(wù)器端有狀態(tài),以增強(qiáng)安全性宛徊,確保用戶注銷時可控
  2. 應(yīng)該考慮使用二次認(rèn)證來增強(qiáng)敏感操作的安全性

到此梅尤,關(guān)于 Token 的話題似乎差不多了——然而并沒有柜思,上面說的只是認(rèn)證服務(wù)和業(yè)務(wù)服務(wù)集成在一起的情況,如果是分離的情況呢巷燥?

分離認(rèn)證服務(wù)

當(dāng) Token 無狀態(tài)之后赡盘,單點(diǎn)登錄就變得容易了。前端拿到一個有效的 Token缰揪,它就可以在任何同一體系的服務(wù)上認(rèn)證通過——只要它們使用同樣的密鑰和算法來認(rèn)證 Token 的有效性陨享。就樣這樣:

2442545916-5a69d3ab205b4_articlex.png

當(dāng)然,如果 Token 過期了钝腺,前端仍然需要去認(rèn)證服務(wù)更新 Token:

2364067243-5a69d3df322ca_articlex.png

可見抛姑,雖然認(rèn)證和業(yè)務(wù)分離了,實(shí)際即并沒產(chǎn)生多大的差異艳狐。當(dāng)然定硝,這是建立在認(rèn)證服務(wù)器信任業(yè)務(wù)服務(wù)器的前提下,因?yàn)檎J(rèn)證服務(wù)器產(chǎn)生 Token 的密鑰和業(yè)務(wù)服務(wù)器認(rèn)證 Token 的密鑰和算法相同毫目。換句話說蔬啡,業(yè)務(wù)服務(wù)器同樣可以創(chuàng)建有效的 Token。

如果業(yè)務(wù)服務(wù)器不能被信任镀虐,該怎么辦箱蟆?

不受信的業(yè)務(wù)服務(wù)器

遇到不受信的業(yè)務(wù)服務(wù)器時,很容易想到的辦法是使用不同的密鑰刮便。認(rèn)證服務(wù)器使用密鑰1簽發(fā)空猜,業(yè)務(wù)服務(wù)器使用密鑰2驗(yàn)證——這是典型非對稱加密簽名的應(yīng)用場景。認(rèn)證服務(wù)器自己使用私鑰對 Token 簽名恨旱,公開公鑰辈毯。信任這個認(rèn)證服務(wù)器的業(yè)務(wù)服務(wù)器保存公鑰,用于驗(yàn)證簽名搜贤。幸好漓摩,JWT 不僅可以使用 HMAC 簽名,也可以使用 RSA(一種非對稱加密算法)簽名入客。

不過,當(dāng)業(yè)務(wù)服務(wù)器已經(jīng)不受信任的時候腿椎,多個業(yè)務(wù)服務(wù)器之間使用相同的 Token 對用戶來說是不安全的桌硫。因?yàn)槿魏我粋€服務(wù)器拿到 Token 都可以仿冒用戶去另一個服務(wù)器處理業(yè)務(wù)……悲劇隨時可能發(fā)生。

為了防止這種情況發(fā)生啃炸,就需要在認(rèn)證服務(wù)器產(chǎn)生 Token 的時候铆隘,把使用該 Token 的業(yè)務(wù)服務(wù)器的信息記錄在 Token 中,這樣當(dāng)另一個業(yè)務(wù)服務(wù)器拿到這個 Token 的時候南用,發(fā)現(xiàn)它并不是自己應(yīng)該驗(yàn)證的 Token膀钠,就可以直接拒絕掏湾。

現(xiàn)在,認(rèn)證服務(wù)器不信任業(yè)務(wù)服務(wù)器肿嘲,業(yè)務(wù)服務(wù)器相互也不信任融击,但前端是信任這些服務(wù)器的——如果前端不信任,就不會拿 Token 去請求驗(yàn)證雳窟。那么為什么會信任尊浪?可能是因?yàn)檫@些是同一家公司或者同一個項(xiàng)目中提供的若干服務(wù)構(gòu)成的服務(wù)體系。

但是封救,前端信任不代表用戶信任拇涤。如果 Token 不沒有攜帶用戶隱私(比如姓名),那么用戶不會關(guān)心信任問題誉结。但如果 Token 含有用戶隱私的時候鹅士,用戶得關(guān)心信任問題了。這時候認(rèn)證服務(wù)就不得不再啰嗦一些惩坑,當(dāng)用戶請求 Token 的時候掉盅,問上一句,你真的要授權(quán)給某某某業(yè)務(wù)服務(wù)嗎旭贬?而這個“某某某”怔接,用戶怎么知道它是不是真的“某某某”呢?用戶當(dāng)然不知道稀轨,甚至認(rèn)證服務(wù)也不知道扼脐,因?yàn)楣€已經(jīng)公開了,任何一個業(yè)務(wù)都可以聲明自己是“某某某”奋刽。

為了得到用戶的信任瓦侮,認(rèn)證服務(wù)就不得不幫助用戶來甄別業(yè)務(wù)服務(wù)。所以佣谐,認(rèn)證服器決定不公開公鑰肚吏,而是要求業(yè)務(wù)服務(wù)先申請注冊并通過審核。只有通過審核的業(yè)務(wù)服務(wù)器才能得到認(rèn)證服務(wù)為它創(chuàng)建的狭魂,僅供它使用的公鑰罚攀。如果該業(yè)務(wù)服務(wù)泄漏公鑰帶來風(fēng)險,由該業(yè)務(wù)服務(wù)自行承擔(dān)〈瞥危現(xiàn)在認(rèn)證服務(wù)可以清楚的告訴用戶斋泄,“某某某”服務(wù)是什么了。如果用戶還是不夠信任镐牺,認(rèn)證服務(wù)甚至可以問炫掐,某某某業(yè)務(wù)服務(wù)需要請求 A、B睬涧、C 三項(xiàng)個人數(shù)據(jù)募胃,其中 A 是必須的旗唁,不然它不工作,是否允許授權(quán)痹束?如果你授權(quán)检疫,我就把你授權(quán)的幾項(xiàng)數(shù)據(jù)加密放在 Token 中……

廢話了這么多,有沒有似曾相識……對了参袱,這類似開放式 API 的認(rèn)證過程电谣。開發(fā)式 API 多采用 OAuth 認(rèn)證,而關(guān)于 OAuth 的探討資源非常豐富抹蚀,這里就不深究了剿牺。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市环壤,隨后出現(xiàn)的幾起案子晒来,更是在濱河造成了極大的恐慌,老刑警劉巖郑现,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件湃崩,死亡現(xiàn)場離奇詭異,居然都是意外死亡接箫,警方通過查閱死者的電腦和手機(jī)攒读,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辛友,“玉大人薄扁,你說我怎么就攤上這事》侠郏” “怎么了邓梅?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長邑滨。 經(jīng)常有香客問我日缨,道長,這世上最難降的妖魔是什么掖看? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任匣距,我火速辦了婚禮,結(jié)果婚禮上哎壳,老公的妹妹穿的比我還像新娘毅待。我一直安慰自己,他們只是感情好耳峦,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著焕毫,像睡著了一般蹲坷。 火紅的嫁衣襯著肌膚如雪驶乾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天循签,我揣著相機(jī)與錄音级乐,去河邊找鬼。 笑死县匠,一個胖子當(dāng)著我的面吹牛风科,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播乞旦,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼贼穆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了兰粉?” 一聲冷哼從身側(cè)響起故痊,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎玖姑,沒想到半個月后愕秫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡焰络,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年戴甩,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闪彼。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡甜孤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出备蚓,到底是詐尸還是另有隱情课蔬,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布郊尝,位于F島的核電站二跋,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏流昏。R本人自食惡果不足惜扎即,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望况凉。 院中可真熱鬧谚鄙,春花似錦、人聲如沸刁绒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至傻盟,卻和暖如春速蕊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背娘赴。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工规哲, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人诽表。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓唉锌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親竿奏。 傳聞我的和親對象是個殘疾皇子袄简,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容