1. 程式人生 > >Cookie、Session、Token那點事兒(原創)

Cookie、Session、Token那點事兒(原創)

本文已獨家授權 鴻洋( hongyangAndroid ) 公眾號釋出!

前言:新公司專案中使用到了Cookie,在各大Android技術討論群向前輩們取經討論這cookie、session、token這仨哥們的時候,很多開發者說法不一各抒已見,所以是時候回顧下http基礎以及總結開發經驗了。本文重在科普分析Cookie、Session、Token的基本概念和應用場景;Okhttp框架下對Cookie的管理使用。文章如果寫的不好請各位開發者老司機私聊或者在評論區指點提出issue。

什麼是Cookie?

Cookie 技術產生源於 HTTP 協議在網際網路上的急速發展。隨著網際網路時代的策馬奔騰,頻寬等限制不存在了,人們需要更復雜的網際網路互動活動,就必須同伺服器保持活動狀態(簡稱:保活)

。於是,在瀏覽器發展初期,為了適應使用者的需求技術上推出了各種保持 Web 瀏覽狀態的手段,其中就包括了 Cookie 技術。Cookie 在計算機中是個儲存在瀏覽器目錄中的文字檔案,當瀏覽器執行時,儲存在 RAM 中發揮作用 (此種 Cookies 稱作 Session Cookies),一旦使用者從該網站或伺服器退出,Cookie 可儲存在使用者本地的硬碟上 (此種 Cookies 稱作 Persistent Cookies)。

Cookie 起源:1993 年,網景公司僱員 Lou Montulli 為了讓使用者在訪問某網站時,進一步提高訪問速度,同時也為了進一步實現個人化網路,發明了今天廣泛使用的 Cookie。(所以,適當的偷懶也會促進人類計算機發展史的一小步~)

Cookie時效性:目前有些 Cookie 是臨時的,有些則是持續的。臨時的 Cookie 只在瀏覽器上儲存一段規定的時間,一旦超過規定的時間,該 Cookie 就會被系統清除。

Cookie使用限制:Cookie 必須在 HTML 檔案的內容輸出之前設定;不同的瀏覽器 (Netscape Navigator、Internet Explorer) 對 Cookie 的處理不一致,使用時一定要考慮;客戶端使用者如果設定禁止 Cookie,則 Cookie 不能建立。 並且在客戶端,一個瀏覽器能建立的 Cookie 數量最多為 300 個,並且每個不能超過 4KB,每個 Web 站點能設定的 Cookie 總數不能超過 20 個。

執行流程:

A:首先,客戶端會發送一個http請求到伺服器端。

B: 伺服器端接受客戶端請求後,傳送一個http響應到客戶端,這個響應頭,其中就包含Set-Cookie頭部。

C:在客戶端發起的第二次請求(注意:如果伺服器需要我們帶上Cookie,我們就需要在B步驟上面拿到這個Cookie然後作為請求頭一起發起第二次請求),提供給了伺服器端可以用來唯一標識客戶端身份的資訊。這時,伺服器端也就可以判斷客戶端是否啟用了cookies。儘管,使用者可能在和應用程式互動的過程中突然禁用cookies的使用,但是,這個情況基本是不太可能發生的,所以可以不加以考慮,這在實踐中也被證明是對的。

為了方便理解,可以先看下這張流程執行圖加深概念

客戶端與服務端的Cookie流程圖

那麼,在瀏覽器上面的請求頭和Cookie在那?下圖給大家截取了其中一種。

請求頭上面的Cookie

那麼,上面都是談瀏覽器上的Cookie,那麼在Android開發中,我們該如何去管理和使用Cookie?Okhttp作為經典到爆的網路框架,它的API(本文是基於Okhttp3.0版本以上,3.0以下的版本API有所不同)是通過OkhttpClient中的CookieJar或者攔截器去管理Cookie的。理論上,我們只需在構建單例OkhttpClient的時候,設定cookiejar或者攔截器,然後具體的操作(具體的操作也就是儲存Cookie,取Cookie),Okhttp框架就會幫我們自動管理Cookie。如下圖:

OKhttp的管理策略

這是其中一種通過集合的增查特性,就可以簡單有效的幫我們管理Cookie。但我們還是要通過原始碼去一探究竟。首先,CookieJar是一個介面。

CookieJar

英文註釋翻譯過來就是(對應段落翻譯):

CookieJar這個介面為HTTP cookies提供了強大的支援和相關策略。

這種策略的實現作用會負責選擇接受和拒絕那些cookie。一個合理的策略是拒絕所有的cookie,儘管這樣會干擾需要cookie的基於會話的自身身份驗證方案。

作為Cookie的永續性,該介面的實現也必須要提供Cookie的儲存。一種簡單的實現可以將cookie儲存在記憶體中;複雜的系統可以使用檔案系統用於儲存已接受的cookie的資料庫。這裡的連結指定cookie儲存模型更新和過期的cookie的策略。

所以,Okhttp的原始碼告知我們可以將cookie儲存在記憶體中;複雜的系統可以使用檔案系統用於儲存已接受的cookie的資料庫。因此,我們就可以通過Map去簡單的管理和使用。

