深入理解token

摘要: Token 是在服務(wù)端產(chǎn)生的裆馒。如果前端使用用戶名/密碼向服務(wù)端請求認(rèn)證姊氓,服務(wù)端認(rèn)證成功,那么在服務(wù)端會(huì)返回 Token 給前端喷好。前端可以在每次請求的時(shí)候帶上 Token 證明自己的合法地位

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

通常情況下棕孙,我們在討論某個(gè)技術(shù)的時(shí)候,都是從問題開始些膨。那么第一個(gè)問題:

為什么要用 Token?

而要回答這個(gè)問題很簡單——因?yàn)樗芙鉀Q問題钦铺!

可以解決哪些問題呢订雾?

  1. Token 完全由應(yīng)用管理,所以它可以避開同源策略

  2. Token 可以避免 CSRF 攻擊(http://dwz.cn/7joLzx)

  3. Token 可以是無狀態(tài)的矛洞,可以在多個(gè)服務(wù)間共享

Token 是在服務(wù)端產(chǎn)生的洼哎。如果前端使用用戶名/密碼向服務(wù)端請求認(rèn)證,服務(wù)端認(rèn)證成功沼本,那么在服務(wù)端會(huì)返回 Token 給前端噩峦。前端可以在每次請求的時(shí)候帶上 Token 證明自己的合法地位。如果這個(gè) Token 在服務(wù)端持久化(比如存入數(shù)據(jù)庫)抽兆,那它就是一個(gè)永久的身份令牌识补。

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

需要設(shè)置有效期嗎凭涂?

對(duì)于這個(gè)問題,我們不妨先看兩個(gè)例子贴妻。一個(gè)例子是登錄密碼切油,一般要求定期改變密碼,以防止泄漏名惩,所以密碼是有有效期的澎胡;另一個(gè)例子是安全證書。SSL 安全證書都有有效期娩鹉,目的是為了解決吊銷的問題攻谁,對(duì)于這個(gè)問題的詳細(xì)情況,來看看知乎的回答(http://dwz.cn/7joMhq)底循。所以無論是從安全的角度考慮巢株,還是從吊銷的角度考慮,Token 都需要設(shè)有效期熙涤。

那么有效期多長合適呢阁苞?

只能說困檩,根據(jù)系統(tǒng)的安全需要,盡可能的短那槽,但也不能短得離譜——想像一下手機(jī)的自動(dòng)熄屏?xí)r間悼沿,如果設(shè)置為 10 秒鐘無操作自動(dòng)熄屏,再次點(diǎn)亮需要輸入密碼骚灸,會(huì)不會(huì)瘋糟趾?如果你覺得不會(huì),那就親自試一試甚牲,設(shè)置成可以設(shè)置的最短時(shí)間义郑,堅(jiān)持一周就好(不排除有人適應(yīng)這個(gè)時(shí)間,畢竟手機(jī)廠商也是有用戶體驗(yàn)研究的)丈钙。

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

為了解決在操作過程不能讓用戶感到 Token 失效這個(gè)問題,有一種方案是在服務(wù)器端保存 Token 狀態(tài)星岗,用戶每次操作都會(huì)自動(dòng)刷新(推遲) Token 的過期時(shí)間——Session 就是采用這種策略來保持用戶登錄狀態(tài)的填大。然而仍然存在這樣一個(gè)問題,在前后端分離俏橘、單頁 App 這些情況下允华,每秒種可能發(fā)起很多次請求,每次都去刷新過期時(shí)間會(huì)產(chǎn)生非常大的代價(jià)敷矫。如果 Token 的過期時(shí)間被持久化到數(shù)據(jù)庫或文件例获,代價(jià)就更大了。所以通常為了提升效率曹仗,減少消耗榨汤,會(huì)把 Token 的過期時(shí)保存在緩存或者內(nèi)存中。

還有另一種方案怎茫,使用 Refresh Token收壕,它可以避免頻繁的讀寫操作。這種方案中轨蛤,服務(wù)端不需要刷新 Token 的過期時(shí)間蜜宪,一旦 Token 過期,就反饋給前端祥山,前端使用 Refresh Token 申請一個(gè)全新 Token 繼續(xù)使用圃验。這種方案中,服務(wù)端只需要在客戶端請求更新 Token 的時(shí)候?qū)?Refresh Token 的有效性進(jìn)行一次檢查缝呕,大大減少了更新有效期的操作澳窑,也就避免了頻繁讀寫斧散。當(dāng)然 Refresh Token 也是有有效期的,但是這個(gè)有效期就可以長一點(diǎn)了摊聋,比如鸡捐,以天為單位的時(shí)間。

