主頁 > 企業開發 > cookie、session,、token,還在傻傻分不清?

cookie、session,、token,還在傻傻分不清?

2023-01-07 07:23:43 企業開發

摘要:session 和 token 本質上是沒有區別的,都是對用戶身份的認證機制,只是他們實作的校驗機制不一樣而已,

本文分享自華為云社區《Session/Cookie/Token 還傻傻分不清?》,作者: 龍哥手記,

相信專案中用JWT Token的應該不在少數,但是發現網上很多文章對 token 的介紹有誤,所以對 cookie,session, token 作了一下對比(文中token指jwt token)相信大家看完肯定有識訓!

Cookie

1991 年 HTTP 0.9 誕生了,當時只是為了滿足大家瀏覽 web 檔案的要求 ,所以只有 GET 請求,瀏覽完了就走了,兩個連接之間是沒有任何聯系的,這也是 HTTP 為無狀態的原因,因為它誕生之初就沒有這個需求,

但隨著互動式 Web 的興起(所謂互動式就是你不光可以瀏覽,還可以登錄,發評論,購物等用戶操作的行為),單純地瀏覽 web 已經無法滿足人們的要求,比如隨著網上購物的興起,需要記錄用戶的購物車記錄,就需要有一個機制記錄每個連接的關系,這樣我們就知道加入購物車的商品到除錯于誰了,于是 Cookie 就誕生了,

Cookie,有時也用其復數形式 Cookies,型別為“小型文本檔案”,是某些網站為了辨別用戶身份,進行 Session 跟蹤而儲存在用戶本地終端上的資料(通常經過加密),由用戶客戶端計算機暫時或永久保存的資訊 ,

作業機制如下

以加入購物車為例,每次瀏覽器請求后 server 都會將本次商品 id 存盤在 Cookie 中回傳給客戶端,客戶端會將 Cookie 保存在本地,下一次再將上次保存在本地的 Cookie 傳給 server 就行了,這樣每個 Cookie 都保存著用戶的商品 id,購買記錄也就不會丟失了

仔細觀察上圖相信你不難發現隨著購物車內的商品越來越多,每次請求的 cookie 也越來越大,這對每個請求來說是一個很大的負擔,我只是想將一個商品加入購買車,為何要將歷史的商品記錄也一起回傳給 server ?購物車資訊其實已經記錄在 server 了,瀏覽器這樣的操作豈不是多此一舉?怎么改進呢

Session

仔細考慮下,由于用戶的購物車資訊都會保存在 Server 中,所以在 Cookie 里只要保存能識別用戶身份的資訊,知道是誰發起了加入購物車操作即可,這樣每次請求后只要在 Cookie 里帶上用戶的身份資訊,請求體里也只要帶上本次加入購物車的商品 id,大大減少了 cookie 的體積大小,我們把這種能識別哪個請求由哪個用戶發起的機制稱為 Session(會話機制),生成的能識別用戶身份資訊的字串稱為 sessionId,它的作業機制如下

  1. 首先用戶登錄,server 會為用戶生成一個 session,為其分配唯一的 sessionId,這個 sessionId 是與某個用戶系結的,也就是說根據此 sessionid(假設為 abc) 可以查詢到它到底是哪個用戶,然后將此 sessionid 通過 cookie 傳給瀏覽器
  2. 之后瀏覽器的每次添加購物車請求中只要在 cookie 里帶上 sessionId=abc 這一個鍵值對即可,server 根據 sessionId 找到它對應的用戶后,把傳過來的商品 id 保存到 server 中對應用戶的購物車即可

可以看到通過這種方式再也不需要在 cookie 里傳所有的購物車的商品 id 了,大大減輕了請求的負擔!

另外通過上文不難觀察出 cookie 是存盤在 client 的,而 session 保存在 server,sessionId 需要借助 cookie 的傳遞才有意義,

session 的痛點

看起來通過 cookie + session 的方式是解決了問題, 但是我們忽略了一個問題,上述情況能正常作業是因為我們假設 server 是單機作業的,但實際在生產上,為了保障高可用,一般服務器至少需要兩臺機器,通過負載均衡的方式來決定到底請求該打到哪臺機器上,

如圖示:客戶端請求后,由負載均衡器(如 Nginx)來決定到底打到哪臺機器

假設登錄請求打到了 A 機器,A 機器生成了 session 并在 cookie 里添加 sessionId 回傳給了瀏覽器,那么問題來了:下次添加購物車時如果請求打到了 B 或者 C,由于 session 是在 A 機器生成的,此時的 B,C 是找不到 session 的,那么就會發生無法添加購物車的錯誤,就得重新登錄了,此時請問該怎么辦,主要有以下三種方式

1、session 復制

A 生成 session 后復制到 B, C,這樣每臺機器都有一份 session,無論添加購物車的請求打到哪臺機器,由于 session 都能找到,故不會有問題

這種方式雖然可行,但缺點也很明顯:

  1. 同一樣的一份 session 保存了多份,資料冗余
  2. 如果節點少還好,但如果節點多的話,特別是像阿里,微信這種由于 DAU 上億,可能需要部署成千上萬臺機器,這樣節點增多復制造成的性能消耗也會很大,

