Cookie、Session娱挨、Token那點事兒(原創(chuàng))

本文已獨家授權(quán) 鴻洋( hongyangAndroid?) 公眾號發(fā)布余指!

前言:新公司項目中使用到了Cookie,在各大Android技術(shù)討論群向前輩們?nèi)〗?jīng)討論這cookie跷坝、session酵镜、token這仨哥們的時候,很多開發(fā)者說法不一各抒已見柴钻,所以是時候回顧下http基礎(chǔ)以及總結(jié)開發(fā)經(jīng)驗了淮韭。本文重在科普分析Cookie、Session贴届、Token的基本概念和應(yīng)用場景靠粪;Okhttp框架下對Cookie的管理使用。文章如果寫的不好請各位開發(fā)者老司機私聊或者在評論區(qū)指點提出issue毫蚓。

文章使用到的源碼

什么是Cookie占键?

Cookie 技術(shù)產(chǎn)生源于 HTTP 協(xié)議在互聯(lián)網(wǎng)上的急速發(fā)展。隨著互聯(lián)網(wǎng)時代的策馬奔騰元潘,帶寬等限制不存在了畔乙,人們需要更復(fù)雜的互聯(lián)網(wǎng)交互活動,就必須同服務(wù)器保持活動狀態(tài)(簡稱:奔砼活)啸澡。于是,在瀏覽器發(fā)展初期氮帐,為了適應(yīng)用戶的需求技術(shù)上推出了各種保持 Web 瀏覽狀態(tài)的手段嗅虏,其中就包括了 Cookie 技術(shù)。Cookie 在計算機中是個存儲在瀏覽器目錄中的文本文件上沐,當(dāng)瀏覽器運行時皮服,存儲在 RAM 中發(fā)揮作用 (此種 Cookies 稱作 Session Cookies),一旦用戶從該網(wǎng)站或服務(wù)器退出,Cookie 可存儲在用戶本地的硬盤上 (此種 Cookies 稱作 Persistent Cookies)龄广。

Cookie 起源:1993 年硫眯,網(wǎng)景公司雇員 Lou Montulli 為了讓用戶在訪問某網(wǎng)站時,進一步提高訪問速度择同,同時也為了進一步實現(xiàn)個人化網(wǎng)絡(luò)两入,發(fā)明了今天廣泛使用的 Cookie。(所以敲才,適當(dāng)?shù)耐祽幸矔龠M人類計算機發(fā)展史的一小步~)

Cookie時效性:目前有些 Cookie 是臨時的裹纳,有些則是持續(xù)的。臨時的 Cookie 只在瀏覽器上保存一段規(guī)定的時間紧武,一旦超過規(guī)定的時間剃氧,該 Cookie 就會被系統(tǒng)清除。

Cookie使用限制:Cookie 必須在 HTML 文件的內(nèi)容輸出之前設(shè)置阻星;不同的瀏覽器 (Netscape Navigator朋鞍、Internet Explorer) 對 Cookie 的處理不一致,使用時一定要考慮妥箕;客戶端用戶如果設(shè)置禁止 Cookie滥酥,則 Cookie 不能建立。 并且在客戶端畦幢,一個瀏覽器能創(chuàng)建的 Cookie 數(shù)量最多為 300 個恨狈,并且每個不能超過 4KB,每個 Web 站點能設(shè)置的 Cookie 總數(shù)不能超過 20 個呛讲。

執(zhí)行流程:

A:首先,客戶端會發(fā)送一個http請求到服務(wù)器端返奉。

B: 服務(wù)器端接受客戶端請求后贝搁,發(fā)送一個http響應(yīng)到客戶端,這個響應(yīng)頭芽偏,其中就包含Set-Cookie頭部雷逆。

C:在客戶端發(fā)起的第二次請求(注意:如果服務(wù)器需要我們帶上Cookie,我們就需要在B步驟上面拿到這個Cookie然后作為請求頭一起發(fā)起第二次請求)污尉,提供給了服務(wù)器端可以用來唯一標(biāo)識客戶端身份的信息膀哲。這時,服務(wù)器端也就可以判斷客戶端是否啟用了cookies被碗。盡管长赞,用戶可能在和應(yīng)用程序交互的過程中突然禁用cookies的使用拳氢,但是,這個情況基本是不太可能發(fā)生的,所以可以不加以考慮矾芙,這在實踐中也被證明是對的低葫。

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