時(shí)序圖表示

使用 Token 和 Refresh Token 的時(shí)序圖如下:

1)登錄

image.jpeg

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

image.jpeg

3)Token 過期麻裁,刷新 Token

image.jpeg

上面的時(shí)序圖中并未提到 Refresh Token 過期怎么辦箍镜。不過很顯然,Refresh Token 既然已經(jīng)過期煎源,就該要求用戶重新登錄了色迂。

當(dāng)然還可以把這個(gè)機(jī)制設(shè)計(jì)得更復(fù)雜一些,比如手销,Refresh Token 每次使用的時(shí)候脚草,都更新它的過期時(shí)間,直到與它的創(chuàng)建時(shí)間相比原献,已經(jīng)超過了非常長的一段時(shí)間(比如三個(gè)月),這等于是在相當(dāng)長一段時(shí)間內(nèi)允許 Refresh Token 自動(dòng)續(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躏啰,而且其信息未被改動(dòng)過趁矾,那就可以認(rèn)為 Token 有效——“簽名”可以作此保證。平時(shí)常說的簽名都存在一方簽發(fā)给僵,另一方驗(yàn)證的情況毫捣,所以要使用非對(duì)稱加密算法。但是在這里帝际,簽發(fā)和驗(yàn)證都是同一方蔓同,所以對(duì)稱加密算法就能達(dá)到要求,而對(duì)稱算法比非對(duì)稱算法要快得多(可達(dá)數(shù)十倍差距)蹲诀。

更進(jìn)一步思考斑粱,對(duì)稱加密算法除了加密,還帶有還原加密內(nèi)容的功能脯爪,而這一功能在對(duì) Token 簽名時(shí)并無必要——既然不需要解密则北,摘要(散列)算法就會(huì)更快矿微。可以指定密碼的散列算法咒锻,自然是 HMAC冷冗。

上面說了這么多,還需要自己去實(shí)現(xiàn)嗎惑艇?不用蒿辙!JWT 已經(jīng)定義了詳細(xì)的規(guī)范,而且有各種語言的若干實(shí)現(xiàn)滨巴。

不過在使用無狀態(tài) Token 的時(shí)候在服務(wù)端會(huì)有一些變化思灌,服務(wù)端雖然不保存有效的 Token 了,卻需要保存未到期卻已注銷的 Token恭取。如果一個(gè) Token 未到期就被用戶主動(dòng)注銷泰偿,那么服務(wù)器需要保存這個(gè)被注銷的 Token,以便下次收到使用這個(gè)仍在有效期內(nèi)的 Token 時(shí)判其無效蜈垮。有沒有感到一點(diǎn)沮喪耗跛?

在前端可控的情況下(比如前端和服務(wù)端在同一個(gè)項(xiàng)目組內(nèi)),可以協(xié)商:前端一但注銷成功攒发,就丟掉本地保存(比如保存在內(nèi)存调塌、LocalStorage 等)的 Token 和 Refresh Token』菰常基于這樣的約定羔砾,服務(wù)器就可以假設(shè)收到的 Token 一定是沒注銷的(因?yàn)樽N之后前端就不會(huì)再使用了)。

