<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          cookie 和 session 到底是什么

          共 4832字,需瀏覽 10分鐘

           ·

          2020-02-05 23:24

          來源:labuladong

          作者:labuladong

          cookie 大家應(yīng)該都熟悉,比如說登錄某些網(wǎng)站一段時間后,就要求你重新登錄;再比如有的同學很喜歡玩爬蟲技術(shù),有時候網(wǎng)站就是可以攔截住你的爬蟲,這些都和 cookie 有關(guān)。

          如果你明白了服務(wù)器后端對于 cookie 和 session 的處理邏輯,就可以解釋這些現(xiàn)象,甚至鉆一些空子無限白嫖,待我慢慢道來。

          一、session 和 cookie 簡介

          cookie 的出現(xiàn)是因為 HTTP 是無狀態(tài)的一種協(xié)議,換句話說,服務(wù)器記不住你,可能你每刷新一次網(wǎng)頁,就要重新輸入一次賬號密碼進行登錄。這顯然是讓人無法接受的,cookie 的作用就好比服務(wù)器給你貼個標簽,然后你每次向服務(wù)器再發(fā)請求時,服務(wù)器就能夠 cookie 認出你。

          抽象地概括一下:一個 cookie 可以認為是一個「變量」,形如name=value,存儲在瀏覽器;一個 session 可以理解為一種數(shù)據(jù)結(jié)構(gòu),多數(shù)情況是「映射」(鍵值對),存儲在服務(wù)器上。

          注意,我說的是「一個」cookie 可以認為是一個變量,但是服務(wù)器可以一次設(shè)置多個 cookie,所以有時候說 cookie 是「一組」鍵值對兒,這也可以說得通。

          cookie 可以在服務(wù)器端通過 HTTP 的 SetCookie 字段設(shè)置 cookie,比如我用 Go 語言寫的一個簡單服務(wù):

          func?cookie(w?http.ResponseWriter,?r?*http.Request)?{
          ????//?設(shè)置了兩個?cookie?
          ????http.SetCookie(w,?&http.Cookie{
          ????????Name:???????"name1",
          ????????Value:??????"value1",
          ????})

          ????http.SetCookie(w,?&http.Cookie{
          ????????Name:??"name2",
          ????????Value:?"value2",
          ????})
          ????//?將字符串寫入網(wǎng)頁
          ????fmt.Fprintln(w,?"頁面內(nèi)容")
          }

          當瀏覽器訪問對應(yīng)網(wǎng)址時,通過瀏覽器的開發(fā)者工具查看此次 HTTP 通信的細節(jié),可以看見服務(wù)器的回應(yīng)發(fā)出了兩次SetCookie命令:

          9602324cdfdfae5679602c897ca6caad.webp

          在這之后,瀏覽器的請求中的Cookie字段就帶上了這兩個 cookie:

          05ded9560a72bb3b31394f4b2538709a.webp

          cookie 的作用其實就是這么簡單,無非就是服務(wù)器給每個客戶端(瀏覽器)打的標簽,方便服務(wù)器辨認而已。當然,HTTP 還有很多參數(shù)可以設(shè)置 cookie,比如過期時間,或者讓某個 cookie 只有某個特定路徑才能使用等等。

          但問題是,我們也知道現(xiàn)在的很多網(wǎng)站功能很復(fù)雜,而且涉及很多的數(shù)據(jù)交互,比如說電商網(wǎng)站的購物車功能,信息量大,而且結(jié)構(gòu)也比較復(fù)雜,無法通過簡單的 cookie 機制傳遞這么多信息,而且要知道 cookie 字段是存儲在 HTTP header 中的,就算能夠承載這些信息,也會消耗很多的帶寬,比較消耗網(wǎng)絡(luò)資源。

          session 就可以配合 cookie 解決這一問題,比如說一個 cookie 存儲這樣一個變量sessionID=xxxx,僅僅把這一個 cookie 傳給服務(wù)器,然后服務(wù)器通過這個 ID 找到對應(yīng)的 session,這個 session 是一個數(shù)據(jù)結(jié)構(gòu),里面存儲著該用戶的購物車等詳細信息,服務(wù)器可以通過這些信息返回該用戶的定制化網(wǎng)頁,有效解決了追蹤用戶的問題。

          session 是一個數(shù)據(jù)結(jié)構(gòu),由網(wǎng)站的開發(fā)者設(shè)計,所以可以承載各種數(shù)據(jù),只要客戶端的 cookie 傳來一個唯一的 session ID,服務(wù)器就可以找到對應(yīng)的 session,認出這個客戶。

          當然,由于 session 存儲在服務(wù)器中,肯定會消耗服務(wù)器的資源,所以 session 一般都會有一個過期時間,服務(wù)器一般會定期檢查并刪除過期的 session,如果后來該用戶再次訪問服務(wù)器,可能就會面臨重新登錄等等措施,然后服務(wù)器新建一個 session,將 session ID 通過 cookie 的形式傳送給客戶端。

          那么,我們知道 cookie 和 session 的原理,有什么切實的好處呢?除了應(yīng)對面試,我給你說一個雞賊的用處,就是可以白嫖某些服務(wù)。

          有些網(wǎng)站,你第一次使用它的服務(wù),它直接免費讓你試用,但是用一次之后,就讓你登錄然后付費繼續(xù)使用該服務(wù)。而且你發(fā)現(xiàn)網(wǎng)站似乎通過某些手段記住了你的電腦,除非你換個電腦或者換個瀏覽器才能再白嫖一次。

          那么問題來了,你試用的時候沒有登錄,網(wǎng)站服務(wù)器是怎么記住你的呢?這就很顯然了,服務(wù)器一定是給你的瀏覽器打了 cookie,后臺建立了對應(yīng)的 session 記錄你的狀態(tài)。你的瀏覽器在每次訪問該網(wǎng)站的時候都會聽話地帶著 cookie,服務(wù)器一查 session 就知道這個瀏覽器已經(jīng)免費使用過了,得讓它登錄付費,不能讓它繼續(xù)白嫖了。

          那如果我不讓瀏覽器發(fā)送 cookie,每次都偽裝成一個第一次來試用的小萌新,不就可以不斷白嫖了么?瀏覽器會把網(wǎng)站的 cookie 以文件的形式存在某些地方(不同的瀏覽器配置不同),你把他們找到然后刪除就行了。但是對于 Firefox 和 Chrome 瀏覽器,有很多插件可以直接編輯 cookie,比如我的 Chrome 瀏覽器就用的一款叫做 EditThisCookie 的插件,這是他們官網(wǎng):

          bd569267e6e9c00b00eb804108d4daef.webphttp://www.editthiscookie.com/

          這類插件可以讀取瀏覽器在當前網(wǎng)頁的 cookie,點開插件可以任意編輯和刪除 cookie。

          當然,偶爾白嫖一兩次還行,不鼓勵高頻率白嫖,想常用還是掏錢吧,否則網(wǎng)站賺不到錢,就只能取消免費試用這個機制了。

          以上就是關(guān)于 cookie 和 session 的簡單介紹,cookie 是 HTTP 協(xié)議的一部分,不算復(fù)雜,而 session 是可以定制的,所以下面詳細看一下實現(xiàn) session 管理的代碼架構(gòu)吧。

          二、session 的實現(xiàn)

          session 的原理不難,但是具體實現(xiàn)它可是很有技巧的,一般需要三個組件配合完成,它們分別是ManagerProviderSession三個類(接口)。

          下圖就是一個 HTTP 請求的完整流程:

          d53e120dd6f7342ae7348166b4b83e9e.webp

          1、瀏覽器通過 HTTP 協(xié)議向服務(wù)器請求路徑/content的網(wǎng)頁資源,對應(yīng)路徑上有一個 Handler 函數(shù)接收請求,解析 HTTP header 中的 cookie,得到其中存儲的 sessionID,然后把這個 ID 發(fā)給Manager

          2、Manager充當一個 session 管理器的角色,主要存儲一些配置信息,比如 session 的存活時間,cookie 的名字等等。而所有的 session 存在一個Provider中。所以Manager會把sid(sessionID)傳遞給Provider,讓它去找這個 ID 對應(yīng)的具體是哪個 session。

          3、Provider就是一個容器,最常見的應(yīng)該就是一個散列表,將每個sid和對應(yīng)的 session 一一映射起來。收到Manager傳遞的sid之后,它就找到sid對應(yīng)的 session 結(jié)構(gòu),也就是Session結(jié)構(gòu),然后返回它。

          4、Session中存儲著用戶的具體信息,一般是一個散列表,由 Handler 函數(shù)中的邏輯拿出這些信息,生成該用戶的 HTML 網(wǎng)頁,返回給客戶端。

          那么你也許會問,為什么搞這么麻煩,直接在 Handler 函數(shù)中搞一個哈希表,然后存儲sidSession結(jié)構(gòu)的映射不就完事兒了?

          這就是設(shè)計層面的技巧了,下面就來說說,為什么分成Manager、ProviderSession。

          先從最底層的Session說。既然 session 就是鍵值對,為啥不直接用哈希表,而是要抽象出這么一個數(shù)據(jù)結(jié)構(gòu)呢?

          第一,因為Session結(jié)構(gòu)可能不止存儲了一個哈希表,還可以存儲一些輔助數(shù)據(jù),比如sid,訪問次數(shù),過期時間或者最后一次的訪問時間,這樣便于實現(xiàn)想 LRU、LFU 這樣的算法。

          第二,因為 session 可以有不同的存儲方式。如果用編程語言內(nèi)置的哈希表,那么 session 數(shù)據(jù)就是存儲在內(nèi)存中,如果數(shù)據(jù)量大,很容易造成程序崩潰,而且一旦程序結(jié)束,所有 session 數(shù)據(jù)都會丟失。所以可以有很多種 session 的存儲方式,比如存入緩存數(shù)據(jù)庫 Redis,或者存入 MySQL 等等。

          因此,Session結(jié)構(gòu)提供一層抽象,屏蔽不同存儲方式的差異,只要提供一組通用接口操縱鍵值對:

          type?Session?interface?{
          ????//?設(shè)置鍵值對
          ????Set(key,?val?interface{})
          ????//?獲取?key?對應(yīng)的值
          ????Get(key?interface{})?interface{}
          ????//?刪除鍵?key
          ????Delete(key?interface{})
          }

          再說Provider為啥要抽象出來。我們上面那個圖的Provider就是一個散列表,保存sidSession的映射,但是實際中肯定會更加復(fù)雜。

          我們不是要時不時刪除一些 session 嗎,除了設(shè)置存活時間之外,還可以采用一些其他策略,比如 LRU 緩存淘汰算法,這樣就需要Provider內(nèi)部使用哈希鏈表這種數(shù)據(jù)結(jié)構(gòu)來存儲 session。

          PS:關(guān)于 LRU 算法的奧妙,參見前文 如何實現(xiàn) LRU 緩存機制。

          因此,Provider作為一個容器,就是要屏蔽算法細節(jié),以合理的數(shù)據(jù)結(jié)構(gòu)和算法組織sidSession的映射關(guān)系,只需要實現(xiàn)下面這幾個方法實現(xiàn)對 session 的增刪查改:

          type?Provider?interface?{
          ????//?新增并返回一個?session
          ????SessionCreate(sid?string)?(Session,?error)
          ????//?刪除一個?session
          ????SessionDestroy(sid?string)
          ????//?查找一個?session
          ????SessionRead(sid?string)?(Session,?error)
          ????//?修改一個session
          ????SessionUpdate(sid?string)
          ????//?通過類似?LRU?的算法回收過期的?session
          ????SessionGC(maxLifeTime?int64)
          }

          最后說Manager,大部分具體工作都委托給SessionProvider承擔了,Manager主要就是一個配置參數(shù)集合,比如 session 的存活時間,清理過期 session 的策略,以及 session 的可用存儲方式。Manager屏蔽了操作的具體細節(jié),我們可以通過Manager靈活地配置 session 機制。

          綜上,session 機制分成幾部分的最主要原因就是解耦,實現(xiàn)定制化。我在 Github 上看過幾個 Go 語言實現(xiàn)的 session 服務(wù),源碼都很簡單,有興趣的朋友可以學習學習:

          https://github.com/alexedwards/scs

          https://github.com/astaxie/build-web-application-with-golang

          最后,如果本文對你有幫助,在看分享,就是對我最大的支持。


          推薦閱讀

          全部文章分類與整理(算法+數(shù)據(jù)結(jié)構(gòu)+計算機基礎(chǔ)),持續(xù)更新

          我的 2019 | 另送我的讀者 3000 元現(xiàn)金紅包

          普普通通,我的三年大學

          寫公眾號15個月以來,這一路上的學習與收獲

          歷經(jīng)兩個月,我的秋招之路結(jié)束了!

          2020 第一篇原創(chuàng) | 我是如何讓自己變的更加優(yōu)秀的?

          瀏覽 50
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  手机在线免费AV | 久久三级电影网站 | 男人捅女人到爽免费网站 | 久久地址 | 黄色视频免费播放久久 |