時間:2016.12.26
背景:在 iOS 開發(fā)中硝皂,大部分應(yīng)用的用戶登錄機制都是基于 token 令牌的,我之前做過的項目也都是如此作谭,但是在我現(xiàn)在的新項目中稽物,由于服務(wù)端同學是沿用他們以前的 cookie 登錄機制,所以我也不得不換種方式來對(zhe)待(teng)登(yi)錄(xia)問題了折欠。
1.什么是 cookie贝或?cookie 和 token 有何區(qū)別?
cookie 是什么呢锐秦?cookie 在英語中通常是指餅干咪奖,當然,我這里指的不是农猬,而是 HTTP 網(wǎng)絡(luò)請求中用來記錄用戶信息的一種數(shù)據(jù)形式或者說一種機制。
cookie:在客戶端發(fā)送登錄操作的網(wǎng)絡(luò)請求時售淡,服務(wù)器在登陸成功返回的 response header 中會添加一個 set-cookie 的值斤葱,作為用戶的身份認證,如果是瀏覽器的話揖闸,后面每一次發(fā)請求時揍堕,瀏覽器都會自動將之前獲取到的 cookie 值插入到 request header 的 cookie 字段中,而且 cookie 本身包括多個屬性汤纸,比如有效期 expires衩茸、域 domain等,因此采用 cookie 的登錄機制需要考慮到對 cookie 本身的管理贮泞。cookie 主要是在 web 領(lǐng)域使用楞慈。
token:相比 cookie幔烛,token 令牌的登錄機制要更輕,直觀的感受是囊蓝,登錄認證成功后饿悬,服務(wù)器返回 token 值,然后在請求的 url 中拼接一段 “token=%^&%#&%#&” 就完事了聚霜,至于什么跨域狡恬、安全策略什么的,根本沒他什么事蝎宇,客戶端管理 token 也非常簡單弟劲,只要看好這個字符串就行了,所以 token 一般在移動端用的比較多姥芥。當然兔乞,移動應(yīng)用中的 web view 還是要處理 cookie 的。
2.iOS 中的網(wǎng)絡(luò)請求中如何處理 cookie撇眯?
在開始處理 cookie 時报嵌,需要了解兩個類,NSHTTPCookie 和 NSHTTPCookieStorage熊榛,在用的時候要注意幾點:
- 在請求時锚国,NSURLSession 和 NSURLConnection 會自動幫我們管理 cookie 的,但并不完善玄坦。AFNetworking 默認設(shè)置了 NSURLRequest 的 HTTPShouldHandleCookies 屬性為 YES血筑。
- 如果服務(wù)器設(shè)置了 Cookie 失效時間 expiresDate,并且 sessionOnly 為 FALSE煎楣,Cookie 就會被持久化到文件中豺总,下次啟動app會自動加載沙盒中的 Cookies。如果 sessionOnly 為 TRUE 或者 expiresDate 為空择懂,則不會自動持久化到沙盒喻喳。
- 手動設(shè)置的 Cookie 不會被 NSHTTPCookieStorage 自動持久化到沙盒。
- 不能簡單地依賴 NSHTTPCookieStorage 的 setCookie: 方法來做 cookie 的存儲困曙,因為在執(zhí)行 setCookie:時表伦, cookie 并不是馬上就更新了。參考: NSHTTPCookieStorage state not saved on app exit. Any definitive knowledge/documentation out there?
- cookie 的 httpOnly 屬性是用來設(shè)置 cookie 是否能通過 js 去訪問慷丽。默認情況下蹦哼,cookie不會帶httpOnly選項(即為空),所以默認情況下要糊,客戶端是可以通過 js 代碼去訪問(包括讀取纲熏、修改、刪除等)這個cookie的。當cookie帶 httpOnly 選項時局劲,客戶端則無法通過js代碼去訪問(包括讀取勺拣、修改、刪除等)這個cookie容握。參考:聊一聊 cookie宣脉。
下面切入正題吧,我是如何做的呢剔氏?
首先是登錄塑猖。登錄成功后,服務(wù)器在 HTTP response header 中的 set-cookie 字段中返回了 cookie 的值谈跛,我們可已通過多種方式獲取到我們想要的 cookie 值羊苟,我是采用了下面這種方式來讀取的,因為我們的服務(wù)器沒有設(shè)置 expireDate感憾,所以我就自己做持久化存儲了蜡励。
NSDictionary *headerFields = [(NSHTTPURLResponse *)response allHeaderFields];
NSArray <NSHTTPCookie *> *cookies =[NSHTTPCookieStorage sharedHTTPCookieStorage].cookies; // 只要服務(wù)器在請求返回時帶了 cookie,NSHTTPCookieStorage 就會自動幫我們管理 cookie
DDLogDebug(@"\n shared cookies %@\n", cookies);
for (NSHTTPCookie *aCookie in cookies) {
if ([aCookie.name isEqualToString:@"BUA"]) { // 獲取并保存用戶cookie
[[NSUserDefaults standardUserDefaults] setObject:cookie.properties forKey:kYIDUserDefaultUserCookieKey]; // 自己做持久化存儲
break;
}
}
然后是請求時添加 cookie 到 request header阻桅。實際上這一步系統(tǒng)(NSURLSession / NSURLConnection)已經(jīng)自動幫我們處理了凉倚,具體細節(jié)我也不太清楚。
還要考慮重啟應(yīng)用后的操作嫂沉,由于我們的服務(wù)器沒有設(shè)置 expireDate 以及上面提到的其他原因稽寒,在程序重啟時,NSHTTPCookieStorage 并不會保存上一次使用應(yīng)用時的 cookie趟章,所以我們需要在程序啟動時讀取自己保存的 cookie杏糙,同時更新 NSHTTPCookieStorage 的 cookie。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { {
NSDictionary *cookieProperties = [[NSUserDefaults standardUserDefaults] objectForKey:kYIDUserDefaultUserCookieKey];
NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieProperties];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
...
}
關(guān)于 cookie 的有效期處理蚓土,在使用 cookie 時需要自己判斷 cookie 是否過期宏侍,NSHTTPCookieStorage 是不會自動幫我們處理的,更何況我們自己還做了本地存儲蜀漆,所以我們在用到 cookie 時還需要檢查 cookie 是否過期谅河,如果過期了,就要廢棄掉失效的 cookie确丢。我是在用戶的登錄狀態(tài)方法中做的處理:
- (BOOL)isLoggedIn {
if (![self.cookie yid_isNotExpired]) { // cookie 失效绷耍,自動退出登錄
[self logout]; // 刪除用戶信息、cookie
}
return [self.cookie yid_isNotEmpty];
}
最后還要記得在退出登錄時也要刪除 cookie:
[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:[self userCookie]];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:kYIDUserDefaultUserCookieKey];
3.iOS 中的原生網(wǎng)絡(luò)請求如何與 webView 實現(xiàn) cookie 共享蠕嫁?
UIWebView 是屬于 URL Loading System 的一部分锨天,所以系統(tǒng)會自動幫我們將 NSHTTPCookieStorage 中的 cookie 同步到 UIWebView 中去毯盈。
由于 WKWebView 是獨立于 URL Loading System 之外的剃毒,所以 WKWebView 所有的 cookie 管理都需要開發(fā)者自己操作,具體方法可以參考 stackoverflow 上的解決方案:Can I set the cookies to be used by a WKWebView?,也有國內(nèi)開發(fā)者根據(jù)這個答案造了一個輪子 haifengkao/YWebView赘阀。
遺留問題:
1.服務(wù)器是在什么時候更新/生成cookie 益缠?
2.登陸成功后,系統(tǒng)是如何自動添加 cookie 到 request header 中去的基公?
3.服務(wù)器是怎么識別客戶端的 cookie 的幅慌?
參考資料:
- Wikipedia - HTTP cookie:
https://en.wikipedia.org/wiki/HTTP_cookie- 聊一聊 cookie:
https://segmentfault.com/a/1190000004556040- NSHTTPCookieStorage 官方文檔:
https://developer.apple.com/reference/foundation/nshttpcookiestorage
中文版:http://www.reibang.com/p/b10652a1803e- iOS平臺下cookie的使用:
http://www.reibang.com/p/65094611980c- iOS HTTP網(wǎng)絡(luò)請求Cookie的讀取與寫入(NSHTTPCookieStorage)
http://www.skyfox.org/ios-url-request-cookie.html
- NSHTTPCookieStorage state not saved on app exit. Any definitive knowledge/documentation out there?
http://stackoverflow.com/questions/5837702/nshttpcookiestorage-state-not-saved-on-app-exit-any-definitive-knowledge-docume - app開發(fā)token、cookie的區(qū)別轰豆,賬號密碼加密又是如何保證安全胰伍?
https://www.zhihu.com/question/39137156 - 為什么 APP 要用 token 而不用 session 認證?
https://www.v2ex.com/t/148426 - How to manage sessions with AFNetworking?
http://stackoverflow.com/questions/10984374/how-to-manage-sessions-with-afnetworking/11039784#11039784 - Persisting Cookies In An iOS Application?
http://stackoverflow.com/questions/4597763/persisting-cookies-in-an-ios-application - NSHTTPCookieStorage and Cookie Expiration Date
http://stackoverflow.com/questions/7203641/nshttpcookiestorage-and-cookie-expiration-date/7209706#7209706 - Can I set the cookies to be used by a WKWebView?http://stackoverflow.com/questions/26573137/can-i-set-the-cookies-to-be-used-by-a-wkwebview
- haifengkao/YWebView:https://github.com/haifengkao/YWebView