客戶端與服務(wù)端的Cookie流程圖

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

請求頭上面的Cookie

那么壶谒,上面都是談瀏覽器上的Cookie云矫,那么在Android開發(fā)中,我們該如何去管理和使用Cookie汗菜?Okhttp作為經(jīng)典到爆的網(wǎng)絡(luò)框架让禀,它的API(本文是基于Okhttp3.0版本以上,3.0以下的版本API有所不同)是通過OkhttpClient中的CookieJar或者攔截器去管理Cookie的呵俏。理論上堆缘,我們只需在構(gòu)建單例OkhttpClient的時候,設(shè)置cookiejar或者攔截器普碎,然后具體的操作(具體的操作也就是保存Cookie吼肥,取Cookie),Okhttp框架就會幫我們自動管理Cookie麻车。如下圖:

OKhttp的管理策略

這是其中一種通過集合的增查特性缀皱,就可以簡單有效的幫我們管理Cookie。但我們還是要通過源代碼去一探究竟动猬。首先啤斗,CookieJar是一個接口。

CookieJar

英文注釋翻譯過來就是(對應(yīng)段落翻譯):

CookieJar這個接口為HTTP cookies提供了強大的支持和相關(guān)策略赁咙。

這種策略的實現(xiàn)作用會負責(zé)選擇接受和拒絕那些cookie钮莲。一個合理的策略是拒絕所有的cookie,盡管這樣會干擾需要cookie的基于會話的自身身份驗證方案彼水。

作為Cookie的持久性崔拥,該接口的實現(xiàn)也必須要提供Cookie的存儲。一種簡單的實現(xiàn)可以將cookie存儲在內(nèi)存中;復(fù)雜的系統(tǒng)可以使用文件系統(tǒng)用于保存已接受的cookie的數(shù)據(jù)庫凤覆。這里的鏈接指定cookie存儲模型更新和過期的cookie的策略链瓦。

所以,Okhttp的源碼告知我們可以將cookie存儲在內(nèi)存中;復(fù)雜的系統(tǒng)可以使用文件系統(tǒng)用于保存已接受的cookie的數(shù)據(jù)庫盯桦。因此慈俯,我們就可以通過Map去簡單的管理和使用。

繼續(xù)分析CookieJar接口里面的方法拥峦,依舊上源碼

CookieJar里面的方法

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

saveFromResponse方法翻譯:根據(jù)這個jar的方法,可以將cookie從一個HTTP響應(yīng)保存到這里略号。請注意步鉴,如果響應(yīng)揪胃,此方法可能被稱為第二次HTTP響應(yīng),包括一個追蹤氛琢。對于這個隱蔽的HTTP特性喊递,這里的cookie只包含其追蹤的cookie。簡單點理解就是如果我們使用了這個方法阳似,就會進行追蹤(說白了就是客戶端請求成功以后骚勘,在響應(yīng)頭里面去存cookie)

loadForRequest方法翻譯:將cookie從這個方法加載到一個HTTP請求到指定的url。這個方法從網(wǎng)絡(luò)上返回的結(jié)果可能是一個空集合撮奏。簡單的實現(xiàn)將返回尚未過期的已接受的cookie去進行匹配俏讹。(說白了就是加載url的時候在請求頭帶上cookie)。

通過CookieJar打印Cookie信息

這樣畜吊,我們通過以上代碼就可以完成了Cookie的非持久化泽疆。什么,非持久化玲献,這又是神馬殉疼?