如果前端不可控的情況偶妖,仍然可以進(jìn)行上面的假設(shè)姜凄,但是這種情況下,需要盡量縮短 Token 的有效期趾访,而且必須在用戶主動(dòng)注銷的情況下讓 Refresh Token 無效态秧。這個(gè)操作存在一定的安全漏洞,因?yàn)橛脩魰?huì)認(rèn)為已經(jīng)注銷了腹缩,實(shí)際上在較短的一段時(shí)間內(nèi)并沒有注銷屿聋。如果應(yīng)用設(shè)計(jì)中,這點(diǎn)漏洞并不會(huì)造成什么損失藏鹊,那采用這種策略就是可行的润讥。

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

  1. Refresh Token 有效時(shí)間較長盘寡,所以它應(yīng)該在服務(wù)器端有狀態(tài)楚殿,以增強(qiáng)安全性,確保用戶注銷時(shí)可控

  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)登錄就變得容易了规伐。前端拿到一個(gè)有效的 Token,它就可以在任何同一體系的服務(wù)上認(rèn)證通過——只要它們使用同樣的密鑰和算法來認(rèn)證 Token 的有效性匣缘。就樣這樣:

image.jpeg

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

image.jpeg

可見肌厨,雖然認(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ù)器時(shí)用爪,很容易想到的辦法是使用不同的密鑰。認(rèn)證服務(wù)器使用密鑰1簽發(fā)胁镐,業(yè)務(wù)服務(wù)器使用密鑰2驗(yàn)證——這是典型非對(duì)稱加密簽名的應(yīng)用場景。認(rèn)證服務(wù)器自己使用私鑰對(duì) Token 簽名诸衔,公開公鑰盯漂。信任這個(gè)認(rèn)證服務(wù)器的業(yè)務(wù)服務(wù)器保存公鑰,用于驗(yàn)證簽名笨农。幸好就缆,JWT 不僅可以使用 HMAC 簽名,也可以使用 RSA(一種非對(duì)稱加密算法)簽名谒亦。

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

為了防止這種情況發(fā)生,就需要在認(rèn)證服務(wù)器產(chǎn)生 Token 的時(shí)候锁摔,把使用該 Token 的業(yè)務(wù)服務(wù)器的信息記錄在 Token 中廓旬,這樣當(dāng)另一個(gè)業(yè)務(wù)服務(wù)器拿到這個(gè) Token 的時(shí)候,發(fā)現(xiàn)它并不是自己應(yīng)該驗(yàn)證的 Token谐腰,就可以直接拒絕孕豹。

現(xiàn)在涩盾,認(rèn)證服務(wù)器不信任業(yè)務(wù)服務(wù)器,業(yè)務(wù)服務(wù)器相互也不信任励背,但前端是信任這些服務(wù)器的——如果前端不信任春霍,就不會(huì)拿 Token 去請求驗(yàn)證。那么為什么會(huì)信任叶眉?可能是因?yàn)檫@些是同一家公司或者同一個(gè)項(xiàng)目中提供的若干服務(wù)構(gòu)成的服務(wù)體系址儒。

但是,前端信任不代表用戶信任竟闪。如果 Token 不沒有攜帶用戶隱私(比如姓名)离福,那么用戶不會(huì)關(guān)心信任問題。但如果 Token 含有用戶隱私的時(shí)候炼蛤,用戶得關(guān)心信任問題了妖爷。這時(shí)候認(rèn)證服務(wù)就不得不再啰嗦一些,當(dāng)用戶請求 Token 的時(shí)候理朋,問上一句絮识,你真的要授權(quán)給某某某業(yè)務(wù)服務(wù)嗎?而這個(gè)“某某某”嗽上,用戶怎么知道它是不是真的“某某某”呢次舌?用戶當(dāng)然不知道,甚至認(rèn)證服務(wù)也不知道兽愤,因?yàn)楣€已經(jīng)公開了彼念,任何一個(gè)業(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)險(xiǎn)徘郭,由該業(yè)務(wù)服務(wù)自行承擔(dān)。現(xiàn)在認(rèn)證服務(wù)可以清楚的告訴用戶丧肴,“某某某”服務(wù)是什么了残揉。如果用戶還是不夠信任,認(rèn)證服務(wù)甚至可以問芋浮,某某某業(yè)務(wù)服務(wù)需要請求 A冲甘、B、C 三項(xiàng)個(gè)人數(shù)據(jù),其中 A 是必須的江醇,不然它不工作濒憋,是否允許授權(quán)?如果你授權(quán)陶夜,我就把你授權(quán)的幾項(xiàng)數(shù)據(jù)加密放在 Token 中……