繼續分析CookieJar接口裡面的方法,依舊上原始碼

CookieJar裡面的方法

裡面有方法一個是saveFromResponse(HttpUrl url, List cookies)、loadForRequest(HttpUrl url)

saveFromResponse方法翻譯:根據這個jar的方法,可以將cookie從一個HTTP響應儲存到這裡。請注意,如果響應,此方法可能被稱為第二次HTTP響應,包括一個追蹤。對於這個隱蔽的HTTP特性,這裡的cookie只包含其追蹤的cookie。簡單點理解就是如果我們使用了這個方法,就會進行追蹤(說白了就是客戶端請求成功以後,在響應頭裡面去存cookie)

loadForRequest方法翻譯:將cookie從這個方法載入到一個HTTP請求到指定的url。這個方法從網路上返回的結果可能是一個空集合。簡單的實現將返回尚未過期的已接受的cookie去進行匹配。(說白了就是載入url的時候在請求頭帶上cookie)。

通過CookieJar列印Cookie資訊

這樣,我們通過以上程式碼就可以完成了Cookie的非持久化。什麼,非持久化,這又是神馬?

繼續給大家科普,在上面說道,Cookie是具有時效性的,所以,Cookie的管理又分為持久化Cookie和非持久化Cookie。非持久化Cookie儲存在記憶體中,也就意味著,其生命週期基本和app保持一致,app關閉後,Cookie丟失。而持久化Cookie則是儲存在本地磁碟中,app關閉後不丟失。那麼,如果我們要使用Cookie的持久化策略,思想可以參考上面的非持久化策略,只需要將儲存方式改一下即可:

A:通過響應攔截器從response取出cookie並儲存到本地,通過請求攔截器從本地取出cookie並新增到請求中

B:自定義CookieJar,在saveFromResponse()中儲存cookie到本地,在loadForRequest()從本地取出cookie。

