3種常用的 Web 會話管理方式
http 是無狀態(tài)的,即我們的一次請求結(jié)束后,下一次請求,服務(wù)端他并不知道是哪個用戶發(fā)來的。
我們在業(yè)務(wù)開發(fā)中通常是不需要關(guān)注是哪個客戶端發(fā)來的,更多的是關(guān)注是哪個用戶發(fā)來的。
基于這個特點,我們在處理業(yè)務(wù)邏輯時,就得想方設(shè)法地在下一次請求時讓服務(wù)端知道我是哪個用戶。
為什么是下一次呢?
因為首先我們得先登錄,才能告訴下一次請求是哪個,否則我們的很多業(yè)務(wù)就沒法開展,這就是所謂的會話管理。
那我們在項目里通常是怎么去管理我們的會話呢?
下面介紹常用的三種方式:
一、基于 server 端 session 的管理
在早期的 web 應(yīng)用中,我們通常都是使用這種方式來管理會話,它也叫服務(wù)端 session 管理,這里快速給大家介紹下它的處理邏輯:
-
用戶第一次訪問應(yīng)用時,服務(wù)端就會給他創(chuàng)建一個對象,用來存儲這次會話需要存儲的數(shù)據(jù),同時會分配一個唯一的 sessionid(字符串)給這次請求,這樣就把用戶和 sessionid 進行了關(guān)聯(lián)。 -
服務(wù)端處理完第一步之后,就會把 sessionid 以 cookie 的方式返回給客戶端,因為客戶端每次請求都會帶上 cookie,所以后續(xù)的請求服務(wù)端都能從 cookie 里面得到 sessionid,以此來知道請求是哪個用戶發(fā)來的。 -
session 是有失效時間的,比如 30 分鐘,當失效時間到了,服務(wù)端就會銷毀之前創(chuàng)建的 session,下次客戶端再拿 sessionid 來請求也就找不到了,從而達到過期的效果。 -
但是只要客戶端在失效期內(nèi)再次訪問,session 的失效時間就會被再次延長,以此來保證用戶的良好體驗。 -
單純的 session 是不具備會話管理的,這里面最關(guān)鍵的就是把登陸用戶和 sessionid 進行關(guān)聯(lián),如果不進行關(guān)聯(lián)就不能起到會話管理的作用,在項目里面的體現(xiàn)就是:用戶登錄后就進行session里面寫登錄用戶信息,從而實現(xiàn)關(guān)聯(lián),用戶退出就刪除 session 里面的用戶信息,從而破壞關(guān)聯(lián)。
我在網(wǎng)上找了一張流程圖,感興趣可以看下:

這種方式有一個比較大的優(yōu)點就是安全性好,因為客戶端和服務(wù)端保持會話的只有 sessionid,只要這個 id 足夠的隨機,那么別人就很難偽造冒充。
即使偽造了 id 也得是后臺存在這個 id 才行。
也不是說這種方式就絕對安全,除非他們通過 CSRF(跨站請求偽造)或者 http 劫持的方式,這又是另個話題了。
這種方式也存在缺點:
-
在線用戶量很多的時候,這些會話信息會比較占用空間。 -
當應(yīng)用采用集群部署時,會遇到多臺服務(wù)器之間 session 共享的問題,因為很可能處理請求的那臺服務(wù)器,不是創(chuàng)建 session 的那臺服務(wù)器。 -
假如你有多個應(yīng)用,想共享 session 時,還得處理跨域的問題。
前兩個問題現(xiàn)在已經(jīng)有比較成熟的解決方案了,那就是可以使用 Redis 這種中間服務(wù)來托管 session 的增刪改查。
對于第三個問題,解決起來就比較麻煩了,前后端都需要做處理,因為它最關(guān)鍵部分是 cookie,所以只要保證多個應(yīng)用之間的 cookie 能互通跨域就 OK。
這也是為啥現(xiàn)在的新系統(tǒng),都很少使用這種方式了,有比較多的局限性。
這種方式也不是不能用了,畢竟與后來出現(xiàn)的兩種管理方式比,這種管理方式他的安全性相對來說是最高的。
比較適合單體服務(wù)網(wǎng)站使用。
由于第一種方式會增加服務(wù)端的負擔和架構(gòu)的復雜性,所以后來的人想,那我把用戶的登錄憑證直接存到客戶端這樣就不會增加服務(wù)端的負擔了哇。
于是就有了第二種,cookie-based 的管理方式。
即:當用戶登錄成功后,直接把用戶的信息寫到 cookie 里面,這樣的話,上面的前兩個缺點都解決了。
他的流程大致為:
-
用戶發(fā)起登錄請求,登錄信息驗證通過后,把用戶信息處理成一個登錄憑證,這個憑證需要有創(chuàng)建時間和過期時間,便于服務(wù)端處理過期。 -
服務(wù)端把登錄憑證進行簽名,加密等操作(這步非常關(guān)鍵),保證其相對安全,然后寫入 cookie。 -
登錄后發(fā)起的請求,服務(wù)端就能根據(jù) cookie 里面的登錄憑證來知道是哪個用戶發(fā)起的請求了。
我又找了一張流程圖,請看:

