前言
了解http 協(xié)議中的cookie和session機制的作用和原理牌借,以及它們在iOS開發(fā)中的使用。
Cookie
1割按、什么是Cookie
Cookie是由服務器端生成膨报,發(fā)送給User-Agent(一般是瀏覽器或者客戶端),瀏覽器會將Cookie的key/value保存到某個目錄下的文本文件內,下次請求同一網(wǎng)站地址時就發(fā)送該Cookie給服務器哲虾。Cookie必然會通過HTTP的Respone傳過來,并且Cookie在Respone中的HTTP header中择示。
為什么需要Cookie束凑?
HTTP是一種無狀態(tài)的協(xié)議,客戶端與服務器建立連接并傳輸數(shù)據(jù)栅盲,數(shù)據(jù)傳輸完成后汪诉,連接就會關閉。再次交互數(shù)據(jù)需要建立新的連接谈秫,因此扒寄,服務器無法從連接上跟蹤會話,也無法知道用戶上一次做了什么拟烫。這嚴重阻礙了基于Web應用程序的交互该编,也影響用戶的交互體驗。如:在網(wǎng)絡有時候需要用戶登錄才進一步操作硕淑,用戶輸入用戶名密碼登錄后课竣,瀏覽了幾個頁面,由于HTTP的無狀態(tài)性置媳,服務器并不知道用戶有沒有登錄于樟。
Cookie是解決HTTP無狀態(tài)性的有效手段,服務器可以設置或讀取Cookie中所包含的信息拇囊。當用戶登錄后迂曲,服務器會發(fā)送包含登錄憑據(jù)的Cookie到用戶瀏覽器客戶端,而瀏覽器對該Cookie進行某種形式的存儲(內存或硬盤)寥袭。用戶再次訪問該網(wǎng)站時路捧,瀏覽器會發(fā)送該Cookie(Cookie未到期時)到服務器关霸,服務器對該憑據(jù)進行驗證,合法時使用戶不必輸入用戶名和密碼就可以直接登錄鬓长。
舉個例子描述一下這種需求谒拴。假設你經(jīng)常去的一家咖啡店有喝5杯咖啡免費贈一杯咖啡的優(yōu)惠,然而一次性消費5杯咖啡的機會微乎其微涉波,這時就需要某種方式來紀錄某位顧客的消費數(shù)量英上。想象一下其實也無外乎下面的幾種方案:
1、該店的店員很厲害啤覆,能記住每位顧客的消費數(shù)量苍日,只要顧客一走進咖啡店,店員就知道該怎么對待了窗声。
2相恃、發(fā)給顧客一張卡片,上面記錄著消費的數(shù)量笨觅,一般還有個有效期限拦耐。每次消費時,如果顧客出示這張卡片见剩,則此次消費就會與以前或以后的消費相聯(lián)系起來,這種做法就是在客戶端保持狀態(tài)杀糯。
3、發(fā)給顧客一張會員卡苍苞,除了卡號之外什么信息也不紀錄固翰,每次消費時,如果顧客出示該卡片羹呵,則店員在店里的紀錄本上找到這個卡號對應的紀錄添加一些消費信息,這種做法就是在服務器端保持狀態(tài)骂际。
由于HTTP協(xié)議是無狀態(tài)的,而出于種種考慮也不希望使之成為有狀態(tài)的冈欢,因此歉铝,后面兩種方案就成為現(xiàn)實的選擇。具體來說cookie機制采用的是在客戶端保持狀態(tài)的方案凑耻,而session機制采用的是在服務器端保持狀態(tài)的方案犯戏。同時我們也看到,由于采用服務器端保持狀態(tài)的方案在客戶端也需要保存一個標識拳话,所以session機制可能需要借助于cookie機制來達到保存標識的目的先匪,但實際上它還有其他選擇。
2弃衍、cookie的作用:
Cookie是解決HTTP無狀態(tài)性的有效手段呀非,服務器可以設置或讀取Cookie中所包含的信息。當用戶登錄后,服務器會發(fā)送包含登錄憑據(jù)的Cookie到用戶瀏覽器客戶端岸裙,而瀏覽器對該Cookie進行某種形式的存儲(內存或硬盤)猖败。用戶再次訪問該網(wǎng)站時,瀏覽器會發(fā)送該Cookie(Cookie未到期時)到服務器降允,服務器對該憑據(jù)進行驗證恩闻,合法時使用戶不必輸入用戶名和密碼就可以直接登錄。
本質上講剧董,Cookie是一段文本信息幢尚。客戶端請求服務器時翅楼,如果服務器需要記錄用戶狀態(tài)尉剩,就在響應用戶請求時發(fā)送一段Cookie信息∫汶客戶端瀏覽器保存該Cookie信息理茎,當用戶再次訪問該網(wǎng)站時,瀏覽器會把Cookie做為請求信息的一部分提交給服務器管嬉。服務器檢查Cookie內容皂林,以此來判斷用戶狀態(tài),服務器還會對Cookie信息進行維護蚯撩,必要時會對Cookie內容進行修改础倍。
3、cookie實現(xiàn)原理
Cookie定義了一些HTTP請求頭和HTTP響應頭求厕,通過這些HTTP頭信息使服務器可以與客戶進行狀態(tài)交互著隆。
客戶端請求服務器后扰楼,如果服務器需要記錄用戶狀態(tài)呀癣,服務器會在響應信息中包含一個Set-Cookie的響應頭,客戶端會根據(jù)這個響應頭存儲Cookie信息弦赖。再次請求服務器時项栏,客戶端會在請求信息中包含一個Cookie請求頭,而服務器會根據(jù)這個請求頭進行用戶身份蹬竖、狀態(tài)等較驗沼沈。
4、cookie的類型
Cookie總時由用戶客戶端進行保存的(一般是瀏覽器)币厕,按其存儲位置可分為:內存式Cookie(cookie是指在不設定它的生命周期expires時的狀態(tài))和硬盤式Cookie列另。
內存式Cookie存儲在內存中,瀏覽器關閉后就會消失旦装,由于其存儲時間較短页衙,因此也被稱為非持久Cookie或會話Cookie。
硬盤式Cookie保存在硬盤中,其不會隨瀏覽器的關閉而消失店乐,除非用戶手工清理或到了過期時間艰躺。由于硬盤式Cookie存儲時間是長期的,因此也被稱為持久Cookie眨八。
5腺兴、機制的區(qū)別:
一、cookie機制和session機制的區(qū)別
具體來說cookie機制采用的是在客戶端保持狀態(tài)的方案廉侧,而session機制采用的是在服務器端保持狀態(tài)的方案页响。
同時我們也看到,由于在服務器端保持狀態(tài)的方案在客戶端也需要保存一個標識伏穆,所以session機制可能需要借助于cookie機制來達到保存標識的目的拘泞,但實際上還有其他選擇。
二枕扫、會話cookie和持久cookie的區(qū)別
如果不設置過期時間陪腌,則表示這個cookie生命周期為瀏覽器會話期間,只要關閉瀏覽器窗口烟瞧,cookie就消失了诗鸭。這種生命期為瀏覽會話期的cookie被稱為會話cookie。會話cookie一般不保存在硬盤上而是保存在內存里参滴。
如果設置了過期時間强岸,瀏覽器就會把cookie保存到硬盤上,關閉后再次打開瀏覽器砾赔,這些cookie依然有效直到超過設定的過期時間蝌箍。
存儲在硬盤上的cookie可以在不同的瀏覽器進程間共享,比如兩個IE窗口暴心。而對于保存在內存的cookie妓盲,不同的瀏覽器有不同的處理方式。
三专普、理解cookie機制
cookie機制的基本原理就如上面的例子一樣簡單悯衬,但是還有幾個問題需要解決:“會員卡”如何分發(fā);“會員卡”的內容檀夹;以及客戶如何使用“會員卡”筋粗。
正統(tǒng)的cookie分發(fā)是通過擴展HTTP協(xié)議來實現(xiàn)的,服務器通過在HTTP的響應頭中加上一行特殊的指示以提示瀏覽器按照指示生成相應的cookie炸渡。然而純粹的客戶端腳本如JavaScript或者VBScript也可以生成cookie娜亿。
而cookie的使用是由瀏覽器按照一定的原則在后臺自動發(fā)送給服務器的。瀏覽器檢查所有存儲的cookie蚌堵,如果某個cookie所聲明的作用范圍大于等于將要請求的資源所在的位置买决,則把該cookie附在請求資源的HTTP請求頭上發(fā)送給服務器。意思是麥當勞的會員卡只能在麥當勞的店里出示,如果某家分店還發(fā)行了自己的會員卡策州,那么進這家店的時候除了要出示麥當勞的會員卡瘸味,還要出示這家店的會員卡。
cookie的內容主要包括:名字够挂,值旁仿,過期時間,路徑和域孽糖。
其中域可以指定某一個域比如.google.com枯冈,相當于總店招牌,比如寶潔公司办悟,也可以指定一個域下的具體某臺機器比如www.google.com或者froogle.google.com尘奏,可以用飄柔來做比。
路徑就是跟在域名后面的URL路徑病蛉,比如/或者/foo等等炫加,可以用某飄柔專柜做比。
路徑與域合在一起就構成了cookie的作用范圍铺然。
如果不設置過期時間俗孝,則表示這個cookie的生命期為瀏覽器會話期間,只要關閉瀏覽器窗口魄健,cookie就消失了赋铝。這種生命期為瀏覽器會話期的cookie被稱為會話cookie。會話cookie一般不存儲在硬盤上而是保存在內存里沽瘦,當然這種行為并不是規(guī)范規(guī)定的革骨。如果設置了過期時間,瀏覽器就會把cookie保存到硬盤上析恋,關閉后再次打開瀏覽器良哲,這些cookie仍然有效直到超過設定的過期時間。
存儲在硬盤上的cookie可以在不同的瀏覽器進程間共享绿满,比如兩個IE窗口臂外。而對于保存在內存里的cookie窟扑,不同的瀏覽器有不同的處理方式喇颁。對于IE,在一個打開的窗口上按Ctrl-N(或者從文件菜單)打開的窗口可以與原窗口共享嚎货,而使用其他方式新開的IE進程則不能共享已經(jīng)打開的窗口的內存cookie橘霎;對于Mozilla Firefox0.8,所有的進程和標簽頁都可以共享同樣的cookie殖属。一般來說是用javascript的window.open打開的窗口會與原窗口共享內存cookie姐叁。瀏覽器對于會話cookie的這種只認cookie不認人的處理方式經(jīng)常給采用session機制的web應用程序開發(fā)者造成很大的困擾。
下面就是一個goolge設置cookie的響應頭的例子
HTTP/1.1 302 Found
Location: http://www.google.com/intl/zh-CN/
Set-Cookie: PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:S=KaeaCFPo49RiA_d8; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com
Content-Type: text/html
瀏覽器在再次訪問goolge的資源時自動向外發(fā)送cookie
四、理解session的機制
session機制是一種服務器端的機制外潜,服務器使用一種類似于散列表的結構(也可能就是使用散列表)來保存信息原环。
當程序需要為某個客戶端的請求創(chuàng)建一個session的時候,服務器首先檢查這個客戶端的請求里是否已包含了一個session標識 - 稱為session id处窥,如果已包含一個session id則說明以前已經(jīng)為此客戶端創(chuàng)建過session嘱吗,服務器就按照session id把這個session檢索出來使用(如果檢索不到,可能會新建一個)滔驾,如果客戶端請求不包含session id谒麦,則為此客戶端創(chuàng)建一個session并且生成一個與此session相關聯(lián)的session id,session id的值應該是一個既不會重復哆致,又不容易被找到規(guī)律以仿造的字符串绕德,這個session id將被在本次響應中返回給客戶端保存。 保存這個session id的方式可以采用cookie摊阀,這樣在交互過程中瀏覽器可以自動的按照規(guī)則把這個標識發(fā)揮給服務器耻蛇。一般這個cookie的名字都是類似于SEEESIONID,而胞此。比如weblogic對于web應用程序生成的cookie城丧,JSESSIONID=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764,它的名字就是JSESSIONID豌鹤。
由于cookie可以被人為的禁止亡哄,必須有其他機制以便在cookie被禁止時仍然能夠把session id傳遞回服務器。經(jīng)常被使用的一種技術叫做URL重寫布疙,就是把session id直接附加在URL路徑的后面蚊惯,附加方式也有兩種,一種是作為URL路徑的附加信息灵临,表現(xiàn)形式為http://...../xxx;jsessionid=ByOK ... 99zWpBng!-145788764
另一種是作為查詢字符串附加在URL后面截型,表現(xiàn)形式為http://...../xxx?jsessionid=ByOK ... 99zWpBng!-145788764
這兩種方式對于用戶來說是沒有區(qū)別的,只是服務器在解析的時候處理的方式不同儒溉,采用第一種方式也有利于把session id的信息和正常程序參數(shù)區(qū)分開來宦焦。
為了在整個交互過程中始終保持狀態(tài),就必須在每個客戶端可能請求的路徑后面都包含這個session id顿涣。
另一種技術叫做表單隱藏字段波闹。就是服務器會自動修改表單,添加一個隱藏字段涛碑,以便在表單提交時能夠把session id傳遞回服務器精堕。這種技術現(xiàn)在已較少應用,筆者接觸過的很古老的iPlanet6(SunONE應用服務器的前身)就使用了這種技術蒲障。
實際上這種技術可以簡單的用對action應用URL重寫來代替歹篓。
在談論session機制的時候瘫证,常常聽到這樣一種誤解“只要關閉瀏覽器,session就消失了”庄撮。其實可以想象一下會員卡的例子背捌,除非顧客主動對店家提出銷卡,否則店家絕對不會輕易刪除顧客的資料洞斯。對session來說也是一樣的载萌,除非程序通知服務器刪除一個session,否則服務器會一直保留巡扇,程序一般都是在用戶做log off的時候發(fā)個指令去刪除session扭仁。然而瀏覽器從來不會主動在關閉之前通知服務器它將要關閉,因此服務器根本不會有機會知道瀏覽器已經(jīng)關閉厅翔,之所以會有這種錯覺谣妻,是大部分session機制都使用會話cookie來保存session id棚壁,而關閉瀏覽器后這個session id就消失了,再次連接服務器時也就無法找到原來的session。如果服務器設置的cookie被保存到硬盤上拓萌,或者使用某種手段改寫瀏覽器發(fā)出的HTTP請求頭耍休,把原來的session id發(fā)送給服務器蛤迎,則再次打開瀏覽器仍然能夠找到原來的session舔清。
恰恰是由于關閉瀏覽器不會導致session被刪除,迫使服務器為seesion設置了一個失效時間施蜜,當距離客戶端上一次使用session的時間超過這個失效時間時卒蘸,服務器就可以認為客戶端已經(jīng)停止了活動,才會把session刪除以節(jié)省存儲空間翻默。
iOS中的Cookie
當你訪問一個網(wǎng)站時缸沃,NSURLRequest都會幫你主動記錄下來你訪問的站點設置的Cookie,如果 Cookie 存在的話修械,會把這些信息放在 NSHTTPCookieStorage 容器中共享趾牧,當你下次再訪問這個站點時,NSURLRequest會拿著上次保存下來了的Cookie繼續(xù)去請求肯污。Cookie是由服務器端生成翘单,發(fā)送給User-Agent(一般是瀏覽器或者客戶端),瀏覽器會將Cookie的key/value保存到某個目錄下的文本文件內,下次請求同一網(wǎng)站地址時就發(fā)送該Cookie給服務器.Cookie必然會通過HTTP的Respone傳過來蹦渣,并且Cookie在Respone中的HTTP header中哄芜。
1、NSHTTPCookie
NSHTTPCookie對象代表一個HTTP cookie剂桥。 這是一個不可改變的對象忠烛,從一個包含cookie的屬性的字典初始化,這個類可以用來手動創(chuàng)建cookie的Properties 属提。
//下面兩個方法用于對象的創(chuàng)建和初始化 都是通過字典進行鍵值設置
- (nullable instancetype)initWithProperties:(NSDictionary<NSString *, id> *)properties;
+ (nullable NSHTTPCookie *)cookieWithProperties:(NSDictionary<NSString *, id> *)properties;
//返回Cookie數(shù)據(jù)中可用于添加HTTP頭字段的字典
+ (NSDictionary<NSString *, NSString *> *)requestHeaderFieldsWithCookies:(NSArray<NSHTTPCookie *> *)cookies;
//從指定的響應頭和URL地址中解析出Cookie數(shù)據(jù)
+ (NSArray<NSHTTPCookie *> *)cookiesWithResponseHeaderFields:(NSDictionary<NSString *, NSString *> *)headerFields forURL:(NSURL *)URL;
//Cookie數(shù)據(jù)中的屬性字典
@property (nullable, readonly, copy) NSDictionary<NSString *, id> *properties;
//請求響應的版本
@property (readonly) NSUInteger version;
//請求相應的名稱
@property (readonly, copy) NSString *name;
//請求相應的值
@property (readonly, copy) NSString *value;
//過期時間
@property (nullable, readonly, copy) NSDate *expiresDate;
//請求的域名
@property (readonly, copy) NSString *domain;
//請求的路徑
@property (readonly, copy) NSString *path;
//是否是安全傳輸
@property (readonly, getter=isSecure) BOOL secure;
//是否只發(fā)送HTTP的服務
@property (readonly, getter=isHTTPOnly) BOOL HTTPOnly;
//響應的文檔
@property (nullable, readonly, copy) NSString *comment;
//相應的文檔URL
@property (nullable, readonly, copy) NSURL *commentURL;
//服務端口列表
@property (nullable, readonly, copy) NSArray<NSNumber *> *portList;
2权逗、NSHTTPCookieStorage
NSHTTPCookieStorage提供了管理所有NSHTTPCookie對象的接口,在OS X里cookie是在所有程序中共享的美尸,而在iOS中,cookie只當當前應用中有效。NSHTTPCookieStorage類采用單例的設計模式斟薇,其中管理著所有HTTP請求的Cookie信息,更改cookie的接受政策將會影響當前所有正在使用cookie的app师坎。
//獲取單例對象
+ (NSHTTPCookieStorage *)sharedHTTPCookieStorage;
//所有Cookie數(shù)據(jù)數(shù)組 其中存放NSHTTPCookie對象
@property (nullable , readonly, copy) NSArray<NSHTTPCookie *> *cookies;
//手動設置一條Cookie數(shù)據(jù)
- (void)setCookie:(NSHTTPCookie *)cookie;
//刪除某條Cookie信息
- (void)deleteCookie:(NSHTTPCookie *)cookie;
//獲取某個特定URL的所有Cookie數(shù)據(jù)
- (nullable NSArray<NSHTTPCookie *> *)cookiesForURL:(NSURL *)URL;
//刪除某個時間后的所有Cookie信息 iOS8后可用
- (void)removeCookiesSinceDate:(NSDate *)date NS_AVAILABLE(10_10, 8_0);
//為某個特定的URL設置Cookie
- (void)setCookies:(NSArray<NSHTTPCookie *> *)cookies forURL:(nullable NSURL *)URL mainDocumentURL:(nullable NSURL *)mainDocumentURL
/*
枚舉如下:
typedef NS_ENUM(NSUInteger, NSHTTPCookieAcceptPolicy) {
NSHTTPCookieAcceptPolicyAlways,//接收所有Cookie信息
NSHTTPCookieAcceptPolicyNever,//不接收所有Cookie信息
NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain//只接收主文檔域的Cookie信息
};
*/
@property NSHTTPCookieAcceptPolicy cookieAcceptPolicy;//Cookie數(shù)據(jù)的接收協(xié)議
**系統(tǒng)下面的兩個通知與Cookie管理有關**
//Cookie數(shù)據(jù)的接收協(xié)議改變時發(fā)送的通知
FOUNDATION_EXPORT NSString * const NSHTTPCookieManagerAcceptPolicyChangedNotification;
//管理的Cookie數(shù)據(jù)發(fā)生變化時發(fā)送的通知
FOUNDATION_EXPORT NSString * const NSHTTPCookieManagerCookiesChangedNotification;
**存放和獲取一個task任務所對應的cookie,iOS8.0以后支持**
- (void)storeCookies:(NSArray<NSHTTPCookie *> *)cookies forTask:(NSURLSessionTask *)task NS_AVAILABLE(10_10, 8_0);
- (void)getCookiesForTask:(NSURLSessionTask *)task completionHandler:(void (^) (NSArray<NSHTTPCookie *> * _Nullable cookies))completionHandler NS_AVAILABLE(10_10, 8_0);
設置cookie(手動創(chuàng)建的cookie不會儲存到本地)
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
[cookieProperties setObject:@"username" forKey:NSHTTPCookieName];
[cookieProperties setObject:@"my ios cookie" forKey:NSHTTPCookieValue];
[cookieProperties setObject:@"dev.skyfox.org" forKey:NSHTTPCookieDomain];
[cookieProperties setObject:@"dev.skyfox.org" forKey:NSHTTPCookieOriginURL];
[cookieProperties setObject:@"/" forKey:NSHTTPCookiePath];
[cookieProperties setObject:@"0" forKey:NSHTTPCookieVersion];
[cookieProperties setObject:[NSDate dateWithTimeIntervalSinceNow:5] forKey:NSHTTPCookieExpires];//設置失效時間
[cookieProperties setObject:@"0" forKey:NSHTTPCookieDiscard]; //設置sessionOnly
NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieProperties];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
清除Cookie
清除所有的cookie 方法:
NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"];
if (url) {
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:url];
for (int i = 0; i < [cookies count]; i++) {
NSHTTPCookie *cookie = (NSHTTPCookie *)[cookies objectAtIndex:i];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
}
[[NSURLCache sharedURLCache] removeAllCachedResponses];
清除某一個特定的cookie方法:
NSArray * cookArray = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[NSURL URLWithString:@"指定URL"]];
for (NSHTTPCookie*cookie in cookArray) {
if ([cookie.name isEqualToString:@"cookiename"]) {
[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
}
}
清除某一個url緩存的方法:
[[NSURLCache sharedURLCache] removeCachedResponseForRequest:[NSURLRequest requestWithURL:url]];
NSArray * cookArray = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[NSURL URLWithString:@"指定URL"]];
for (NSHTTPCookie*cookie in cookArray) {
[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
}
參考鏈接:
http://blog.csdn.NET/jzhf2012/article/details/8496502
http://blog.csdn.net/u014753892/article/details/52821268