那麼在這裡主要介紹如何通過Okhttp逼格值較高的攔截器去進行持久化cookie操作。(攔截器原始碼見文章末尾處

儲存cookie攔截器-1

 

儲存cookie攔截器-2

這個SaveCookiesInterceptor攔截器的實現,是首先從response獲取set-cookie欄位的值,然後通過SharedPreferences儲存在本地。

將Cookie新增到請求頭

AddCookiesInterceptor請求攔截器,這個攔截的作用就是判斷如果該請求存在cookie,則為其新增到Header的Cookie中。

寫好這兩個攔截器之後,我們只需要將例項物件放進OkhttpClient裡面即可快速的完成Cookie持久化操作。(PS:這兩個攔截器在同步Cookie的時候也是超級好用)。

Okhttp使用cookie攔截器

拓展:如何通過客戶端的cookie與H5上面的cookie進行同步,針對這個問題,給大家推薦筆者的另外一篇文章

客戶端與H5同步Cookie

Session :

Session是對於服務端來說的,客戶端是沒有Session一說的。Session是伺服器在和客戶端建立連線時新增客戶端連線標誌,最終會在伺服器軟體(Apache、Tomcat、JBoss)轉化為一個臨時Cookie傳送給給客戶端,當客戶端第一請求時伺服器會檢查是否攜帶了這個Session(臨時Cookie),如果沒有則會新增Session,如果有就拿出這個Session來做相關操作。

在這裡引用別人家的一個小故事來加深印象:

        在說session是啥之前,我們先來說說為什麼會出現session會話,它出現的機理是什麼?

    我們知道,我們用瀏覽器開啟一個網頁,用到的是HTTP協議,瞭解計算機的應該都知道這個協議,它是無狀態的,什麼是無狀態呢?就是說這一次請求和上一次請求是沒有任何關係的,互不認識的,沒有關聯的。但是這種無狀態的的好處是快速。所以就會帶來一個問題就是,我希望幾個請求的頁面要有關聯,比如:我在www.a.com/login.php裡面登陸了,我在www.a.com/index.php 也希望是登陸狀態,但是,這是2個不同的頁面,也就是2個不同的HTTP請求,這2個HTTP請求是無狀態的,也就是無關聯的,所以無法單純的在index.php中讀取到它在login.php中已經登陸了!

        那咋搞呢?我不可能這2個頁面我都去登陸一遍吧。或者用笨方法這2個頁面都去查詢資料庫,如果有登陸狀態,就判斷是登陸的了。這種查詢資料庫的方案雖然可行,但是每次都要去查詢資料庫不是個事,會造成資料庫的壓力。 所以正是這種訴求,這個時候,一個新的客戶端儲存資料方式出現了:cookie。cookie是把少量的資訊儲存在使用者自己的電腦上,它在一個域名下是一個全域性的,只要設定它的儲存路徑在域名www.a.com下 ,那麼當用戶用瀏覽器訪問時,php就可以從這個域名的任意頁面讀取cookie中的資訊。所以就很好的解決了我在www.a.com/login.php頁面登陸了,我也可以在www.a.com/index.php獲取到這個登陸資訊了。同時又不用反覆去查詢資料庫。 雖然這種方案很不錯,也很快速方便,但是由於cookie 是存在使用者端,而且它本身儲存的尺寸大小也有限,最關鍵是使用者可以是可見的,並可以隨意的修改,很不安全。那如何又要安全,又可以方便的全域性讀取資訊呢?於是,這個時候,一種新的儲存會話機制:session 誕生了。

      Session 就是在一次會話中解決2次HTTP的請求的關聯,讓它們產生聯絡,讓2兩個頁面都能讀取到找個這個全域性的session資訊。session資訊存在於伺服器端,所以也就很好的解決了安全問題。

Token :

token是使用者身份的驗證方式,我們通常叫它:令牌。最簡單的token組成:uid(使用者唯一的身份標識)、time(當前時間的時間戳)、sign(簽名,由token的前幾位+鹽以雜湊演算法壓縮成一定長的十六進位制字串,可以防止惡意第三方拼接token請求伺服器)。還可以把不變的引數也放進token,避免多次查庫。

應用場景:

A:當用戶首次登入成功(註冊也是一種可以適用的場景)之後, 伺服器端就會生成一個 token 值,這個值,會在伺服器儲存token值(儲存在資料庫中),再將這個token值返回給客戶端.

B:客戶端拿到 token 值之後,進行本地儲存。(SP儲存是大家能夠比較支援和易於理解操作的儲存)

C:當客戶端再次傳送網路請求(一般不是登入請求)的時候,就會將這個 token 值附帶到引數中傳送給伺服器.

D:伺服器接收到客戶端的請求之後,會取出token值與儲存在本地(資料庫)中的token值做對比

對比一:如果兩個 token 值相同, 說明使用者登入成功過!當前使用者處於登入狀態!

對比二:如果沒有這個 token 值, 則說明沒有登入成功.

對比三:如果 token 值不同: 說明原來的登入資訊已經失效,讓使用者重新登入.

 

Cookie和Session的區別:

1、cookie資料存放在客戶的瀏覽器上,session資料放在伺服器上。

2、cookie不是很安全,別人可以分析存放在本地的cookie並進行cookie欺騙,考慮到安全應當使用session。

3、session會在一定時間內儲存在伺服器上。當訪問增多,會比較佔用你伺服器的效能,考慮到減輕伺服器效能方面,應當使用cookie。

4、單個cookie儲存的資料不能超過4K,很多瀏覽器都限制一個站點最多儲存20個cookie。

5、所以個人建議:

將登陸資訊等重要資訊存放為session

其他資訊如果需要保留,可以放在cookie中

Token 和 Session 的區別:

session和 token並不矛盾,作為身份認證token安全性比session好,因為每個請求都有簽名還能防止監聽以及重放攻擊,而session就必須靠鏈路層來保障通訊安全了。如上所說,如果你需要實現有狀態的會話,仍然可以增加session來在伺服器端儲存一些狀態

App通常用restful api跟server打交道。Rest是stateless的,也就是app不需要像browser那樣用cookie來儲存session,因此用session token來標示自己就夠了,session/state由api server的邏輯處理。如果你的後端不是stateless的rest api,那麼你可能需要在app裡儲存session.可以在app裡嵌入webkit,用一個隱藏的browser來管理cookie session.

Session是一種HTTP儲存機制,目的是為無狀態的HTTP提供的持久機制。所謂Session認證只是簡單的把User資訊儲存到Session裡,因為SID的不可預測性,暫且認為是安全的。這是一種認證手段。而Token,如果指的是OAuth Token或類似的機制的話,提供的是 認證 和 授權 ,認證是針對使用者,授權是針對App。其目的是讓 某App有權利訪問 某使用者 的資訊。這裡的Token是唯一的。不可以轉移到其它App上,也不可以轉到其它 使用者 上。轉過來說Session。Session只提供一種簡單的認證,即有此SID,即認為有此User的全部權利。是需要嚴格保密的,這個資料應該只儲存在站方,不應該共享給其它網站或者第三方App。所以簡單來說,如果你的使用者資料可能需要和第三方共享,或者允許第三方呼叫API介面,用Token。如果永遠只是自己的網站,自己的App,用什麼就無所謂了。

token就是令牌,比如你授權(登入)一個程式時,他就是個依據,判斷你是否已經授權該軟體;cookie就是寫在客戶端的一個txt檔案,裡面包括你登入資訊之類的,這樣你下次在登入某個網站,就會自動呼叫cookie自動登入使用者名稱;session和cookie差不多,只是session是寫在伺服器端的檔案,也需要在客戶端寫入cookie檔案,但是檔案裡是你的瀏覽器編號.Session的狀態是儲存在伺服器端,客戶端只有session id;而Token的狀態是儲存在客戶端。

如果覺得這篇文章對你有幫助,希望點下一個小小的star,謝謝。

文章使用到的原始碼

Ps:著作權歸作者所有,轉載請註明作者, 商業轉載請聯絡作者獲得授權,非商業轉載請註明出處(開頭或結尾請新增轉載出處,新增原文url地址),文章請勿濫用,也希望大家尊重筆者的勞動成果,謝謝。



作者:騎小豬看流星
連結:https://www.jianshu.com/p/bd1be47a16c1
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。