認(rèn)證、授權(quán)與憑證
什么是認(rèn)證(Authentication)酪惭?
- 通俗地講就是驗(yàn)證當(dāng)前用戶的身份是否合法的過程希痴,即你是誰?證明“你是你自己”(比如:你每天上下班打卡春感,都需要通過指紋打卡砌创,當(dāng)你的指紋和系統(tǒng)里錄入的指紋相匹配時(shí),就打卡成功)
- 互聯(lián)網(wǎng)中的認(rèn)證:用戶名密碼登錄鲫懒;郵箱發(fā)送登錄鏈接嫩实;手機(jī)號(hào)接收驗(yàn)證碼。只要你能收到郵箱/驗(yàn)證碼窥岩,就默認(rèn)你是賬號(hào)的主人甲献!認(rèn)證主要是為了保護(hù)系統(tǒng)的隱私數(shù)據(jù)與資源。
- 拓展:什么是會(huì)話谦秧?認(rèn)證通過后竟纳,為了避免用戶每次操作都進(jìn)行認(rèn)證(除銀行轉(zhuǎn)賬等),可以將用戶信息保存在會(huì)話中疚鲤,會(huì)話就是系統(tǒng)為了保存當(dāng)前用戶的登錄狀態(tài)所提供的機(jī)制锥累,常見的有基于Session和token的方式,具體見下文集歇。
什么是授權(quán)(Authorization)桶略?
- 簡(jiǎn)單來講就是誰(who)對(duì)什么(what)進(jìn)行了什么操作(how)。認(rèn)證是保證用戶的合法性,授權(quán)則是為了更細(xì)粒度的對(duì)隱私數(shù)據(jù)的劃分际歼。*授權(quán)是在認(rèn)證通過后惶翻,控制不同的用戶訪問不同的資源。
- 用戶授予第三方應(yīng)用訪問該用戶某些資源的權(quán)限鹅心。比如吕粗,你在安裝手機(jī)應(yīng)用的時(shí)候,APP 會(huì)詢問是否允許授予權(quán)限(訪問相冊(cè)旭愧、地理位置等權(quán)限)颅筋;你在訪問微信小程序時(shí),當(dāng)?shù)卿洉r(shí)输枯,小程序會(huì)詢問是否允許授予權(quán)限(獲取昵稱议泵、頭像、地區(qū)桃熄、性別等個(gè)人信息)
- 實(shí)現(xiàn)授權(quán)的方式有:業(yè)界通诚瓤冢基于R(role/resource)BAC實(shí)現(xiàn)授權(quán):(1)基于角色的訪問控制(2)基于資源(權(quán)限)的訪問控制,系統(tǒng)設(shè)計(jì)時(shí)定義好某項(xiàng)操作的權(quán)限標(biāo)識(shí)瞳收,系統(tǒng)擴(kuò)展性好碉京。
什么是憑證(Credentials)
- 實(shí)現(xiàn)認(rèn)證和授權(quán)的前提是需要一種媒介(證書) 來標(biāo)記訪問者的身份。
- 例如:在戰(zhàn)國時(shí)期缎讼,商鞅變法收夸,發(fā)明了照身帖。照身帖由官府發(fā)放血崭,是一塊打磨光滑細(xì)密的竹板卧惜,上面刻有持有人的頭像和籍貫信息。國人必須持有夹纫,如若沒有就被認(rèn)為是黑戶咽瓷,或者間諜之類的。在現(xiàn)實(shí)生活中舰讹,每個(gè)人都會(huì)有一張專屬的居民身份證茅姜,是用于證明持有人身份的一種法定證件。通過身份證月匣,我們可以辦理手機(jī)卡/銀行卡/個(gè)人貸款/交通出行等等钻洒,這就是認(rèn)證的憑證。
- 在互聯(lián)網(wǎng)應(yīng)用中锄开,一般網(wǎng)站會(huì)有兩種模式素标,游客模式和登錄模式。游客模式下萍悴,可以正常瀏覽網(wǎng)站上面的文章头遭,一旦想要點(diǎn)贊/收藏/分享文章寓免,就需要登錄或者注冊(cè)賬號(hào)。當(dāng)用戶登錄成功后计维,服務(wù)器會(huì)給該用戶使用的瀏覽器頒發(fā)一個(gè)令牌(token)袜香,這個(gè)令牌用來表明你的身份,每次瀏覽器發(fā)送請(qǐng)求時(shí)會(huì)帶上這個(gè)令牌鲫惶,就可以使用游客模式下無法使用的功能蜈首。
Cookie與Session
什么是Cookie?
- HTTP 是無狀態(tài)的協(xié)議(對(duì)于事務(wù)處理沒有記憶能力剑按,每次客戶端和服務(wù)端會(huì)話完成時(shí)疾就,服務(wù)端不會(huì)保存任何會(huì)話信息):每個(gè)請(qǐng)求都是完全獨(dú)立的,服務(wù)端無法確認(rèn)當(dāng)前訪問者的身份信息艺蝴,無法分辨上一次的請(qǐng)求發(fā)送者和這一次的發(fā)送者是不是同一個(gè)人。所以服務(wù)器與瀏覽器為了進(jìn)行會(huì)話跟蹤(知道是誰在訪問我)鸟废,就必須主動(dòng)的去維護(hù)一個(gè)狀態(tài)猜敢,這個(gè)狀態(tài)用于告知服務(wù)端前后兩個(gè)請(qǐng)求是否來自同一瀏覽器。而這個(gè)狀態(tài)需要通過 cookie 或者 session 去實(shí)現(xiàn)盒延。
- cookie 存儲(chǔ)在客戶端: cookie 是服務(wù)器發(fā)送到用戶瀏覽器并保存在本地的一小塊數(shù)據(jù)缩擂,它會(huì)在瀏覽器下次向同一服務(wù)器再發(fā)起請(qǐng)求時(shí)被攜帶并發(fā)送到服務(wù)器上。
- cookie 是不可跨域的: 每個(gè) cookie 都會(huì)綁定單一的域名添寺,無法在別的域名下獲取使用胯盯,一級(jí)域名和二級(jí)域名之間是允許共享使用的(靠的是 domain)。
cookie 重要的屬性
什么是 Session
- session 是另一種記錄服務(wù)器和客戶端會(huì)話狀態(tài)的機(jī)制计露,即告訴服務(wù)端前后兩個(gè)請(qǐng)求是否來自同一個(gè)客戶端(瀏覽器)博脑,知道誰在訪問我。因?yàn)閔ttp本身是無狀態(tài)協(xié)議票罐,這樣叉趣,無法確定你的本次請(qǐng)求和上次請(qǐng)求是不是你發(fā)送的。如果要進(jìn)行類似論壇登陸相關(guān)的操作该押,就實(shí)現(xiàn)不了了疗杉。
- session 是基于 cookie 實(shí)現(xiàn)的,session 存儲(chǔ)在服務(wù)器端蚕礼,sessionId 會(huì)被存儲(chǔ)到客戶端的cookie 中烟具。ps:還有一種是瀏覽器禁用了cookie或不支持cookie,這種可以通過URL重寫的方式發(fā)到服務(wù)器奠蹬;
session 認(rèn)證流程(如上圖):
- 用戶第一次請(qǐng)求服務(wù)器的時(shí)候朝聋,服務(wù)器根據(jù)用戶提交的相關(guān)信息,創(chuàng)建對(duì)應(yīng)的 Session
- 請(qǐng)求返回時(shí)將此 Session 的唯一標(biāo)識(shí)信息 SessionID 返回給瀏覽器
- 瀏覽器接收到服務(wù)器返回的 SessionID 信息后罩润,會(huì)將此信息存入到 Cookie 中玖翅,同時(shí) Cookie 記錄此 SessionID 屬于哪個(gè)域名
- 當(dāng)用戶第二次訪問服務(wù)器的時(shí)候翼馆,請(qǐng)求會(huì)自動(dòng)判斷此域名下是否存在 Cookie 信息,如果存在自動(dòng)將 Cookie 信息也發(fā)送給服務(wù)端金度,服務(wù)端會(huì)從 Cookie 中獲取 SessionID应媚,再根據(jù) SessionID 查找對(duì)應(yīng)的 Session 信息,如果沒有找到說明用戶沒有登錄或者登錄失效猜极,如果找到 Session 證明用戶已經(jīng)登錄可執(zhí)行后面操作中姜。
根據(jù)以上流程可知,SessionID 是連接 Cookie 和 Session 的一道橋梁跟伏,大部分系統(tǒng)也是根據(jù)此原理來驗(yàn)證用戶登錄狀態(tài)丢胚。
基于Session的認(rèn)證機(jī)制由Servlet規(guī)范定制,Servlet容器已經(jīng)實(shí)現(xiàn)受扳,用戶通過HttpSession的操作方法可以實(shí)現(xiàn):
Cookie 和 Session 的區(qū)別
- 安全性:Session 是存儲(chǔ)在服務(wù)器端的携龟,Cookie 是存儲(chǔ)在客戶端的。所以 Session 相比 Cookie 安全勘高,
- 存取值的類型不同:Cookie 只支持存字符串?dāng)?shù)據(jù)峡蟋,想要設(shè)置其他類型的數(shù)據(jù),需要將其轉(zhuǎn)換成字符串华望,Session 可以存任意數(shù)據(jù)類型蕊蝗。
- 有效期不同: Cookie 可設(shè)置為長時(shí)間保持,比如我們經(jīng)常使用的默認(rèn)登錄功能赖舟,Session 一般失效時(shí)間較短蓬戚,客戶端關(guān)閉(默認(rèn)情況下)或者 Session 超時(shí)都會(huì)失效。
- 存儲(chǔ)大小不同: 單個(gè) Cookie 保存的數(shù)據(jù)不能超過 4K宾抓,Session 可存儲(chǔ)數(shù)據(jù)遠(yuǎn)高于 Cookie子漩,但是當(dāng)訪問量過多,會(huì)占用過多的服務(wù)器資源洞慎。
拓展:Session痛點(diǎn)
看起來通過 cookie + session 的方式是解決了問題痛单, 但是我們忽略了一個(gè)問題,上述情況能正常工作是因?yàn)槲覀兗僭O(shè) server 是單機(jī)工作的劲腿,但實(shí)際在生產(chǎn)上旭绒,為了保障高可用,一般服務(wù)器至少需要兩臺(tái)機(jī)器焦人,通過負(fù)載均衡的方式來決定到底請(qǐng)求該打到哪臺(tái)機(jī)器上挥吵。
假設(shè)登錄請(qǐng)求打到了 A 機(jī)器,A 機(jī)器生成了 session 并在 cookie 里添加 sessionId 返回給了瀏覽器花椭,那么問題來了:下次添加購物車時(shí)如果請(qǐng)求打到了 B 或者 C忽匈,由于 session 是在 A 機(jī)器生成的,此時(shí)的 B,C 是找不到 session 的矿辽,那么就會(huì)發(fā)生無法添加購物車的錯(cuò)誤丹允,就得重新登錄了郭厌,此時(shí)請(qǐng)問該怎么辦。主要有以下三種方式:
(1 )session 復(fù)制
A 生成 session 后復(fù)制到 B, C雕蔽,這樣每臺(tái)機(jī)器都有一份 session折柠,無論添加購物車的請(qǐng)求打到哪臺(tái)機(jī)器,由于 session 都能找到批狐,故不會(huì)有問題
這種方式雖然可行扇售,但缺點(diǎn)也很明顯:
- 同一樣的一份 session 保存了多份,數(shù)據(jù)冗余
- 如果節(jié)點(diǎn)少還好嚣艇,但如果節(jié)點(diǎn)多的話承冰,特別是像阿里,微信這種由于 DAU 上億食零,可能需要部署成千上萬臺(tái)機(jī)器困乒,這樣節(jié)點(diǎn)增多復(fù)制造成的性能消耗也會(huì)很大。
(2)session 粘連
這種方式是讓每個(gè)客戶端請(qǐng)求只打到固定的一臺(tái)機(jī)器上贰谣,比如瀏覽器登錄請(qǐng)求打到 A 機(jī)器后顶燕,后續(xù)所有的添加購物車請(qǐng)求也都打到 A 機(jī)器上,Nginx 的 sticky 模塊可以支持這種方式冈爹,支持按 ip 或 cookie 粘連等等,如按 ip 粘連方式如下
這樣的話每個(gè) client 請(qǐng)求到達(dá) Nginx 后欧引,只要它的 ip 不變频伤,根據(jù) ip hash 算出來的值會(huì)打到固定的機(jī)器上,也就不存在 session 找不到的問題了芝此,當(dāng)然不難看出這種方式缺點(diǎn)也是很明顯憋肖,對(duì)應(yīng)的機(jī)器掛了怎么辦?
(3)session 共享
這種方式也是目前各大公司普遍采用的方案婚苹,將 session 保存在 redis岸更,memcached 等中間件中,請(qǐng)求到來時(shí)膊升,各個(gè)機(jī)器去這些中間件取一下 session 即可怎炊。
缺點(diǎn)其實(shí)也不難發(fā)現(xiàn),就是每個(gè)請(qǐng)求都要去 redis 取一下 session廓译,多了一次內(nèi)部連接,消耗了一點(diǎn)性能,另外為了保證 redis 的高可用省艳,必須做集群颁股,當(dāng)然了對(duì)于大公司來說, redis 集群基本都會(huì)部署,所以這方案可以說是大公司的首選了征绸。
Token(令牌)與 JWT(跨域認(rèn)證)
Token概述(no session!)
通過上文分析我們知道通過在服務(wù)端共享 session 的方式可以完成用戶的身份定位久橙,但是不難發(fā)現(xiàn)也有一個(gè)小小的瑕疵:搞個(gè)校驗(yàn)機(jī)制我還得搭個(gè) redis 集群俄占?大廠確實(shí) redis 用得比較普遍,但對(duì)于小廠來說可能它的業(yè)務(wù)量還未達(dá)到用 redis 的程度淆衷,所以有沒有其他不用 server 存儲(chǔ) session 的用戶身份校驗(yàn)機(jī)制呢缸榄,使用token!
簡(jiǎn)單來說:首先請(qǐng)求方輸入自己的用戶名吭敢,密碼碰凶,然后 server 據(jù)此生成 token,客戶端拿到 token 后會(huì)保存到本地(token存儲(chǔ)在瀏覽器端)鹿驼,之后向 server 請(qǐng)求時(shí)在請(qǐng)求頭帶上此 token 即可(server有校驗(yàn)機(jī)制欲低,檢驗(yàn)token合法性,同時(shí)server通過token中攜帶的uid確定是誰在訪問它)畜晰。
可以看到 token 主要由三部分組成:
- header:指定了簽名算法
- payload:可以指定用戶 id砾莱,過期時(shí)間等非敏感數(shù)據(jù)
- Signature: 簽名,server 根據(jù) header 知道它該用哪種簽名算法凄鼻,再用密鑰根據(jù)此簽名算法對(duì) head + payload 生成簽名腊瑟,這樣一個(gè) token 就生成了。
當(dāng) server 收到瀏覽器傳過來的 token 時(shí)块蚌,它會(huì)首先取出 token 中的 header + payload闰非,根據(jù)密鑰生成簽名,然后再與 token 中的簽名比對(duì)峭范,如果成功則說明簽名是合法的财松,即 token 是合法的。而且你會(huì)發(fā)現(xiàn) payload 中存有我們的 userId纱控,所以拿到 token 后直接在 payload 中就可獲取 userid辆毡,避免了像 session 那樣要從 redis 去取的開銷。
你會(huì)發(fā)現(xiàn)這種方式確實(shí)很妙甜害,只要 server 保證密鑰不泄露舶掖,那么生成的 token 就是安全的,因?yàn)槿绻麄卧?token 的話在簽名驗(yàn)證環(huán)節(jié)是無法通過的尔店,就此即可判定 token 非法眨攘。
可以看到通過這種方式有效地避免了 token 必須保存在 server 的弊端,實(shí)現(xiàn)了分布式存儲(chǔ)闹获,不過需要注意的是期犬,token 一旦由 server 生成,它就是有效的避诽,直到過期龟虎,無法讓 token 失效,除非在 server 為 token 設(shè)立一個(gè)黑名單沙庐,在校驗(yàn) token 前先過一遍此黑名單鲤妥,如果在黑名單里則此 token 失效佳吞,但一旦這樣做的話,那就意味著黑名單就必須保存在 server棉安,這又回到了 session 的模式底扳,那直接用 session 不香嗎。所以一般的做法是當(dāng)客戶端登出要讓 token 失效時(shí)贡耽,直接在本地移除 token 即可衷模,下次登錄重新生成 token 就好。
另外需要注意的是 token 一般是放在 header 的 Authorization 自定義頭里蒲赂,不是放在 Cookie 里的阱冶,這主要是為了解決跨域不能共享 Cookie 的問題
總結(jié):token解決什么問題(為什么要用token)?
- 完全由應(yīng)用管理滥嘴,可以避開同源策略
- 支持跨域訪問木蹬,cookie不支持, Cookie 跨站是不能共享的若皱,這樣的話如果你要實(shí)現(xiàn)多應(yīng)用(多系統(tǒng))的單點(diǎn)登錄(SSO)镊叁,使用 Cookie 來做需要的話就很困難了。但如果用 token 來實(shí)現(xiàn) SSO 會(huì)非常簡(jiǎn)單走触,只要在 header 中的 authorize 字段(或其他自定義)加上 token 即可完成所有跨域站點(diǎn)的認(rèn)證晦譬。
- token是無狀態(tài)的,可以在多個(gè)服務(wù)器間共享
- token可以避免CSRF攻擊(跨站請(qǐng)求攻擊)
- 易于擴(kuò)展互广,在移動(dòng)端原生請(qǐng)求是沒有 cookie 之說的蛔添,而 sessionid 依賴于 cookie,sessionid 就不能用 cookie 來傳了兜辞,如果用 token 的話,由于它是隨著 header 的 authoriize 傳過來的夸溶,也就不存在此問題逸吵,換句話說token 天生支持移動(dòng)平臺(tái),可擴(kuò)展性好
拓展1:那啥是CSRF呢缝裁?
攻擊者通過一些技術(shù)手段欺騙用戶的瀏覽器去訪問一個(gè)自己曾經(jīng)認(rèn)證過的網(wǎng)站并運(yùn)行一些操作(如發(fā)郵件扫皱,發(fā)消息,甚至財(cái)產(chǎn)操作如轉(zhuǎn)賬和購買商品)捷绑。由于瀏覽器曾經(jīng)認(rèn)證過(cookie 里帶來 sessionId 等身份認(rèn)證的信息)韩脑,所以被訪問的網(wǎng)站會(huì)認(rèn)為是真正的用戶操作而去運(yùn)行。
那么如果正常的用戶誤點(diǎn)了上面這張圖片粹污,由于相同域名的請(qǐng)求會(huì)自動(dòng)帶上 cookie段多,而 cookie 里帶有正常登錄用戶的 sessionid,類似上面這樣的轉(zhuǎn)賬操作在 server 就會(huì)成功壮吩,會(huì)造成極大的安全風(fēng)險(xiǎn)
CSRF 攻擊的根本原因在于對(duì)于同樣域名的每個(gè)請(qǐng)求來說进苍,它的 cookie 都會(huì)被自動(dòng)帶上加缘,這個(gè)是瀏覽器的機(jī)制決定的!
至于完成一次CSRF攻擊必要的兩個(gè)步驟:
1觉啊、首先登了一個(gè)正常的網(wǎng)站A拣宏,并且在本地生成了cookie
2、在cookie有效時(shí)間內(nèi)杠人,訪問了危險(xiǎn)網(wǎng)站B(就獲取了身份信息)
Q:那我不訪問危險(xiǎn)網(wǎng)站就完了唄勋乾?
A:危險(xiǎn)網(wǎng)站也許只是個(gè)存在漏洞的可信任網(wǎng)站!
Q:那我訪問完正常網(wǎng)站嗡善,關(guān)了瀏覽器就好了呀辑莫?
A:即使關(guān)閉瀏覽器,cookie也不保證一定立即失效滤奈,而且關(guān)閉瀏覽器并不能結(jié)束會(huì)話摆昧,session的生命周期跟這些都沒關(guān)系。
拓展2:同源策略蜒程?
- 就是不同源的客戶端腳本在沒有明確授權(quán)情況下绅你,不準(zhǔn)讀寫對(duì)方的資源!
- 同源就是:協(xié)議昭躺、域名與端口號(hào)都相同忌锯。
- 同源策略是由 Netscape 提出的著名安全策略,是瀏覽器最核心领炫、基本的安全功能偶垮,它限制了一個(gè)源中加載腳本與來自其他源中資源的交互方式。
拓展3:什么是跨域帝洪,如何解決似舵?
當(dāng)瀏覽器執(zhí)行腳本時(shí)會(huì)檢查是否同源,只有同源的腳本才會(huì)執(zhí)行葱峡,如果不同源即為跨域砚哗。
產(chǎn)生原因:它是由瀏覽器的同源策略造成的,是瀏覽器對(duì)JavaScript實(shí)施的安全限制砰奕。
-
解決方案:
- nginx(靜態(tài)服務(wù)器)反向代理解決跨域(前端常用)蛛芥,a明確訪問c代理服務(wù)器,但是不知道c的內(nèi)容從哪里來军援,c反向從別的地方拿來數(shù)據(jù)仅淑。(忽略的是目標(biāo)地址),瀏覽器可以訪問a,而服務(wù)器之間不存在跨域問題胸哥,瀏覽器先訪問a的服務(wù)器c涯竟,讓c服務(wù)器作為代理去訪問b服務(wù)器,拿到之后再返回?cái)?shù)據(jù)給a。
- jsonp:通常為了減輕web服務(wù)器的負(fù)載,我們把js昆禽、css蝗蛙、圖片等靜態(tài)資源分離到另一臺(tái)獨(dú)立域名的服務(wù)器上,在html頁面中再通過相應(yīng)的標(biāo)簽從不同域名下加載靜態(tài)資源醉鳖,而被瀏覽器允許捡硅。
- 添加響應(yīng)頭
拓展4:易于擴(kuò)展?
- 比如有多臺(tái)服務(wù)器盗棵,使用負(fù)載均衡壮韭,第一次登錄轉(zhuǎn)發(fā)到了A,A中seesion緩存了用戶的登錄信息纹因,第二次登錄轉(zhuǎn)發(fā)到了B喷屋,這時(shí)候就丟失了登錄狀態(tài),當(dāng)然這樣也是有解決方案可以共享session瞭恰,但token只需要所有的服務(wù)器使用相同的解密手段即可屯曹。
拓展5:無狀態(tài)?
- 服務(wù)端不保存客戶端請(qǐng)求者的任何信息惊畏,客戶端每次請(qǐng)求必須自備描述信息恶耽,通過這些信息來識(shí)別客戶端身份。服務(wù)端只需要確認(rèn)該token是否是自己親自簽發(fā)即可颜启,簽發(fā)和驗(yàn)證都在服務(wù)端進(jìn)行偷俭。
拓展6:什么是單點(diǎn)登錄?
- 所謂單點(diǎn)登錄缰盏,是指在多個(gè)應(yīng)用系統(tǒng)中涌萤,用戶只需要登錄一次就可以訪問所有相互信任的應(yīng)用系統(tǒng)。
Acesss Token
- 訪問資源接口(API)時(shí)所需要的資源憑證
- 簡(jiǎn)單 token 的組成: uid(用戶唯一的身份標(biāo)識(shí))口猜、time(當(dāng)前時(shí)間的時(shí)間戳)负溪、sign(簽名,token 的前幾位以哈希算法壓縮成的一定長度的十六進(jìn)制字符串)
特點(diǎn):
- 服務(wù)端無狀態(tài)化济炎、可擴(kuò)展性好
- 支持移動(dòng)端設(shè)備
- 安全性高
- 支持跨程序調(diào)用
token 的身份驗(yàn)證流程:
- 客戶端使用用戶名跟密碼請(qǐng)求登錄
- 服務(wù)端收到請(qǐng)求笙以,去驗(yàn)證用戶名與密碼
- 驗(yàn)證成功后,服務(wù)端會(huì)簽發(fā)一個(gè) token 并把這個(gè) token 發(fā)送給客戶端
- 客戶端收到 token 以后冻辩,會(huì)把它存儲(chǔ)起來,比如放在 cookie 里或者 localStorage 里
- 客戶端每次向服務(wù)端請(qǐng)求資源的時(shí)候需要帶著服務(wù)端簽發(fā)的 token
- 服務(wù)端收到請(qǐng)求拆祈,然后去驗(yàn)證客戶端請(qǐng)求里面帶著的 token 恨闪,如果驗(yàn)證成功,就向客戶端返回請(qǐng)求的數(shù)據(jù)
注意點(diǎn):
- 每一次請(qǐng)求都需要攜帶 token放坏,需要把 token 放到 HTTP 的 Header 里
- 基于 token 的用戶認(rèn)證是一種服務(wù)端無狀態(tài)的認(rèn)證方式咙咽,服務(wù)端不用存放 token 數(shù)據(jù)。用解析 token 的計(jì)算時(shí)間換取 session 的存儲(chǔ)空間淤年,從而減輕服務(wù)器的壓力钧敞,減少頻繁的查詢數(shù)據(jù)庫
- token 完全由應(yīng)用管理蜡豹,所以它可以避開同源策略
Refresh Token
- 另外一種 token——refresh token
- refresh token 是專用于刷新 access token 的 token。如果沒有 refresh token溉苛,也可以刷新 access token镜廉,但每次刷新都要用戶輸入登錄用戶名與密碼,會(huì)很麻煩愚战。有了 refresh token娇唯,可以減少這個(gè)麻煩,客戶端直接用 refresh token 去更新 access token寂玲,無需用戶進(jìn)行額外的操作塔插。
兩者區(qū)別:
- Access Token 的有效期比較短,當(dāng) Acesss Token 由于過期而失效時(shí)拓哟,使用 Refresh Token 就可以獲取到新的 Token想许,如果 Refresh Token 也失效了,用戶就只能重新登錄了断序。
- Refresh Token 及過期時(shí)間是存儲(chǔ)在服務(wù)器的數(shù)據(jù)庫中流纹,只有在申請(qǐng)新的 Acesss Token 時(shí)才會(huì)驗(yàn)證,不會(huì)對(duì)業(yè)務(wù)接口響應(yīng)時(shí)間造成影響逢倍,也不需要向 Session 一樣一直保持在內(nèi)存中以應(yīng)對(duì)大量的請(qǐng)求捧颅。
Token 的缺點(diǎn)
那有人就問了,既然 token 這么好较雕,那為什么各個(gè)大公司幾乎都采用共享 session 的方式呢碉哑,可能很多人是第一次聽到 token,token 不香嗎? token 有以下兩點(diǎn)劣勢(shì):
token 太長了:token 是 header, payload 編碼后的樣式亮蒋,所以一般要比 sessionId 長很多扣典,很有可能超出 cookie 的大小限制(cookie 一般有大小限制的,如 4kb)慎玖,如果你在 token 中存儲(chǔ)的信息越長贮尖,那么 token 本身也會(huì)越長,這樣的話由于你每次請(qǐng)求都會(huì)帶上 token趁怔,對(duì)請(qǐng)求來是個(gè)不小的負(fù)擔(dān)
不太安全:網(wǎng)上很多文章說 token 更安全湿硝,其實(shí)不然,細(xì)心的你可能發(fā)現(xiàn)了润努,我們說 token 是存在瀏覽器的关斜,再細(xì)問,存在瀏覽器的哪里铺浇?既然它太長放在 cookie 里可能導(dǎo)致 cookie 超限痢畜,那就只好放在 local storage 里,這樣會(huì)造成安全隱患,因?yàn)?local storage 這類的本地存儲(chǔ)是可以被 JS 直接讀取的丁稀,另外由上文也提到吼拥,token 一旦生成無法讓其失效,必須等到其過期才行线衫,這樣的話如果服務(wù)端檢測(cè)到了一個(gè)安全威脅凿可,也無法使相關(guān)的 token 失效。
所以 token 更適合一次性的命令認(rèn)證桶雀,設(shè)置一個(gè)比較短的有效期?蠼汀!矗积!
拓展:不管是 cookie 還是 token全肮,從存儲(chǔ)角度來看其實(shí)都不安全(實(shí)際上防護(hù) CSRF 攻擊的正確方式是用 CSRF token),都有暴露的風(fēng)險(xiǎn)棘捣,我們所說的安全更多的是強(qiáng)調(diào)傳輸中的安全辜腺,可以用 HTTPS 協(xié)議來傳輸, 這樣的話請(qǐng)求頭都能被加密乍恐,也就保證了傳輸中的安全评疗。
其實(shí)我們把 cookie 和 token 比較本身就不合理,一個(gè)是存儲(chǔ)方式茵烈,一個(gè)是驗(yàn)證方式百匆,正確的比較應(yīng)該是 session vs token。
Token 和 Session 的區(qū)別
token和session其實(shí)都是為了身份驗(yàn)證呜投,session一般翻譯為會(huì)話加匈,而token更多的時(shí)候是翻譯為令牌;session和token都是有過期時(shí)間一說仑荐,都需要去管理過期時(shí)間雕拼;
Session 是一種記錄服務(wù)器和客戶端會(huì)話狀態(tài)的機(jī)制,使服務(wù)端有狀態(tài)化粘招,可以記錄會(huì)話信息(可能保存在緩存啥寇、文件或數(shù)據(jù)庫)。而 Token 是令牌洒扎,訪問資源接口(API)時(shí)所需要的資源憑證辑甜。Token 使服務(wù)端無狀態(tài)化,不會(huì)存儲(chǔ)會(huì)話信息袍冷。
其實(shí)token與session的問題是一種時(shí)間與空間的博弈問題磷醋,session是空間換時(shí)間,而token是時(shí)間換空間难裆。兩者的選擇要看具體情況而定。雖然確實(shí)都是“客戶端記錄,每次訪問攜帶”乃戈,但 token 很容易設(shè)計(jì)為自包含的褂痰,也就是說,后端不需要記錄什么東西症虑,每次一個(gè)無狀態(tài)請(qǐng)求缩歪,每次解密驗(yàn)證,每次當(dāng)場(chǎng)得出合法 /非法的結(jié)論谍憔。這一切判斷依據(jù)匪蝙,除了固化在 CS 兩端的一些邏輯之外,整個(gè)信息是自包含的习贫。這才是真正的無狀態(tài)逛球。 而 sessionid ,一般都是一段隨機(jī)字符串苫昌,需要到后端去檢索 id 的有效性颤绕。萬一服務(wù)器重啟導(dǎo)致內(nèi)存里的 session 沒了呢?萬一 redis 服務(wù)器掛了呢祟身?
所謂 Session 認(rèn)證只是簡(jiǎn)單的把 User 信息存儲(chǔ)到 Session 里奥务,因?yàn)?SessionID 的不可預(yù)測(cè)性,暫且認(rèn)為是安全的袜硫。而 Token 氯葬,如果指的是 OAuth Token 或類似的機(jī)制的話,提供的是 認(rèn)證 和 授權(quán) 婉陷,認(rèn)證是針對(duì)用戶帚称,授權(quán)是針對(duì) App 。其目的是讓某 App 有權(quán)利訪問某用戶的信息憨攒。這里的 Token 是唯一的世杀。不可以轉(zhuǎn)移到其它 App上,也不可以轉(zhuǎn)到其它用戶上肝集。Session 只提供一種簡(jiǎn)單的認(rèn)證瞻坝,即只要有此 SessionID ,即認(rèn)為有此 User 的全部權(quán)利杏瞻。是需要嚴(yán)格保密的所刀,這個(gè)數(shù)據(jù)應(yīng)該只保存在站方,不應(yīng)該共享給其它網(wǎng)站或者第三方 App捞挥。所以簡(jiǎn)單來說:如果你的用戶數(shù)據(jù)可能需要和第三方共享浮创,或者允許第三方調(diào)用 API 接口,用 Token 砌函。如果永遠(yuǎn)只是自己的網(wǎng)站斩披,自己的 App溜族,用什么就無所謂了。
JWT概述
JSON Web Token(簡(jiǎn)稱 JWT)是目前最流行的跨域認(rèn)證解決方案垦沉。是一種認(rèn)證授權(quán)機(jī)制煌抒。
JWT 是為了在網(wǎng)絡(luò)應(yīng)用環(huán)境間傳遞聲明而執(zhí)行的一種基于 JSON 的開放標(biāo)準(zhǔn)(RFC 7519)。JWT 的聲明一般被用來在身份提供者和服務(wù)提供者間傳遞被認(rèn)證的用戶身份信息厕倍,以便于從資源服務(wù)器獲取資源寡壮。比如用在用戶登錄上。
可以使用 HMAC 算法或者是 RSA 的公/私秘鑰對(duì) JWT 進(jìn)行簽名讹弯。因?yàn)閿?shù)字簽名的存在况既,這些傳遞的信息是可信的。
阮一峰老師的 JSON Web Token 入門教程 講的非常通俗易懂组民,這里就不再班門弄斧了
生成 JWT
JWT 的原理
JWT 認(rèn)證流程:
用戶輸入用戶名/密碼登錄棒仍,服務(wù)端認(rèn)證成功后,會(huì)返回給客戶端一個(gè) JWT
客戶端將 token 保存到本地(通常使用 localstorage邪乍,也可以使用 cookie)
當(dāng)用戶希望訪問一個(gè)受保護(hù)的路由或者資源的時(shí)候降狠,需要請(qǐng)求頭的 Authorization 字段中使用Bearer 模式添加 JWT,其內(nèi)容看起來是下面這樣
Authorization: Bearer <token>
服務(wù)端的保護(hù)路由將會(huì)檢查請(qǐng)求頭 Authorization 中的 JWT 信息庇楞,如果合法榜配,則允許用戶的行為
因?yàn)?JWT 是自包含的(內(nèi)部包含了一些會(huì)話信息),因此減少了需要查詢數(shù)據(jù)庫的需要
因?yàn)?JWT 并不使用 Cookie 的吕晌,所以你可以使用任何域名提供你的 API 服務(wù)而不需要擔(dān)心跨域資源共享問題(CORS)
因?yàn)橛脩舻臓顟B(tài)不再存儲(chǔ)在服務(wù)端的內(nèi)存中蛋褥,所以這是一種無狀態(tài)的認(rèn)證機(jī)制
JWT 的使用方式
方式一
- 當(dāng)用戶希望訪問一個(gè)受保護(hù)的路由或者資源的時(shí)候,可以把它放在 Cookie 里面自動(dòng)發(fā)送睛驳,但是這樣不能跨域烙心,所以更好的做法是放在 HTTP 請(qǐng)求頭信息的 Authorization 字段里,使用 Bearer 模式添加 JWT乏沸。
GET /calendar/v1/events
用戶的狀態(tài)不會(huì)存儲(chǔ)在服務(wù)端的內(nèi)存中淫茵,這是一種 無狀態(tài)的認(rèn)證機(jī)制
服務(wù)端的保護(hù)路由將會(huì)檢查請(qǐng)求頭 Authorization 中的 JWT 信息,如果合法蹬跃,則允許用戶的行為匙瘪。
由于 JWT 是自包含的,因此減少了需要查詢數(shù)據(jù)庫的需要
JWT 的這些特性使得我們可以完全依賴其無狀態(tài)的特性提供數(shù)據(jù) API 服務(wù)蝶缀,甚至是創(chuàng)建一個(gè)下載流服務(wù)丹喻。
因?yàn)?JWT 并不使用 Cookie ,所以你可以使用任何域名提供你的 API 服務(wù)而不需要擔(dān)心跨域資源共享問題(CORS)
方式二
- 跨域的時(shí)候翁都,可以把 JWT 放在 POST 請(qǐng)求的數(shù)據(jù)體里碍论。
方式三
- 通過 URL 傳輸
http://www.example.com/user?token=xxx
項(xiàng)目中使用 JWT
項(xiàng)目地址:https://github.com/yjdjiayou/jwt-demo
Token 和 JWT 的區(qū)別
相同:
都是訪問資源的令牌
都可以記錄用戶的信息
都是使服務(wù)端無狀態(tài)化
都是只有驗(yàn)證成功后,客戶端才能訪問服務(wù)端上受保護(hù)的資源
區(qū)別:
Token:服務(wù)端驗(yàn)證客戶端發(fā)送過來的 Token 時(shí)柄慰,還需要查詢數(shù)據(jù)庫獲取用戶信息鳍悠,然后驗(yàn)證 Token 是否有效税娜。
JWT:將 Token 和 Payload 加密后存儲(chǔ)于客戶端,服務(wù)端只需要使用密鑰解密進(jìn)行校驗(yàn)(校驗(yàn)也是 JWT 自己實(shí)現(xiàn)的)即可藏研,不需要查詢或者減少查詢數(shù)據(jù)庫巧涧,因?yàn)?JWT 自包含了用戶信息和加密的數(shù)據(jù)。
常見的前后端鑒權(quán)方式
Session-Cookie
Token 驗(yàn)證(包括 JWT遥倦,SSO)
OAuth2.0(開放授權(quán))
常見的加密算法
不可逆加密:【Hash加密算法/散列算法/摘要算法】
一旦加密就不能反向解密得到密碼原文,一般用來加密用戶密碼,app的服務(wù)器端數(shù)據(jù)庫里一般存儲(chǔ)的也都是加密后的用戶密碼占锯。
在數(shù)據(jù)傳輸?shù)倪^程中袒哥,首先把密碼類數(shù)據(jù)經(jīng)過MD5加密算法加密,然后再在外面使用可逆的加密方式加密一次消略,這樣在數(shù)據(jù)傳輸?shù)倪^程中堡称,即便數(shù)據(jù)被截獲了,但是想要完全破解艺演,還是很難的却紧。
Hash算法特別的地方在于它是一種單向算法,用戶可以通過Hash算法對(duì)目標(biāo)信息生成一段特定長度的唯一的Hash值胎撤,卻不能通過這個(gè)Hash值重新獲得目標(biāo)信息晓殊。因此Hash算法常用在不可還原的密碼存儲(chǔ)、信息完整性校驗(yàn)等伤提。
用途:一般用于效驗(yàn)下載文件正確性巫俺,一般在網(wǎng)站上下載文件都能見到;存儲(chǔ)用戶敏感信息肿男,如密碼介汹、 卡號(hào)等不可解密的信息。
常見的不可逆加密算法有:MD5舶沛、SHA嘹承、HMAC
MD5:Message Digest Algorithm MD5(中文名為消息摘要算法第五版)為計(jì)算機(jī)安全領(lǐng)域廣泛使用的一種散列函數(shù),用以提供消息的完整性保護(hù)如庭。該算法的文件號(hào)為RFC 1321(R.Rivest,MIT Laboratory for Computer Science and RSA Data Security Inc. April 1992)叹卷。 MD5即Message-Digest Algorithm 5(信息-摘要算法5),用于確保信息傳輸完整一致柱彻。是計(jì)算機(jī)廣泛使用的雜湊算法之一(又譯摘要算法豪娜、哈希算法),主流編程語言普遍已有MD5實(shí)現(xiàn)哟楷。將數(shù)據(jù)(如漢字)運(yùn)算為另一固定長度值瘤载,是雜湊算法的基礎(chǔ)原理,MD5的前身有MD2卖擅、MD3和MD4鸣奔。
MD5算法具有以下特點(diǎn): 1墨技、壓縮性:任意長度的數(shù)據(jù),算出的MD5值長度都是固定的挎狸。 2扣汪、容易計(jì)算:從原數(shù)據(jù)計(jì)算出MD5值很容易。 3锨匆、抗修改性:對(duì)原數(shù)據(jù)進(jìn)行任何改動(dòng)崭别,哪怕只修改1個(gè)字節(jié),所得到的MD5值都有很大區(qū)別恐锣。 4茅主、強(qiáng)抗碰撞:已知原數(shù)據(jù)和其MD5值,想找到一個(gè)具有相同MD5值的數(shù)據(jù)(即偽造數(shù)據(jù))是非常困難的土榴。 MD5的作用是讓大容量信息在用數(shù)字簽名軟件簽署私人密鑰前被"壓縮"成一種保密的格式(就是把一個(gè)任意長度的字節(jié)串變換成一定長的十六進(jìn)制數(shù)字串)诀姚。除了MD5以外,其中比較有名的還有sha-1玷禽、RIPEMD以及Haval等赫段。
SHA1 :安全哈希算法(Secure Hash Algorithm)主要適用于數(shù)字簽名標(biāo)準(zhǔn) (Digital Signature Standard DSS)里面定義的數(shù)字簽名算法(Digital Signature Algorithm DSA)。對(duì)于長度小于2^64位的消息矢赁,SHA1會(huì)產(chǎn)生一個(gè)160位的消息摘要糯笙。當(dāng)接收到消息的時(shí)候,這個(gè)消息摘要可以用來驗(yàn)證數(shù)據(jù)的完整性撩银。在傳輸?shù)倪^程中炬丸,數(shù)據(jù)很可能會(huì)發(fā)生變化,那么這時(shí)候就會(huì)產(chǎn)生不同的消息摘要蜒蕾。 SHA1有如下特點(diǎn): 1.不可以從消息摘要中復(fù)原信息稠炬; 2.兩個(gè)不同的消息不會(huì)產(chǎn)生同樣的消息摘要,(但會(huì)有1x10 ^ 48分之一的機(jī)率出現(xiàn)相同的消息摘要,一般使用時(shí)忽略)。
可逆加密:可逆加密有對(duì)稱加密和非對(duì)稱加密咪啡。
對(duì)稱加密:【文件加密和解密使用相同的密鑰首启,即加密密鑰也可以用作解密密鑰】
在對(duì)稱加密算法中,數(shù)據(jù)發(fā)信方將明文和加密密鑰一起經(jīng)過特殊的加密算法處理后撤摸,使其變成復(fù)雜的加密密文發(fā)送出去毅桃,收信方收到密文后,若想解讀出原文准夷,則需要使用加密時(shí)用的密鑰以及相同加密算法的逆算法對(duì)密文進(jìn)行解密钥飞,才能使其回復(fù)成可讀明文。在對(duì)稱加密算法中衫嵌,使用的密鑰只有一個(gè)读宙,收發(fā)雙方都使用這個(gè)密鑰,這就需要解密方事先知道加密密鑰楔绞。
對(duì)稱加密算法的優(yōu)點(diǎn)是算法公開结闸、計(jì)算量小唇兑、加密速度快、加密效率高桦锄。
用途:一般用于保存用戶手機(jī)號(hào)扎附、身份證等敏感但能解密的信息。
常見的對(duì)稱加密算法有AES(高級(jí)加密標(biāo)準(zhǔn))结耀、DES(數(shù)據(jù)加密算法)留夜、3DES、Blowfish图甜、IDEA香伴、RC4、RC5具则、RC6
非對(duì)稱加密:【兩個(gè)密鑰:公開密鑰(publickey)和私有密鑰,公有密鑰加密具帮,私有密鑰解密】
非對(duì)稱加密算法是一種密鑰的保密方法博肋。非對(duì)稱加密算法需要兩個(gè)密鑰:公開密鑰(publickey)和私有密鑰(privatekey)。公開密鑰與私有密鑰是一對(duì)蜂厅,如果用公開密鑰對(duì)數(shù)據(jù)進(jìn)行加密匪凡,只有用對(duì)應(yīng)的私有密鑰才能解密;如果用私有密鑰對(duì)數(shù)據(jù)進(jìn)行加密掘猿,那么只有用對(duì)應(yīng)的公開密鑰才能解密病游。因?yàn)榧用芎徒饷苁褂玫氖莾蓚€(gè)不同的密鑰,所以這種算法叫作非對(duì)稱加密算法稠通。
非對(duì)稱加密算法實(shí)現(xiàn)機(jī)密信息交換的基本過程是:甲方生成一對(duì)密鑰并將其中的一把作為公用密鑰向其它方公開衬衬;得到該公用密鑰的乙方使用該密鑰對(duì)機(jī)密信息進(jìn)行加密后再發(fā)送給甲方;甲方再用自己保存的另一把專用密鑰對(duì)加密后的信息進(jìn)行解密改橘。
服務(wù)器存私鑰滋尉,客戶端拿公鑰,客戶端加解密算法可以做成so庫飞主。
非對(duì)稱加密與對(duì)稱加密相比狮惜,其安全性更好;非對(duì)稱加密的缺點(diǎn)是加密和解密花費(fèi)時(shí)間長碌识、速度慢碾篡,只適合對(duì)少量數(shù)據(jù)進(jìn)行加密。
用途:一般用于簽名和認(rèn)證筏餐。
常見的非對(duì)稱加密算法有:RSA(公鑰加密算法)开泽、DSA(數(shù)字簽名用)、ECC(移動(dòng)設(shè)備用)魁瞪、Diffie-Hellman眼姐、El Gamal
常見問題
使用 cookie 時(shí)需要考慮的問題
因?yàn)榇鎯?chǔ)在客戶端诅迷,容易被客戶端篡改,使用前需要驗(yàn)證合法性众旗,所以罢杉,不要存儲(chǔ)敏感數(shù)據(jù),比如用戶密碼贡歧,賬戶余額滩租;使用 httpOnly 在一定程度上提高安全性
盡量減少 cookie 的體積,能存儲(chǔ)的數(shù)據(jù)量不能超過 4kb利朵;一個(gè)瀏覽器針對(duì)一個(gè)網(wǎng)站最多存 20 個(gè)Cookie律想,瀏覽器一般只允許存放 300 個(gè)Cookie
設(shè)置正確的 domain 和 path,減少數(shù)據(jù)傳輸
cookie 無法跨域(每個(gè) cookie 都會(huì)綁定單一的域名绍弟,無法在別的域名下獲取使用)
移動(dòng)端對(duì) cookie 的支持不是很好技即,而 session 需要基于 cookie 實(shí)現(xiàn),所以移動(dòng)端常用的是 token
使用 session 時(shí)需要考慮的問題
將 session 存儲(chǔ)在服務(wù)器里面樟遣,當(dāng)用戶同時(shí)在線量比較多時(shí)或油,這些 session 會(huì)占據(jù)較多的內(nèi)存含懊,需要在服務(wù)端定期的去清理過期的 session
當(dāng)網(wǎng)站采用集群部署的時(shí)候抽莱,會(huì)遇到多臺(tái) web 服務(wù)器之間如何做 session 共享的問題分瘦。因?yàn)?session 是由單個(gè)服務(wù)器創(chuàng)建的,但是處理用戶請(qǐng)求的服務(wù)器不一定是那個(gè)創(chuàng)建 session 的服務(wù)器瞻佛,那么該服務(wù)器就無法拿到之前已經(jīng)放入到 session 中的登錄憑證之類的信息了脱篙。解決方案:寫客戶端cookie的方式、服務(wù)器之間session數(shù)據(jù)同步伤柄、用mysql數(shù)據(jù)庫共享session數(shù)據(jù)绊困。
當(dāng)多個(gè)應(yīng)用要共享 session 時(shí),除了以上問題适刀,還會(huì)遇到跨域問題考抄,因?yàn)椴煌膽?yīng)用可能部署的主機(jī)不一樣,需要在各個(gè)應(yīng)用做好 cookie 跨域的處理蔗彤。
sessionId 是存儲(chǔ)在 cookie 中的川梅,假如瀏覽器禁止 cookie 或不支持 cookie 怎么辦? 一般會(huì)把 sessionId 跟在 url 參數(shù)后面即重寫 url然遏,所以 session 不一定非得需要靠 cookie 實(shí)現(xiàn)
移動(dòng)端對(duì) cookie 的支持不是很好贫途,而 session 需要基于 cookie 實(shí)現(xiàn),所以移動(dòng)端常用的是 token
使用 token 時(shí)需要考慮的問題
如果你認(rèn)為用數(shù)據(jù)庫來存儲(chǔ) token 會(huì)導(dǎo)致查詢時(shí)間太長待侵,可以選擇放在內(nèi)存當(dāng)中丢早。比如 redis 很適合你對(duì) token 查詢的需求。
token 完全由應(yīng)用管理,所以它可以避開同源策略
token 可以避免 CSRF 攻擊(因?yàn)椴恍枰?cookie 了)
移動(dòng)端對(duì) cookie 的支持不是很好怨酝,而 session 需要基于 cookie 實(shí)現(xiàn)傀缩,所以移動(dòng)端常用的是 token
使用 JWT 時(shí)需要考慮的問題
因?yàn)?JWT 并不依賴 Cookie 的,所以你可以使用任何域名提供你的 API 服務(wù)而不需要擔(dān)心跨域資源共享問題(CORS)
JWT 默認(rèn)是不加密农猬,但也是可以加密的赡艰。生成原始 Token 以后,可以用密鑰再加密一次斤葱。
JWT 不加密的情況下慷垮,不能將秘密數(shù)據(jù)寫入 JWT。
JWT 不僅可以用于認(rèn)證揍堕,也可以用于交換信息料身。有效使用 JWT,可以降低服務(wù)器查詢數(shù)據(jù)庫的次數(shù)衩茸。
JWT 最大的優(yōu)勢(shì)是服務(wù)器不再需要存儲(chǔ) Session芹血,使得服務(wù)器認(rèn)證鑒權(quán)業(yè)務(wù)可以方便擴(kuò)展。但這也是 JWT 最大的缺點(diǎn):由于服務(wù)器不需要存儲(chǔ) Session 狀態(tài)楞慈,因此使用過程中無法廢棄某個(gè) Token 或者更改 Token 的權(quán)限幔烛。也就是說一旦 JWT 簽發(fā)了,到期之前就會(huì)始終有效抖部,除非服務(wù)器部署額外的邏輯。
JWT 本身包含了認(rèn)證信息议惰,一旦泄露慎颗,任何人都可以獲得該令牌的所有權(quán)限。為了減少盜用言询,JWT的有效期應(yīng)該設(shè)置得比較短俯萎。對(duì)于一些比較重要的權(quán)限,使用時(shí)應(yīng)該再次對(duì)用戶進(jìn)行認(rèn)證运杭。
JWT 適合一次性的命令認(rèn)證夫啊,頒發(fā)一個(gè)有效期極短的 JWT,即使暴露了危險(xiǎn)也很小辆憔,由于每次操作都會(huì)生成新的 JWT撇眯,因此也沒必要保存 JWT,真正實(shí)現(xiàn)無狀態(tài)虱咧。
為了減少盜用熊榛,JWT 不應(yīng)該使用 HTTP 協(xié)議明碼傳輸,要使用 HTTPS 協(xié)議傳輸腕巡。
使用加密算法時(shí)需要考慮的問題
絕不要以明文存儲(chǔ)密碼
永遠(yuǎn)使用 哈希算法 來處理密碼玄坦,絕不要使用 Base64 或其他編碼方式來存儲(chǔ)密碼,這和以明文存儲(chǔ)密碼是一樣的,使用哈希煎楣,而不要使用編碼豺总。編碼以及加密,都是雙向的過程择懂,而密碼是保密的喻喳,應(yīng)該只被它的所有者知道, 這個(gè)過程必須是單向的休蟹。哈希正是用于做這個(gè)的沸枯,從來沒有解哈希這種說法, 但是編碼就存在解碼赂弓,加密就存在解密绑榴。
絕不要使用弱哈希或已被破解的哈希算法盈魁,像 MD5 或 SHA1 翔怎,只使用強(qiáng)密碼哈希算法。
絕不要以明文形式顯示或發(fā)送密碼杨耙,即使是對(duì)密碼的所有者也應(yīng)該這樣赤套。如果你需要 “忘記密碼” 的功能,可以隨機(jī)生成一個(gè)新的 一次性的(這點(diǎn)很重要)密碼珊膜,然后把這個(gè)密碼發(fā)送給用戶容握。
分布式架構(gòu)下 session 共享方案
1. session 復(fù)制
- 任何一個(gè)服務(wù)器上的 session 發(fā)生改變(增刪改),該節(jié)點(diǎn)會(huì)把這個(gè) session 的所有內(nèi)容序列化车柠,然后廣播給所有其它節(jié)點(diǎn)剔氏,不管其他服務(wù)器需不需要 session ,以此來保證 session 同步
優(yōu)點(diǎn): 可容錯(cuò)竹祷,各個(gè)服務(wù)器間 session 能夠?qū)崟r(shí)響應(yīng)谈跛。
缺點(diǎn): 會(huì)對(duì)網(wǎng)絡(luò)負(fù)荷造成一定壓力,如果 session 量大的話可能會(huì)造成網(wǎng)絡(luò)堵塞塑陵,拖慢服務(wù)器性能感憾。
2. 粘性 session /IP 綁定策略
- 采用 Ngnix 中的 ip_hash 機(jī)制,將某個(gè) ip的所有請(qǐng)求都定向到同一臺(tái)服務(wù)器上令花,即將用戶與服務(wù)器綁定阻桅。 用戶第一次請(qǐng)求時(shí),負(fù)載均衡器將用戶的請(qǐng)求轉(zhuǎn)發(fā)到了 A 服務(wù)器上兼都,如果負(fù)載均衡器設(shè)置了粘性 session 的話鳍刷,那么用戶以后的每次請(qǐng)求都會(huì)轉(zhuǎn)發(fā)到 A 服務(wù)器上,相當(dāng)于把用戶和 A 服務(wù)器粘到了一塊俯抖,這就是粘性 session 機(jī)制输瓜。
優(yōu)點(diǎn): 簡(jiǎn)單,不需要對(duì) session 做任何處理。
缺點(diǎn): 缺乏容錯(cuò)性尤揣,如果當(dāng)前訪問的服務(wù)器發(fā)生故障搔啊,用戶被轉(zhuǎn)移到第二個(gè)服務(wù)器上時(shí),他的 session 信息都將失效北戏。
適用場(chǎng)景: 發(fā)生故障對(duì)客戶產(chǎn)生的影響較懈河蟆;服務(wù)器發(fā)生故障是低概率事件 嗜愈。
實(shí)現(xiàn)方式: 以 Nginx 為例旧蛾,在 upstream 模塊配置 ip_hash 屬性即可實(shí)現(xiàn)粘性 session。
3. session 共享(常用)
使用分布式緩存方案比如 Memcached 蠕嫁、Redis 來緩存 session锨天,但是要求 Memcached 或 Redis 必須是集群
-
把 session 放到 Redis 中存儲(chǔ),雖然架構(gòu)上變得復(fù)雜剃毒,并且需要多訪問一次 Redis 病袄,但是這種方案帶來的好處也是很大的:
實(shí)現(xiàn)了 session 共享;
可以水平擴(kuò)展(增加 Redis 服務(wù)器)赘阀;
服務(wù)器重啟 session 不丟失(不過也要注意 session 在 Redis 中的刷新/失效機(jī)制)益缠;
不僅可以跨服務(wù)器 session 共享,甚至可以跨平臺(tái)(例如網(wǎng)頁端和 APP 端)
4. session 持久化
- 將 session 存儲(chǔ)到數(shù)據(jù)庫中基公,保證 session 的持久化
優(yōu)點(diǎn): 服務(wù)器出現(xiàn)問題幅慌,session 不會(huì)丟失
缺點(diǎn): 如果網(wǎng)站的訪問量很大,把 session 存儲(chǔ)到數(shù)據(jù)庫中轰豆,會(huì)對(duì)數(shù)據(jù)庫造成很大壓力胰伍,還需要增加額外的開銷維護(hù)數(shù)據(jù)庫。
只要關(guān)閉瀏覽器 秒咨,session 真的就消失了喇辽?
不對(duì)掌挚。對(duì) session 來說雨席,除非程序通知服務(wù)器刪除一個(gè) session,否則服務(wù)器會(huì)一直保留吠式,程序一般都是在用戶做 log off 的時(shí)候發(fā)個(gè)指令去刪除 session陡厘。
然而瀏覽器從來不會(huì)主動(dòng)在關(guān)閉之前通知服務(wù)器它將要關(guān)閉,因此服務(wù)器根本不會(huì)有機(jī)會(huì)知道瀏覽器已經(jīng)關(guān)閉特占,之所以會(huì)有這種錯(cuò)覺糙置,是大部分 session 機(jī)制都使用會(huì)話 cookie 來保存 session id,而關(guān)閉瀏覽器后這個(gè) session id 就消失了是目,再次連接服務(wù)器時(shí)也就無法找到原來的 session谤饭。
如果服務(wù)器設(shè)置的 cookie 被保存在硬盤上,或者使用某種手段改寫瀏覽器發(fā)出的 HTTP 請(qǐng)求頭,把原來的 session id 發(fā)送給服務(wù)器揉抵,則再次打開瀏覽器仍然能夠打開原來的 session亡容。
恰恰是由于關(guān)閉瀏覽器不會(huì)導(dǎo)致 session 被刪除,迫使服務(wù)器為 session 設(shè)置了一個(gè)失效時(shí)間冤今,當(dāng)距離客戶端上一次使用 session 的時(shí)間超過這個(gè)失效時(shí)間時(shí)闺兢,服務(wù)器就認(rèn)為客戶端已經(jīng)停止了活動(dòng),才會(huì)把 session 刪除以節(jié)省存儲(chǔ)空間戏罢。
項(xiàng)目地址
https://github.com/yjdjiayou/jwt-demo
巨人的肩膀:
https://juejin.im/post/6844904034181070861
http://www.reibang.com/p/b2e23939ad46
https://blog.51cto.com/jamesfancy/2065665
https://blog.csdn.net/kaixuansui/article/details/94433920
https://www.cnblogs.com/yuki-nana/p/14001600.html
https://www.cnblogs.com/xiaozhang2014/p/7750200.html
https://mp.weixin.qq.com/s/j-6ngGKRJvB3gfY4VJaU4A