在做接口測(cè)試時(shí),經(jīng)常會(huì)碰到請(qǐng)求參數(shù)為token的類型幽勒,但是可能大部分測(cè)試人員對(duì)token嗜侮,cookie,session的區(qū)別還是一知半解啥容。
Cookie
cookie 是一個(gè)非常具體的東西锈颗,指的就是瀏覽器里面能永久存儲(chǔ)的一種數(shù)據(jù),僅僅是瀏覽器實(shí)現(xiàn)的一種數(shù)據(jù)存儲(chǔ)功能咪惠。
cookie由服務(wù)器生成击吱,發(fā)送給瀏覽器,瀏覽器把cookie以kv形式保存到某個(gè)目錄下的文本文件內(nèi)遥昧,下一次請(qǐng)求同一網(wǎng)站時(shí)會(huì)把該cookie發(fā)送給服務(wù)器覆醇。由于cookie是存在客戶端上的,所以瀏覽器加入了一些限制確保cookie不會(huì)被惡意使用渠鸽,同時(shí)不會(huì)占據(jù)太多磁盤(pán)空間叫乌,所以每個(gè)域的cookie數(shù)量是有限的。
Session
session 從字面上講徽缚,就是會(huì)話。這個(gè)就類似于你和一個(gè)人交談革屠,你怎么知道當(dāng)前和你交談的是張三而不是李四呢凿试?對(duì)方肯定有某種特征(長(zhǎng)相等)表明他就是張三排宰。
session 也是類似的道理,服務(wù)器要知道當(dāng)前發(fā)請(qǐng)求給自己的是誰(shuí)那婉。為了做這種區(qū)分板甘,服務(wù)器就要給每個(gè)客戶端分配不同的“身份標(biāo)識(shí)”,然后客戶端每次向服務(wù)器發(fā)請(qǐng)求的時(shí)候详炬,都帶上這個(gè)“身份標(biāo)識(shí)”盐类,服務(wù)器就知道這個(gè)請(qǐng)求來(lái)自于誰(shuí)了。至于客戶端怎么保存這個(gè)“身份標(biāo)識(shí)”呛谜,可以有很多種方式在跳,對(duì)于瀏覽器客戶端,大家都默認(rèn)采用 cookie 的方式隐岛。
服務(wù)器使用session把用戶的信息臨時(shí)保存在了服務(wù)器上猫妙,用戶離開(kāi)網(wǎng)站后session會(huì)被銷毀。這種用戶信息存儲(chǔ)方式相對(duì)cookie來(lái)說(shuō)更安全聚凹,可是session有一個(gè)缺陷:如果web服務(wù)器做了負(fù)載均衡割坠,那么下一個(gè)操作請(qǐng)求到了另一臺(tái)服務(wù)器的時(shí)候session會(huì)丟失。
Token
Token的引入:Token是在客戶端頻繁向服務(wù)端請(qǐng)求數(shù)據(jù)妒牙,服務(wù)端頻繁的去數(shù)據(jù)庫(kù)查詢用戶名和密碼并進(jìn)行對(duì)比彼哼,判斷用戶名和密碼正確與否,并作出相應(yīng)提示湘今,在這樣的背景下敢朱,Token便應(yīng)運(yùn)而生。
Token的定義:Token是服務(wù)端生成的一串字符串象浑,以作客戶端進(jìn)行請(qǐng)求的一個(gè)令牌蔫饰,當(dāng)?shù)谝淮蔚卿浐螅?wù)器生成一個(gè)Token便將此Token返回給客戶端愉豺,以后客戶端只需帶上這個(gè)Token前來(lái)請(qǐng)求數(shù)據(jù)即可篓吁,無(wú)需再次帶上用戶名和密碼。最簡(jiǎn)單的token組成:uid(用戶唯一的身份標(biāo)識(shí))蚪拦、time(當(dāng)前時(shí)間的時(shí)間戳)杖剪、sign(簽名,由token的前幾位+鹽以哈希算法壓縮成一定長(zhǎng)的十六進(jìn)制字符串驰贷,可以防止惡意第三方拼接token請(qǐng)求服務(wù)器)盛嘿。
使用Token的目的:Token的目的是為了減輕服務(wù)器的壓力,減少頻繁的查詢數(shù)據(jù)庫(kù)括袒,使服務(wù)器更加健壯次兆。
傳統(tǒng)身份驗(yàn)證
HTTP 是一種沒(méi)有狀態(tài)的協(xié)議,也就是它并不知道是誰(shuí)是訪問(wèn)應(yīng)用锹锰。這里我們把用戶看成是客戶端芥炭,客戶端使用用戶名還有密碼通過(guò)了身份驗(yàn)證漓库,不過(guò)下回這個(gè)客戶端再發(fā)送請(qǐng)求時(shí)候,還得再驗(yàn)證一下园蝠。
解決的方法就是渺蒿,當(dāng)用戶請(qǐng)求登錄的時(shí)候,如果沒(méi)有問(wèn)題彪薛,我們?cè)诜?wù)端生成一條記錄茂装,這個(gè)記錄里可以說(shuō)明一下登錄的用戶是誰(shuí),然后把這條記錄的 ID 號(hào)發(fā)送給客戶端善延,客戶端收到以后把這個(gè) ID 號(hào)存儲(chǔ)在 Cookie 里少态,下次這個(gè)用戶再向服務(wù)端發(fā)送請(qǐng)求的時(shí)候,可以帶著這個(gè) Cookie 挚冤,這樣服務(wù)端會(huì)驗(yàn)證一個(gè)這個(gè) Cookie 里的信息况增,看看能不能在服務(wù)端這里找到對(duì)應(yīng)的記錄,如果可以训挡,說(shuō)明用戶已經(jīng)通過(guò)了身份驗(yàn)證澳骤,就把用戶請(qǐng)求的數(shù)據(jù)返回給客戶端。
上面說(shuō)的就是 Session澜薄,我們需要在服務(wù)端存儲(chǔ)為登錄的用戶生成的 Session 为肮,這些 Session 可能會(huì)存儲(chǔ)在內(nèi)存,磁盤(pán)肤京,或者數(shù)據(jù)庫(kù)里颊艳。我們可能需要在服務(wù)端定期的去清理過(guò)期的 Session 。
基于 Token 的身份驗(yàn)證
使用基于 Token 的身份驗(yàn)證方法忘分,在服務(wù)端不需要存儲(chǔ)用戶的登錄記錄棋枕。大概的流程是這樣的:
- 客戶端使用用戶名跟密碼請(qǐng)求登錄
- 服務(wù)端收到請(qǐng)求,去驗(yàn)證用戶名與密碼
- 驗(yàn)證成功后妒峦,服務(wù)端會(huì)簽發(fā)一個(gè) Token重斑,再把這個(gè) Token 發(fā)送給客戶端
- 客戶端收到 Token 以后可以把它存儲(chǔ)起來(lái),比如放在 Cookie 里或者 Local Storage 里
- 客戶端每次向服務(wù)端請(qǐng)求資源的時(shí)候需要帶著服務(wù)端簽發(fā)的 Token
- 服務(wù)端收到請(qǐng)求肯骇,然后去驗(yàn)證客戶端請(qǐng)求里面帶著的 Token窥浪,如果驗(yàn)證成功,就向客戶端返回請(qǐng)求的數(shù)據(jù)
APP登錄的時(shí)候發(fā)送加密的用戶名和密碼到服務(wù)器笛丙,服務(wù)器驗(yàn)證用戶名和密碼漾脂,如果成功,以某種方式比如隨機(jī)生成32位的字符串作為token胚鸯,存儲(chǔ)到服務(wù)器中骨稿,并返回token到APP,以后APP請(qǐng)求時(shí),凡是需要驗(yàn)證的地方都要帶上該token啊终,然后服務(wù)器端驗(yàn)證token镜豹,成功返回所需要的結(jié)果傲须,失敗返回錯(cuò)誤信息蓝牲,讓他重新登錄。其中服務(wù)器上token設(shè)置一個(gè)有效期泰讽,每次APP請(qǐng)求的時(shí)候都驗(yàn)證token和有效期例衍。
那么我的問(wèn)題來(lái)了: 1.服務(wù)器上的token存儲(chǔ)到數(shù)據(jù)庫(kù)中,每次查詢會(huì)不會(huì)很費(fèi)時(shí)已卸。如果不存儲(chǔ)到數(shù)據(jù)庫(kù)佛玄,應(yīng)該存儲(chǔ)到哪里呢。 2.客戶端得到的token肯定要加密存儲(chǔ)的累澡,發(fā)送token的時(shí)候再解密梦抢。存儲(chǔ)到數(shù)據(jù)庫(kù)還是配置文件呢?
token是個(gè)易失數(shù)據(jù)愧哟,丟了無(wú)非讓用戶重新登錄一下奥吩,新浪微博動(dòng)不動(dòng)就讓我重新登錄,反正這事兒我是無(wú)所謂啦蕊梧。
所以如果你覺(jué)得普通的數(shù)據(jù)庫(kù)表?yè)尾蛔×讼己眨梢苑诺?MSSQL/MySQL 的內(nèi)存表里(不過(guò)據(jù)說(shuō)mysql的內(nèi)存表性能提升有限),可以放到 Memcache里(講真肥矢,這個(gè)是挺常見(jiàn)的策略)端衰,可以放到redis里(我做過(guò)這樣的實(shí)現(xiàn)),甚至可以放到 OpenResty 的變量字典里(只要你有信心不爆內(nèi)存)甘改。
token是個(gè)憑條旅东,不過(guò)它比門(mén)票溫柔多了,門(mén)票丟了重新花錢(qián)買十艾,token丟了重新操作下認(rèn)證一個(gè)就可以了抵代,因此token丟失的代價(jià)是可以忍受的——前提是你別丟太頻繁,要是讓用戶隔三差五就認(rèn)證一次那就損失用戶體驗(yàn)了疟羹。
基于這個(gè)出發(fā)點(diǎn)主守,如果你認(rèn)為用數(shù)據(jù)庫(kù)來(lái)保持token查詢時(shí)間太長(zhǎng),會(huì)成為你系統(tǒng)的瓶頸或者隱患榄融,可以放在內(nèi)存當(dāng)中参淫。
比如memcached、redis愧杯,KV方式很適合你對(duì)token查詢的需求涎才。
這個(gè)不會(huì)太占內(nèi)存,比如你的token是32位字符串,要是你的用戶量在百萬(wàn)級(jí)或者千萬(wàn)級(jí)耍铜,那才多少內(nèi)存邑闺。
要是數(shù)據(jù)量真的大到單機(jī)內(nèi)存扛不住,或者覺(jué)得一宕機(jī)全丟風(fēng)險(xiǎn)大棕兼,只要這個(gè)token生成是足夠均勻的陡舅,高低位切一下分到不同機(jī)器上就行,內(nèi)存絕對(duì)不會(huì)是問(wèn)題伴挚。
客戶端方面這個(gè)除非你有一個(gè)非常安全的辦法靶衍,比如操作系統(tǒng)提供的隱私數(shù)據(jù)存儲(chǔ),那token肯定會(huì)存在泄露的問(wèn)題茎芋。比如我拿到你的手機(jī)颅眶,把你的token拷出來(lái),在過(guò)期之前就都可以以你的身份在別的地方登錄田弥。
解決這個(gè)問(wèn)題的一個(gè)簡(jiǎn)單辦法
1涛酗、在存儲(chǔ)的時(shí)候把token進(jìn)行對(duì)稱加密存儲(chǔ),用時(shí)解開(kāi)偷厦。
2商叹、將請(qǐng)求URL、時(shí)間戳沪哺、token三者進(jìn)行合并加鹽簽名沈自,服務(wù)端校驗(yàn)有效性。
這兩種辦法的出發(fā)點(diǎn)都是:竊取你存儲(chǔ)的數(shù)據(jù)較為容易辜妓,而反匯編你的程序hack你的加密解密和簽名算法是比較難的枯途。然而其實(shí)說(shuō)難也不難,所以終究是防君子不防小人的做法籍滴。話說(shuō)加密存儲(chǔ)一個(gè)你要是被人扒開(kāi)客戶端看也不會(huì)被噴明文存儲(chǔ)……
方法1它拿到存儲(chǔ)的密文解不開(kāi)酪夷、方法2它不知道你的簽名算法和鹽,兩者可以結(jié)合食用孽惰。
但是如果token被人拷走晚岭,他自然也能植入到自己的手機(jī)里面,那到時(shí)候他的手機(jī)也可以以你的身份來(lái)用著勋功,這你就瞎了坦报。
于是可以提供一個(gè)讓用戶可以主動(dòng)expire一個(gè)過(guò)去的token類似的機(jī)制,在被盜的時(shí)候能遠(yuǎn)程止損狂鞋。
在網(wǎng)絡(luò)層面上token明文傳輸?shù)脑挄?huì)非常的危險(xiǎn)片择,所以建議一定要使用HTTPS,并且把token放在post body里骚揍。
補(bǔ)充:
cookie與session的區(qū)別
1字管、cookie數(shù)據(jù)存放在客戶端上啰挪,session數(shù)據(jù)放在服務(wù)器上。
2嘲叔、cookie不是很安全亡呵,別人可以分析存放在本地的COOKIE并進(jìn)行COOKIE欺騙
考慮到安全應(yīng)當(dāng)使用session。
3硫戈、session會(huì)在一定時(shí)間內(nèi)保存在服務(wù)器上锰什。當(dāng)訪問(wèn)增多,會(huì)比較占用你服務(wù)器的性能
考慮到減輕服務(wù)器性能方面掏愁,應(yīng)當(dāng)使用COOKIE歇由。
4、單個(gè)cookie保存的數(shù)據(jù)不能超過(guò)4K果港,很多瀏覽器都限制一個(gè)站點(diǎn)最多保存20個(gè)cookie。
5糊昙、所以個(gè)人建議:
將登陸信息等重要信息存放為SESSION
其他信息如果需要保留辛掠,可以放在COOKIE中
session與token的區(qū)別
session 和 oauth token并不矛盾,作為身份認(rèn)證 token安全性比session好释牺,因?yàn)槊總€(gè)請(qǐng)求都有簽名還能防止監(jiān)聽(tīng)以及重放攻擊萝衩,而session就必須靠鏈路層來(lái)保障通訊安全了。如上所說(shuō)没咙,如果你需要實(shí)現(xiàn)有狀態(tài)的會(huì)話猩谊,仍然可以增加session來(lái)在服務(wù)器端保存一些狀態(tài)
App通常用restful api跟server打交道。Rest是stateless的祭刚,也就是app不需要像browser那樣用cookie來(lái)保存session,因此用session token來(lái)標(biāo)示自己就夠了牌捷,session/state由api server的邏輯處理。 如果你的后端不是stateless的rest api, 那么你可能需要在app里保存session.可以在app里嵌入webkit,用一個(gè)隱藏的browser來(lái)管理cookie session.
Session 是一種HTTP存儲(chǔ)機(jī)制涡驮,目的是為無(wú)狀態(tài)的HTTP提供的持久機(jī)制暗甥。所謂Session 認(rèn)證只是簡(jiǎn)單的把User 信息存儲(chǔ)到Session 里,因?yàn)镾ID 的不可預(yù)測(cè)性捉捅,暫且認(rèn)為是安全的撤防。這是一種認(rèn)證手段。 而Token 棒口,如果指的是OAuth Token 或類似的機(jī)制的話寄月,提供的是 認(rèn)證 和 授權(quán) ,認(rèn)證是針對(duì)用戶无牵,授權(quán)是針對(duì)App 漾肮。其目的是讓 某App有權(quán)利訪問(wèn) 某用戶 的信息。這里的 Token是唯一的合敦。不可以轉(zhuǎn)移到其它 App上初橘,也不可以轉(zhuǎn)到其它 用戶 上。 轉(zhuǎn)過(guò)來(lái)說(shuō)Session 。Session只提供一種簡(jiǎn)單的認(rèn)證保檐,即有此 SID耕蝉,即認(rèn)為有此 User的全部權(quán)利。是需要嚴(yán)格保密的夜只,這個(gè)數(shù)據(jù)應(yīng)該只保存在站方垒在,不應(yīng)該共享給其它網(wǎng)站或者第三方App。 所以簡(jiǎn)單來(lái)說(shuō)扔亥,如果你的用戶數(shù)據(jù)可能需要和第三方共享场躯,或者允許第三方調(diào)用 API 接口,用 Token 旅挤。如果永遠(yuǎn)只是自己的網(wǎng)站踢关,自己的 App,用什么就無(wú)所謂了粘茄。
打破誤解:
“只要關(guān)閉瀏覽器 签舞,session就消失了?”
不對(duì)柒瓣。對(duì)session來(lái)說(shuō)儒搭,除非程序通知服務(wù)器刪除一個(gè)session,否則服務(wù)器會(huì)一直保留芙贫,程序一般都是在用戶做log off的時(shí)候發(fā)個(gè)指令去刪除session搂鲫。
然而瀏覽器從來(lái)不會(huì)主動(dòng)在關(guān)閉之前通知服務(wù)器它將要關(guān)閉,因此服務(wù)器根本不會(huì)有機(jī)會(huì)知道瀏覽器已經(jīng)關(guān)閉磺平,之所以會(huì)有這種錯(cuò)覺(jué)魂仍,是大部分session機(jī)制都使用會(huì)話cookie來(lái)保存session id,而關(guān)閉瀏覽器后這個(gè)session id就消失了褪秀,再次連接服務(wù)器時(shí)也就無(wú)法找到原來(lái)的session蓄诽。如果服務(wù)器設(shè)置的cookie被保存在硬盤(pán)上,或者使用某種手段改寫(xiě)瀏覽器發(fā)出的HTTP請(qǐng)求頭媒吗,把原來(lái)的session id發(fā)送給服務(wù)器仑氛,則再次打開(kāi)瀏覽器仍然能夠打開(kāi)原來(lái)的session.
恰恰是由于關(guān)閉瀏覽器不會(huì)導(dǎo)致session被刪除,迫使服務(wù)器為session設(shè)置了一個(gè)失效時(shí)間闸英,當(dāng)距離客戶端上一次使用session的時(shí)間超過(guò)這個(gè)失效時(shí)間時(shí)锯岖,服務(wù)器就可以以為客戶端已經(jīng)停止了活動(dòng),才會(huì)把session刪除以節(jié)省存儲(chǔ)空間甫何。
總結(jié):
Token 完全由應(yīng)用管理出吹,所以它可以避開(kāi)同源策略
Token 可以避免 CSRF 攻擊
Token 可以是無(wú)狀態(tài)的,可以在多個(gè)服務(wù)間共享
如果你的用戶數(shù)據(jù)可能需要和第三方共享辙喂,或者允許第三方調(diào)用 API 接口捶牢,用 Token鸠珠,如果之上自己的那就無(wú)所謂了