廢話了這么多凛驮,有沒有似曾相識(shí)……對(duì)了,這類似開放式 API 的認(rèn)證過程条辟。開發(fā)式 API 多采用 OAuth 認(rèn)證黔夭,而關(guān)于 OAuth 的探討資源非常豐富,這里就不深究了羽嫡。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末本姥,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子杭棵,更是在濱河造成了極大的恐慌婚惫,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件魂爪,死亡現(xiàn)場離奇詭異先舷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)滓侍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門蒋川,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人撩笆,你說我怎么就攤上這事捺球。” “怎么了夕冲?”我有些...
    開封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵懒构,是天一觀的道長。 經(jīng)常有香客問我耘擂,道長,這世上最難降的妖魔是什么絮姆? 我笑而不...
    開封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任醉冤,我火速辦了婚禮,結(jié)果婚禮上篙悯,老公的妹妹穿的比我還像新娘蚁阳。我一直安慰自己,他們只是感情好鸽照,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開白布螺捐。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪定血。 梳的紋絲不亂的頭發(fā)上赔癌,一...
    開封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音澜沟,去河邊找鬼灾票。 笑死,一個(gè)胖子當(dāng)著我的面吹牛茫虽,可吹牛的內(nèi)容都是我干的刊苍。 我是一名探鬼主播,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼濒析,長吁一口氣:“原來是場噩夢啊……” “哼正什!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起号杏,我...
    開封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤婴氮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后馒索,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體莹妒,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年绰上,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了旨怠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蜈块,死狀恐怖鉴腻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情百揭,我是刑警寧澤爽哎,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站器一,受9級(jí)特大地震影響课锌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜祈秕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一渺贤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧请毛,春花似錦志鞍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽统翩。三九已至,卻和暖如春此洲,著一層夾襖步出監(jiān)牢的瞬間厂汗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來泰國打工黍翎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留面徽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓匣掸,卻偏偏與公主長得像趟紊,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子碰酝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

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

  • 不久前霎匈,我在在前后端分離實(shí)踐中提到了基于 Token 的認(rèn)證,現(xiàn)在我們稍稍深入一些送爸。 通常情況下铛嘱,我們在討論某個(gè)技...
    simple_50a1閱讀 1,529評(píng)論 3 40
  • 幾個(gè)文章:https://segmentfault.com/a/1190000013010835http://ww...
    流動(dòng)碼文閱讀 563評(píng)論 0 4
  • 不久前,我在在前后端分離實(shí)踐中提到了基于 Token 的認(rèn)證袭厂,現(xiàn)在我們稍稍深入一些墨吓。 通常情況下,我們在討論某個(gè)技...
    邊城狂人閱讀 1,383評(píng)論 0 14
  • 我們小時(shí)候常被大人引誘著去思考纹磺,長大了以后做什么帖烘,不斷地教育、提醒橄杨、栽培的努力為我們長大后做準(zhǔn)備秘症。逐漸的我們習(xí)...
    蝴蝶蘭的夢閱讀 471評(píng)論 0 4
  • 上一個(gè)反思日記從3月21日開始寫到4月8日,后來就放棄了(堅(jiān)持了11天)式矫。 這些天感覺沒寫反思日記失去了很多(如:...
    Jacub閱讀 339評(píng)論 0 1