繼續(xù)給大家科普,在上面說道捌年,Cookie是具有時效性的瓢娜,所以,Cookie的管理又分為持久化Cookie和非持久化Cookie礼预。非持久化Cookie存儲在內(nèi)存中眠砾,也就意味著,其生命周期基本和app保持一致,app關(guān)閉后托酸,Cookie丟失褒颈。而持久化Cookie則是存儲在本地磁盤中,app關(guān)閉后不丟失励堡。那么哈肖,如果我們要使用Cookie的持久化策略,思想可以參考上面的非持久化策略念秧,只需要將存儲方式改一下即可:

A:通過響應(yīng)攔截器從response取出cookie并保存到本地,通過請求攔截器從本地取出cookie并添加到請求中

B:自定義CookieJar布疼,在saveFromResponse()中保存cookie到本地摊趾,在loadForRequest()從本地取出cookie。

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

保存cookie攔截器-1


保存cookie攔截器-2

這個SaveCookiesInterceptor攔截器的實現(xiàn)砾层,是首先從response獲取set-cookie字段的值,然后通過SharedPreferences保存在本地贱案。

將Cookie添加到請求頭

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

寫好這兩個攔截器之后侨糟,我們只需要將實例對象放進OkhttpClient里面即可快速的完成Cookie持久化操作碍扔。(PS:這兩個攔截器在同步Cookie的時候也是超級好用)。

Okhttp使用cookie攔截器

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

客戶端與H5同步Cookie

Session :

Session是對于服務(wù)端來說的,客戶端是沒有Session一說的溶耘。Session是服務(wù)器在和客戶端建立連接時添加客戶端連接標(biāo)志二拐,最終會在服務(wù)器軟件(Apache、Tomcat凳兵、JBoss)轉(zhuǎn)化為一個臨時Cookie發(fā)送給給客戶端百新,當(dāng)客戶端第一請求時服務(wù)器會檢查是否攜帶了這個Session(臨時Cookie),如果沒有則會添加Session庐扫,如果有就拿出這個Session來做相關(guān)操作饭望。

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

? ? ? ? 在說session是啥之前,我們先來說說為什么會出現(xiàn)session會話聚蝶,它出現(xiàn)的機理是什么杰妓?

? ? 我們知道,我們用瀏覽器打開一個網(wǎng)頁碘勉,用到的是HTTP協(xié)議巷挥,了解計算機的應(yīng)該都知道這個協(xié)議,它是無狀態(tài)的验靡,什么是無狀態(tài)呢倍宾?就是說這一次請求和上一次請求是沒有任何關(guān)系的,互不認識的胜嗓,沒有關(guān)聯(lián)的高职。但是這種無狀態(tài)的的好處是快速。所以就會帶來一個問題就是辞州,我希望幾個請求的頁面要有關(guān)聯(lián)怔锌,比如:我在www.a.com/login.php里面登陸了,我在www.a.com/index.php 也希望是登陸狀態(tài)变过,但是埃元,這是2個不同的頁面,也就是2個不同的HTTP請求媚狰,這2個HTTP請求是無狀態(tài)的岛杀,也就是無關(guān)聯(lián)的,所以無法單純的在index.php中讀取到它在login.php中已經(jīng)登陸了崭孤!

? ? ? ? 那咋搞呢类嗤?我不可能這2個頁面我都去登陸一遍吧糊肠。或者用笨方法這2個頁面都去查詢數(shù)據(jù)庫遗锣,如果有登陸狀態(tài)货裹,就判斷是登陸的了。這種查詢數(shù)據(jù)庫的方案雖然可行黄伊,但是每次都要去查詢數(shù)據(jù)庫不是個事泪酱,會造成數(shù)據(jù)庫的壓力。 所以正是這種訴求还最,這個時候墓阀,一個新的客戶端存儲數(shù)據(jù)方式出現(xiàn)了:cookie。cookie是把少量的信息存儲在用戶自己的電腦上拓轻,它在一個域名下是一個全局的斯撮,只要設(shè)置它的存儲路徑在域名www.a.com下 ,那么當(dāng)用戶用瀏覽器訪問時扶叉,php就可以從這個域名的任意頁面讀取cookie中的信息勿锅。所以就很好的解決了我在www.a.com/login.php頁面登陸了,我也可以在www.a.com/index.php獲取到這個登陸信息了枣氧。同時又不用反復(fù)去查詢數(shù)據(jù)庫溢十。 雖然這種方案很不錯,也很快速方便达吞,但是由于cookie 是存在用戶端张弛,而且它本身存儲的尺寸大小也有限,最關(guān)鍵是用戶可以是可見的酪劫,并可以隨意的修改吞鸭,很不安全。那如何又要安全覆糟,又可以方便的全局讀取信息呢刻剥?于是,這個時候滩字,一種新的存儲會話機制:session 誕生了造虏。