2、session 粘連

這種方式是讓每個客戶端請求只打到固定的一臺機器上,比如瀏覽器登錄請求打到 A 機器后,后續所有的添加購物車請求也都打到 A 機器上,Nginx 的 sticky 模塊可以支持這種方式,支持按 ip 或 cookie 粘連等等,如按 ip 粘連方式如下

upstream tomcats {
 ip_hash;
  server 10.1.1.107:88;
  server 10.1.1.132:80;
}

這樣的話每個 client 請求到達 Nginx 后,只要它的 ip 不變,根據 ip hash 算出來的值會打到固定的機器上,也就不存在 session 找不到的問題了,當然不難看出這種方式缺點也是很明顯,對應的機器掛了怎么辦?

3、session 共享

這種方式也是目前各大公司普遍采用的方案,將 session 保存在 redis,memcached 等中間件中,請求到來時,各個機器去這些中間件取一下 session 即可,

缺點其實也不難發現,就是每個請求都要去 redis 取一下 session,多了一次內部連接,消耗了一點性能,另外為了保證 redis 的高可用,必須做集群,當然了對于大公司來說, redis 集群基本都會部署,所以這方案可以說是大公司的首選了,

Token:no session!

通過上文分析我們知道通過在服務端共享 session 的方式可以完成用戶的身份定位,但是不難發現也有一個小小的瑕疵:搞個校驗機制我還得搭個 redis 集群?大廠確實 redis 用得比較普遍,但對于小廠來說可能它的業務量還未達到用 redis 的程度,所以有沒有其他不用 server 存盤 session 的用戶身份校驗機制呢,這就是我們今天要介紹的主角:token,

首先請求方輸入自己的用戶名,密碼,然后 server 據此生成 token,客戶端拿到 token 后會保存到本地,之后向 server 請求時在請求頭帶上此 token 即可,

相信大家看了上圖會發現存在兩個問題

1、 token 只存盤在瀏覽器中,服務端卻沒有存盤,這樣的話我隨便搞個 token 傳給 server 也行?

答:server 會有一套校驗機制,校驗這個 token 是否合法,

2、怎么不像 session 那樣根據 sessionId 找到 userid 呢,這樣的話怎么知道是哪個用戶?

答:token 本身攜帶 uid 資訊

第一個問題,如何校驗 token 呢?我們可以借鑒 HTTPS 的簽名機制來校驗,先來看 jwt token 的組成部分

可以看到 token 主要由三部分組成

  1. header:指定了簽名演算法
  2. payload:可以指定用戶 id,過期時間等非敏感資料
  3. Signature: 簽名,server 根據 header 知道它該用哪種簽名演算法,再用密鑰根據此簽名演算法對 head + payload 生成簽名,這樣一個 token 就生成了,

當 server 收到瀏覽器傳過來的 token 時,它會首先取出 token 中的 header + payload,根據密鑰生成簽名,然后再與 token 中的簽名比對,如果成功則說明簽名是合法的,即 token 是合法的,而且你會發現 payload 中存有我們的 userId,所以拿到 token 后直接在 payload 中就可獲取 userid,避免了像 session 那樣要從 redis 去取的開銷

畫外音:header, payload 實際上是以 base64 的形式存在的,文中為了描述方便,省去了這一步,

你會發現這種方式確實很妙,只要 server 保證密鑰不泄露,那么生成的 token 就是安全的,因為如果偽造 token 的話在簽名驗證環節是無法通過的,就此即可判定 token 非法,

可以看到通過這種方式有效地避免了 token 必須保存在 server 的弊端,實作了分布式存盤,不過需要注意的是,token 一旦由 server 生成,它就是有效的,直到過期,無法讓 token 失效,除非在 server 為 token 設立一個黑名單,在校驗 token 前先過一遍此黑名單,如果在黑名單里則此 token 失效,但一旦這樣做的話,那就意味著黑名單就必須保存在 server,這又回到了 session 的模式,那直接用 session 不香嗎,所以一般的做法是當客戶端登出要讓 token 失效時,直接在本地移除 token 即可,下次登錄重新生成 token 就好,

另外需要注意的是 token 一般是放在 header 的 Authorization 自定義頭里,不是放在 Cookie 里的,這主要是為了解決跨域不能共享 Cookie 的問題 (下文詳述)

Cookie 與 Token 的簡單總結

Cookie 有哪些局限性?

1、 Cookie 跨站是不能共享的,這樣的話如果你要實作多應用(多系統)的單點登錄(SSO),使用 Cookie 來做需要的話就很困難了(要用比較復雜的 trick 來實作,有興趣的話可以看文末參考鏈接)

畫外音: 所謂單點登錄,是指在多個應用系統中,用戶只需要登錄一次就可以訪問所有相互信任的應用系統,

但如果用 token 來實作 SSO 會非常簡單,如下

只要在 header 中的 authorize 欄位(或其他自定義)加上 token 即可完成所有跨域站點的認證,