這種方式最大的優(yōu)點就是實現(xiàn)了服務(wù)端的無狀態(tài)化,就是服務(wù)端不用再去管理會話了,服務(wù)端只需要處理?創(chuàng)建和驗證?cookie 里面的登陸憑證就好了。
但是也存在缺點:
-
cookie 是有大小限制的,存儲不了太多數(shù)據(jù),所以對寫入的數(shù)據(jù)量有非常大的要求。 -
cookie 也依舊存在跨域的問題。
最關(guān)鍵的是,不是所有客戶端都是瀏覽器,如果是 APP 這類不太好管理 cookie 的客戶端,這兩種方式都不太適用。
于是人們便又有了新方式!
三、token-based 的管理方式
這是目前用的比較多的方式,他本質(zhì)上和第二種沒太大區(qū)別,只不過第二種登憑證是放 cookie 里面,token-based 是丟給客戶端自己管理。
只需要下次請求時把 token(登錄憑證) 放請求頭里,或者和服務(wù)端約定好的地方,只要能獲取到的地方,就能達到驗證的目的,從而進行會話的管理。
看下流程圖:

這種方式服務(wù)端不再通過固定的 cookie 進行 token 傳遞,所以他的靈活性更大,我們只需要:
-
存儲好服務(wù)端返回的 token,得保證每次調(diào)用接口的時候能拿到 token。 -
每次調(diào)用接口時,把 token 加到和服務(wù)端約定的地方,比如請求頭里面即可。
這種方式解決了第一種方式遺留的的三個問題:資源占用,共享,跨域。
但是也讓想偽造 token 的朋友們,更加容易偽造了,所以這種方式最關(guān)鍵的部分就是簽名了,簽名被破譯就等于完全打開了你家的大門。
四、總結(jié)
市面上在處理 token 的問題上,用得比較多就是?JWT 標準。
JWT 本身并沒有任何技術(shù)實現(xiàn),他只是定義了 token 的生成的過程和方法,因此不同開發(fā)語言也就有了非常多開源的庫,實際開發(fā)中沒太大必要自己再去重復造一個 JWT 的輪子。
第三種方式使用起來固然便捷,但是相對第一種和第二種來說,token 被偽造的可能性高出不是一點半點。
最關(guān)鍵的地方就是簽名的算法,相關(guān)的加密 Key 千萬千萬不能泄露了。
這三種方式都有一個終結(jié)問題,就是 CSRF 攻擊(跨站請求偽造)。
其原理就是通過各種方法偷偷拿到別人的登錄憑證,然后偽造請求。
其解決方案針對不同的攻擊方式解決方案不一樣,完全取決于開發(fā)人員對這種攻擊方式的了解程度,所以親們,有空多看看 CSRF 這方面的知識吧!
這里提供一種非常具有代表性的攻擊方式:
假如你的 token 是放在 cookie 里面的,正常情況下別人是很難拿到的。
但是假如你的網(wǎng)站里,用戶有填寫外鏈的地方,比如評論,此時如果其他地方填寫了他的服務(wù)地址,又或者你掛了外網(wǎng)的圖片地址等。
這樣你網(wǎng)站在請求這些外網(wǎng)地址的時候,也就間接的泄露了你的 cookie,假如這個地址是別人的惡意地址,后果就可想而知了。
這就是一種典型的 CSRF 跨站攻擊方式。
安全無小事,防范需謹慎!
原文鏈接:https://mp.weixin.qq.com/s/Njss-eZz6LMgpcRsmDFbOw