? ? ? Session 就是在一次會話中解決2次HTTP的請求的關(guān)聯(lián),讓它們產(chǎn)生聯(lián)系麦箍,讓2兩個頁面都能讀取到找個這個全局的session信息漓藕。session信息存在于服務(wù)器端,所以也就很好的解決了安全問題内列。

Token :

token是用戶身份的驗證方式,我們通常叫它:令牌背率。最簡單的token組成:uid(用戶唯一的身份標(biāo)識)话瞧、time(當(dāng)前時間的時間戳)嫩与、sign(簽名,由token的前幾位+鹽以哈希算法壓縮成一定長的十六進制字符串交排,可以防止惡意第三方拼接token請求服務(wù)器)划滋。還可以把不變的參數(shù)也放進token,避免多次查庫埃篓。

應(yīng)用場景:

A:當(dāng)用戶首次登錄成功(注冊也是一種可以適用的場景)之后, 服務(wù)器端就會生成一個 token 值处坪,這個值,會在服務(wù)器保存token值(保存在數(shù)據(jù)庫中)架专,再將這個token值返回給客戶端.

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

C:當(dāng)客戶端再次發(fā)送網(wǎng)絡(luò)請求(一般不是登錄請求)的時候,就會將這個 token 值附帶到參數(shù)中發(fā)送給服務(wù)器.

D:服務(wù)器接收到客戶端的請求之后,會取出token值與保存在本地(數(shù)據(jù)庫)中的token值做對比

對比一:如果兩個 token 值相同, 說明用戶登錄成功過!當(dāng)前用戶處于登錄狀態(tài)!

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

對比三:如果 token 值不同: 說明原來的登錄信息已經(jīng)失效,讓用戶重新登錄.


Cookie和Session的區(qū)別:

1部脚、cookie數(shù)據(jù)存放在客戶的瀏覽器上想邦,session數(shù)據(jù)放在服務(wù)器上。

2委刘、cookie不是很安全丧没,別人可以分析存放在本地的cookie并進行cookie欺騙,考慮到安全應(yīng)當(dāng)使用session。

3锡移、session會在一定時間內(nèi)保存在服務(wù)器上呕童。當(dāng)訪問增多,會比較占用你服務(wù)器的性能,考慮到減輕服務(wù)器性能方面淆珊,應(yīng)當(dāng)使用cookie夺饲。

4、單個cookie保存的數(shù)據(jù)不能超過4K套蒂,很多瀏覽器都限制一個站點最多保存20個cookie钞支。

5、所以個人建議:

將登陸信息等重要信息存放為session

其他信息如果需要保留操刀,可以放在cookie中

Token 和 Session 的區(qū)別:

session和 token并不矛盾烁挟,作為身份認證token安全性比session好,因為每個請求都有簽名還能防止監(jiān)聽以及重放攻擊骨坑,而session就必須靠鏈路層來保障通訊安全了撼嗓。如上所說,如果你需要實現(xiàn)有狀態(tài)的會話欢唾,仍然可以增加session來在服務(wù)器端保存一些狀態(tài)

App通常用restful api跟server打交道且警。Rest是stateless的,也就是app不需要像browser那樣用cookie來保存session,因此用session token來標(biāo)示自己就夠了礁遣,session/state由api server的邏輯處理斑芜。如果你的后端不是stateless的rest api,那么你可能需要在app里保存session.可以在app里嵌入webkit,用一個隱藏的browser來管理cookie session.