2、 在移動端原生請求是沒有 cookie 之說的,而 sessionid 依賴于 cookie,sessionid 就不能用 cookie 來傳了,如果用 token 的話,由于它是隨著 header 的 authoriize 傳過來的,也就不存在此問題,換句話說token 天生支持移動平臺,可擴展性好

綜上所述,token 具有存盤實作簡單,擴展性好這些特點,

token 有哪些缺點

那有人就問了,既然 token 這么好,那為什么各個大公司幾乎都采用共享 session 的方式呢,可能很多人是第一次聽到 token,token 不香嗎? token 有以下兩點劣勢:

1、 token 太長了

token 是 header, payload 編碼后的樣式,所以一般要比 sessionId 長很多,很有可能超出 cookie 的大小限制(cookie 一般有大小限制的,如 4kb),如果你在 token 中存盤的資訊越長,那么 token 本身也會越長,這樣的話由于你每次請求都會帶上 token,對請求來是個不小的負擔

2、 不太安全

網上很多文章說 token 更安全,其實不然,細心的你可能發現了,我們說 token 是存在瀏覽器的,再細問,存在瀏覽器的哪里?既然它太長放在 cookie 里可能導致 cookie 超限,那就只好放在 local storage 里,這樣會造成安全隱患,因為 local storage 這類的本地存盤是可以被 JS 直接讀取的,另外由上文也提到,token 一旦生成無法讓其失效,必須等到其過期才行,這樣的話如果服務端檢測到了一個安全威脅,也無法使相關的 token 失效,

所以 token 更適合一次性的命令認證,設定一個比較短的有效期

誤解: Cookie 相比 token 更不安全,比如 CSRF 攻擊

首先我們需要解釋下 CSRF 攻擊是怎么回事

攻擊者通過一些技術手段欺騙用戶的瀏覽器去訪問一個自己曾經認證過的網站并運行一些操作(如發郵件,發訊息,甚至財產操作如轉賬和購買商品),由于瀏覽器曾經認證過(cookie 里帶來 sessionId 等身份認證的資訊),所以被訪問的網站會認為是真正的用戶操作而去運行,

比如用戶登錄了某銀行網站(假設為 http://www.examplebank.com/,并且轉賬地址為 http://www.examplebank.com/withdraw?amount=1000&transferTo=PayeeName),登錄后 cookie 里會包含登錄用戶的 sessionid,攻擊者可以在另一個網站上放置如下代碼

<img src="http://www.examplebank.com/withdraw?account=Alice&amount=1000&for=Badman">

那么如果正常的用戶誤點了上面這張圖片,由于相同域名的請求會自動帶上 cookie,而 cookie 里帶有正常登錄用戶的 sessionid,類似上面這樣的轉賬操作在 server 就會成功,會造成極大的安全風險

CSRF 攻擊的根本原因在于對于同樣域名的每個請求來說,它的 cookie 都會被自動帶上,這個是瀏覽器的機制決定的,所以很多人據此認定 cookie 不安全,

使用 token 確實避免了CSRF 的問題,但正如上文所述,由于 token 保存在 local storage,它會被 JS 讀取,從存盤角度來看也不安全(實際上防護 CSRF 攻擊的正確方式是用 CSRF token)

所以不管是 cookie 還是 token,從存盤角度來看其實都不安全,都有暴露的風險,我們所說的安全更多的是強調傳輸中的安全,可以用 HTTPS 協議來傳輸, 這樣的話請求頭都能被加密,也就保證了傳輸中的安全,

其實我們把 cookie 和 token 比較本身就不合理,一個是存盤方式,一個是驗證方式,正確的比較應該是 session vs token,

總結

session 和 token 本質上是沒有區別的,都是對用戶身份的認證機制,只是他們實作的校驗機制不一樣而已(一個保存在 server,通過在 redis 等中間件獲取來校驗,一個保存在 client,通過簽名校驗的方式來校驗),多數場景上使用 session 會更合理,但如果在單點登錄,一次性命令認證上使用 token 會更合適,最好在不同的業務場景中合理選型,才能達到事半功倍的效果,

 

點擊關注,第一時間了解華為云新鮮技術~

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/541394.html

標籤:Html/Css

上一篇:如何安全的保存用戶密碼

下一篇:可以一學的代碼優化小技巧:減少if-else冗余

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......

    uj5u.com 2023-04-20 08:17:54 more
  • 記錄-new Date() 我忍你很久了!

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......

    uj5u.com 2023-04-20 08:17:47 more
  • 使用Vue.js實作文字跑馬燈效果

    實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......

    uj5u.com 2023-04-20 08:12:31 more
  • JavaScript 運算子

    JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......

    uj5u.com 2023-04-20 08:02:25 more
  • CSS—相對單位rem

    一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......

    uj5u.com 2023-04-20 08:02:21 more
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 08:01:50 more
  • 如何在 vue3 中使用 jsx/tsx?

    我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......

    uj5u.com 2023-04-20 08:01:37 more
  • 【Vue2.x原始碼系列06】計算屬性computed原理

    本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......

    uj5u.com 2023-04-20 08:01:31 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:01:10 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:00:32 more