iOS Cookie 概述
Cookie 提供服務(wù)器存儲(chǔ)相關(guān)數(shù)據(jù)到客戶端的一種解決方案, 服務(wù)器通過Response中的Http-Header的Set-Cookie來將對(duì)應(yīng)的Cookie持久化到本地的問題見, 當(dāng)APP下次請(qǐng)求該域名的時(shí)候, 會(huì)將之前緩存的Cookies信息, 一起發(fā)送給服務(wù)端, 服務(wù)端可以根據(jù)Cookie信息來做下一步處理. 可以將Cookie當(dāng)做服務(wù)器分辨某個(gè)終端設(shè)備的憑證, 一般iOS中可以用來判斷該設(shè)備是否可以自動(dòng)登錄, 是否有登錄態(tài).
我們碰到的Cookie可以分成以下兩類:
- Session Cookie -- 會(huì)話Cookie是臨時(shí)Cookie, 在一次會(huì)話中有效, 當(dāng)APP退出以后,該Cookie會(huì)自動(dòng)失效, 被系統(tǒng)刪除.
- 持久化Cookie -- 持久化Cookie會(huì)將Cookie存儲(chǔ)在APP的沙盒中,在APP退出以后,沙盒中的Cookie依然存在, 不會(huì)被刪除.
iOS系統(tǒng)中的URL Loading System和WebKit.framework
我們知道, 在iOS中每個(gè)單獨(dú)app都一個(gè)一個(gè)獨(dú)立的數(shù)據(jù)區(qū)域, 不同的app數(shù)據(jù)區(qū)域互相不會(huì)影響.
Apple官方文檔中有以下一段:
The URL loading system automatically sends any stored cookies appropriate for an NSURLRequest object unless the request specifies not to send cookies. Likewise, cookies returned in an NSURLResponse object are accepted in accordance with the current cookie acceptance policy.
我們知道iOS中有URL Loading System, 不論我們使用的是UIWebView, 還是系統(tǒng)原生的NSURLSession或者第三方庫AFNetworking, 他們都是在這個(gè)URL Loading System之上的, 因此我們可以通過NSURLProtocol去自定義我們的網(wǎng)絡(luò)操作. 而上面一段文字,我們理解一下, URL Loading System會(huì)幫我們做下面兩個(gè)事情:
-
NSURLRequest在請(qǐng)求發(fā)送的時(shí)候, 會(huì)自動(dòng)帶上已經(jīng)存儲(chǔ)在Cookie Storage中的cookies, 除非我們?cè)O(shè)置了NSURLRequest的HTTPShouldHandleCookies屬性為不處理Cookie.
-
-
NSURLResponse會(huì)根據(jù)當(dāng)前APP中的NSHTTPCookieStorage的cookieAcceptPolicy, 選擇是否將響應(yīng)頭中的Cookie存儲(chǔ)在Cookie Storage中.
-
在iOS8以后, 蘋果又推出了WKWebView, 它是在WebKit.framework中的一套新的體系, 它不依賴URL Loading System, 因此需要區(qū)分一下不同網(wǎng)絡(luò)基礎(chǔ)體系對(duì)Cookie產(chǎn)生的影響:
-
NSURLSession/NSURLRequest/NSURLConnection-- 在一個(gè)app內(nèi), 共享Shared Cookie Storage(底層會(huì)走URL Loading System). - UIWebView -- 在一個(gè)app內(nèi), 每個(gè)UIWebView實(shí)例會(huì)繼承app中的 ``Shared Cookie Storage
. (實(shí)際也可以規(guī)為第一條, 因?yàn)槲覀兪褂肬IWebView時(shí), 底層會(huì)創(chuàng)建NSURLRequest, 會(huì)走URL Loading System`) - WKWebview -- 每個(gè)WKWebView的實(shí)例對(duì)象都有一個(gè)與它自己關(guān)聯(lián)的 cookie storage!!!, 我們可以參考
WKHTTPCookieStore.(與APP 的Shared Cookie Storage互相獨(dú)立!!!!) - 特殊情況:
App group-- 通過sharedCookieStorageForGroupContainerIdentifier用這個(gè)方法可以獲取一個(gè)app group相關(guān)的cookies存儲(chǔ)區(qū). 方便我們用同一個(gè)證書發(fā)布的app共享CookieStorage
NSURLCache和NSHTTPCookieStroage無法操作(WKWebView)WebCore進(jìn)程的緩存和Cookie。WKWebView實(shí)例不會(huì)把Cookie存入到App標(biāo)準(zhǔn)的的Cookie容器(NSHTTPCookieStorage)中,因?yàn)?NSURLSession/NSURLConnection等網(wǎng)絡(luò)請(qǐng)求使用NSHTTPCookieStorage進(jìn)行訪問Cookie,所以不能訪問WKWebView的Cookie,現(xiàn)象就是WKWebView存了Cookie,其他的網(wǎng)絡(luò)類如NSURLSession/NSURLConnection卻看不到。
iOS中的Cookie相關(guān)的類
iOS 中使用了NSHTTPCookie, NSHTTPCookieStorage兩個(gè)類來處理Cookie相關(guān)的內(nèi)容.
1. NSHTTPCookie
每一個(gè)被存儲(chǔ)的Cookie都可以用 NSHTTPCookie的實(shí)例來表示.Cookie中有以下重要的屬性:
- NSHTTPCookieValue
- NSHTTPCookieName
- NSHTTPCookiePath
- NSHTTPCookieDomain
- NSHTTPCookieVersion
- NSHTTPCookieExpires
- NSHTTPCookieDiscard -- session-Only
我們可以用以下方式創(chuàng)建Cookie, 或者用其他的方式, 請(qǐng)參考官方文檔:
NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
[cookieProperties setObject:@"username" forKey:NSHTTPCookieName]; // Cookie 名稱
[cookieProperties setObject:@"my ios cookie" forKey:NSHTTPCookieValue]; // Cookie 值
[cookieProperties setObject:@".webank.com" forKey:NSHTTPCookieDomain]; // Cookie domain
[cookieProperties setObject:@".webank.com" forKey:NSHTTPCookieOriginURL]; // Origin URL
[cookieProperties setObject:@"/" forKey:NSHTTPCookiePath]; // Cookie Path 路徑使用
[cookieProperties setObject:@"0" forKey:NSHTTPCookieVersion]; // Cookie 的版本號(hào), 目前只有 0 . 1 兩個(gè)版本
[cookieProperties setObject:[NSDate dateWithTimeIntervalSinceNow:5] forKey:NSHTTPCookieExpires];//設(shè)置失效時(shí)間 這里設(shè)置的是5s
// String value must be either "TRUE" or "FALSE". This cookie attribute is optional. The default is "FALSE", unless this cookie is version 1 or greater and a value for NSHTTPCookieMaximumAge is not specified, in which case it is assumed to be "TRUE".
[cookieProperties setObject:@"0" forKey:NSHTTPCookieDiscard]; //設(shè)置sessionOnly, 表示這個(gè) Cookie 是否只支持 一次session, 如果session結(jié)束, 那么cookie 失效
NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieProperties];
// **手動(dòng)** 將數(shù)據(jù)請(qǐng)求存儲(chǔ)到Cookie Storage中
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
2. NSHTTPCookieStorage
它是一個(gè)單例設(shè)計(jì), 用來管理Cookie存儲(chǔ)的單例. NSHTTPCookieStorage單例中的cookieAcceptPolicy屬性也能夠修改系統(tǒng)對(duì)Cookie的處理策略, 有以下幾種模式:
- NSHTTPCookieAcceptPolicyAlways --- Accept all cookies.
- NSHTTPCookieAcceptPolicyNever --- Reject all cookies.
- NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain --- Accept cookies only from the main document domain.
除了系統(tǒng)的Cookie Storage自動(dòng)處理Cookie以外, 我們也可以手動(dòng)通過NSHTTPCookieStorage的setCookie,deleteCookie等方法來增刪改查Cookie Storage中的內(nèi)容.
實(shí)際使用中發(fā)現(xiàn), iOS的Cookie Storage存在者一些問題, React Native持久化sessions #9中就提到, 系統(tǒng)自動(dòng)幫我們處理Cookie是不靠譜的cookie存儲(chǔ), 因此在實(shí)際開發(fā)中我們盡量自己主動(dòng)去處理Cookie(在NSURLRequest中手動(dòng)寫入Cookie, 在收到NSHTTPResponse響應(yīng)時(shí), 手動(dòng)持久化收到的Cookie).
也可以參考作者的另外一篇文章, 里面簡(jiǎn)述了Cookie Storage Policy可能導(dǎo)致的bug -- iOS APP中后臺(tái)無法種植Cookie?
iOS 使用Cookie中注意問題
- 在請(qǐng)求時(shí),NSURLSession 和 NSURLConnection 會(huì)自動(dòng)幫我們管理 cookie 的,但并不完善。AFNetworking 默認(rèn)設(shè)置了 NSURLRequest 的 HTTPShouldHandleCookies 屬性為 YES。
- 如果服務(wù)器設(shè)置Cookie的失效時(shí)
expiresDate, 并且sessionOnly為False, Cookie就是持久化Cookie,會(huì)被持久化到沙盒中, App在下次啟動(dòng)時(shí), 會(huì)自動(dòng)將沙盒中的Cookie加載到內(nèi)存中.如果sessionOnly被設(shè)置成TRUE或者,expiresDate為空, 則不會(huì)被持久化到沙盒. - 不要簡(jiǎn)單的依賴
NSHTTPCookieStorage的setCookie:方法來做 cookie 的存儲(chǔ),因?yàn)樵趫?zhí)行setCookie:時(shí), cookie 并不是馬上就更新.NSHTTPCookieStorage state not saved on app exit. Any definitive knowledge/documentation out there? - cookie的
httpOnly屬性是用來設(shè)置cookie是否能通過 js 去訪問。默認(rèn)情況下,cookie不會(huì)帶httpOnly選項(xiàng)(即為空),所以默認(rèn)情況下,客戶端是可以通過 js 代碼去訪問(包括讀取、修改、刪除等)這個(gè)cookie的。當(dāng)cookie帶httpOnly選項(xiàng)時(shí),客戶端則無法通過js代碼去訪問(包括讀取、修改、刪除等)這個(gè)cookie. - 當(dāng)cookie的 name/domain/path 這3個(gè)字段都相同的時(shí)候會(huì)被覆蓋.
- 關(guān)于Domain的說明: 1. 如果顯式設(shè)置了 domain,則設(shè)置成什么,瀏覽器就存成什么;但如果沒有顯式設(shè)置,則瀏覽器會(huì)自動(dòng)取 url 的 host 作為 domain 值. 2. 前面帶點(diǎn)‘.’和不帶點(diǎn)‘.’有啥區(qū)別: 帶點(diǎn):任何subdomain都可以訪問,包括父domain, 不帶點(diǎn):只有完全一樣的域名才能訪問,subdomain 不能.
參考文件
聊一聊 cookie
關(guān)于ios的Cookie那些事
How can I retrieve a file using WKWebView?
React Native持久化sessions #9
如何處理 iOS 原生網(wǎng)絡(luò)請(qǐng)求中的 cookie ?