Session是一種HTTP存儲機制,目的是為無狀態(tài)的HTTP提供的持久機制祟霍。所謂Session認證只是簡單的把User信息存儲到Session里杏头,因為SID的不可預(yù)測性盈包,暫且認為是安全的。這是一種認證手段醇王。而Token呢燥,如果指的是OAuth Token或類似的機制的話,提供的是 認證 和 授權(quán) 寓娩,認證是針對用戶叛氨,授權(quán)是針對App。其目的是讓 某App有權(quán)利訪問 某用戶 的信息棘伴。這里的Token是唯一的寞埠。不可以轉(zhuǎn)移到其它App上,也不可以轉(zhuǎn)到其它 用戶 上排嫌。轉(zhuǎn)過來說Session畸裳。Session只提供一種簡單的認證,即有此SID淳地,即認為有此User的全部權(quán)利怖糊。是需要嚴(yán)格保密的,這個數(shù)據(jù)應(yīng)該只保存在站方颇象,不應(yīng)該共享給其它網(wǎng)站或者第三方App伍伤。所以簡單來說,如果你的用戶數(shù)據(jù)可能需要和第三方共享遣钳,或者允許第三方調(diào)用API接口扰魂,用Token。如果永遠只是自己的網(wǎng)站蕴茴,自己的App劝评,用什么就無所謂了。

token就是令牌倦淀,比如你授權(quán)(登錄)一個程序時蒋畜,他就是個依據(jù),判斷你是否已經(jīng)授權(quán)該軟件撞叽;cookie就是寫在客戶端的一個txt文件姻成,里面包括你登錄信息之類的,這樣你下次在登錄某個網(wǎng)站愿棋,就會自動調(diào)用cookie自動登錄用戶名科展;session和cookie差不多,只是session是寫在服務(wù)器端的文件糠雨,也需要在客戶端寫入cookie文件才睹,但是文件里是你的瀏覽器編號.Session的狀態(tài)是存儲在服務(wù)器端,客戶端只有session id;而Token的狀態(tài)是存儲在客戶端琅攘。

如果這篇文章對您有開發(fā)or學(xué)習(xí)上的些許幫助真椿,希望各位看官留下寶貴的star,謝謝乎澄。

文章使用到的源碼

Ps:著作權(quán)歸作者所有,轉(zhuǎn)載請注明作者, 商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處(開頭或結(jié)尾請?zhí)砑愚D(zhuǎn)載出處测摔,添加原文url地址),文章請勿濫用,也希望大家尊重筆者的勞動成果,謝謝置济。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市锋八,隨后出現(xiàn)的幾起案子浙于,更是在濱河造成了極大的恐慌,老刑警劉巖挟纱,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件羞酗,死亡現(xiàn)場離奇詭異,居然都是意外死亡紊服,警方通過查閱死者的電腦和手機檀轨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來欺嗤,“玉大人参萄,你說我怎么就攤上這事〖灞” “怎么了讹挎?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長吆玖。 經(jīng)常有香客問我筒溃,道長,這世上最難降的妖魔是什么沾乘? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任怜奖,我火速辦了婚禮,結(jié)果婚禮上意鲸,老公的妹妹穿的比我還像新娘烦周。我一直安慰自己,他們只是感情好怎顾,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布读慎。 她就那樣靜靜地躺著,像睡著了一般槐雾。 火紅的嫁衣襯著肌膚如雪夭委。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天募强,我揣著相機與錄音株灸,去河邊找鬼崇摄。 笑死,一個胖子當(dāng)著我的面吹牛慌烧,可吹牛的內(nèi)容都是我干的逐抑。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼屹蚊,長吁一口氣:“原來是場噩夢啊……” “哼厕氨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起汹粤,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤命斧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后嘱兼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體国葬,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年芹壕,在試婚紗的時候發(fā)現(xiàn)自己被綠了汇四。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡踢涌,死狀恐怖船殉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情斯嚎,我是刑警寧澤利虫,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站堡僻,受9級特大地震影響糠惫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜钉疫,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一硼讽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧牲阁,春花似錦固阁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至凌唬,卻和暖如春并齐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工况褪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留撕贞,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓测垛,卻偏偏與公主長得像捏膨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子食侮,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容