網(wǎng)絡(luò)編程03 -- cookie的使用

客戶端登錄接口中的Cookie

  • 由于HTTP協(xié)議是無狀態(tài)的惕耕,客戶端需借助cookie來實現(xiàn)跨URL的請求;
  • 實現(xiàn)原理為:客戶端登錄服務(wù)端,服務(wù)端下發(fā)cookie數(shù)據(jù)給客戶端肋坚,客戶端的NSURLResponse根據(jù)會當前的NSHTTPCookieStorage接受策略自動接收服務(wù)端返回的cookie并存儲在NSHTTPCookieStorage容器中飞蹂,我們不需要做任何操作几苍,當我們發(fā)送其他網(wǎng)絡(luò)請求(NSURLRequest)時,我們只需要設(shè)置NSURLRequestHTTPShouldHandleCookies屬性為YES(默認為YES)陈哑,NSURLRequest會自動附帶cookie的信息發(fā)送給服務(wù)器妻坝;
  • NSHTTPCookieStorage可設(shè)置cookieAcceptPolicy屬性,定義其接收cookie的策略芥颈,總共有三種接收策略惠勒,如下所示:
typedef NS_ENUM(NSUInteger, NSHTTPCookieAcceptPolicy) {
    NSHTTPCookieAcceptPolicyAlways,
    NSHTTPCookieAcceptPolicyNever,
    NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain
};
  • NSHTTPCookieAcceptPolicyAlways:永遠接收Cookie,這種情況下爬坑,NSHTTPCookieStorage會將接收到的cookie 存儲在偏好設(shè)置中纠屋;
  • NSHTTPCookieAcceptPolicyNever:永遠不接受Cookie,這種情況下盾计,NSHTTPCookieStorage不會存儲cookie到本地售担;
  • NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain:只接收指定域名的Cookie赁遗;
  • NSHTTPCookieStorage的常見Api操作如下所示:
// cookie的接收策略
@property NSHTTPCookieAcceptPolicy cookieAcceptPolicy

// 獲取NSHTTPCookieStorage存儲的所有cookie
@property (nullable , readonly, copy) NSArray<NSHTTPCookie *> *cookies

// 設(shè)置cookie
- (void)setCookie:(NSHTTPCookie *)cookie

// 刪除cookie
- (void)deleteCookie:(NSHTTPCookie *)cookie

// 在某個時間點刪除cookies
- (void)removeCookiesSinceDate:(NSDate *)date

// 獲取指定URL的cookies
- (nullable NSArray<NSHTTPCookie *> *)cookiesForURL:(NSURL *)URL

// 獲取指定域名指定URL的cookies
- (void)setCookies:(NSArray<NSHTTPCookie *> *)cookies forURL:(nullable NSURL *)URL mainDocumentURL:(nullable NSURL *)mainDocumentURL
  • NSHTTPCookieStorage容器中存儲的cookie數(shù)據(jù)是NSHTTPCookie實例對象,涉及的Api如下所示:
// 下面兩個方法用于對象的創(chuàng)建和初始化 都是通過字典進行鍵值設(shè)置
- (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;

// 從指定的響應(yīng)頭和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;

// 請求響應(yīng)的版本
@property (readonly) NSUInteger version;

// 請求相應(yīng)的名稱
@property (readonly, copy) NSString *name;

// 請求相應(yīng)的值
@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的服務(wù)
@property (readonly, getter=isHTTPOnly) BOOL HTTPOnly;

// 響應(yīng)的文檔
@property (nullable, readonly, copy) NSString *comment;

// 相應(yīng)的文檔URL
@property (nullable, readonly, copy) NSURL *commentURL;

// 服務(wù)端口列表
@property (nullable, readonly, copy) NSArray<NSNumber *> *portList;
  • 在客戶端當用戶推出登錄時族铆,我們需要清除NSHTTPCookieStorage容器中存儲的關(guān)于用戶的所有cookie數(shù)據(jù)岩四;
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
for (NSHTTPCookie *cookie in cookies) {
   [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
}

UIWebView中Cookie機制

  • UIWebView在加載HTML網(wǎng)頁時,會自動將網(wǎng)頁中cookie數(shù)據(jù)存儲到NSHTTPCookieStorage容器中哥攘,然后再加載其他的URL網(wǎng)頁請求時剖煌,請求會自動攜帶NSHTTPCookieStorage容器中的cookie數(shù)據(jù),基本原理與上述的客戶端登錄接口中的Cookie的原理是一致的逝淹;
  • 在同一個App內(nèi)耕姊,多個UIWebView之間共享cookie數(shù)據(jù);
UIWebView中cookie的獲取與持久化
- (void)webViewDidFinishLoad:(UIWebView *)webView {
    NSString *requestUrl = webView.request.URL.absoluteString;
    NSLog(@" requestUrl: %@",requestUrl);
    
    NSMutableArray *cookieArray = [[NSMutableArray alloc] init];
    ///網(wǎng)頁加載完成取出cookies
    NSArray *nCookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
    for (NSHTTPCookie *cookie in nCookies) {
        ///設(shè)置原始 cookie
        NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
        [cookieProperties setObject:cookie.name forKey:NSHTTPCookieName];
        [cookieProperties setObject:cookie.value forKey:NSHTTPCookieValue];
        [cookieProperties setObject:cookie.domain forKey:NSHTTPCookieDomain];
        [cookieProperties setObject:cookie.path forKey:NSHTTPCookiePath];
        [cookieProperties setObject:[[NSDate date] dateByAddingTimeInterval:2629743] forKey:NSHTTPCookieExpires];
        [cookieArray addObject:cookieProperties];
        
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
    }
   ///cookie 持久化 存入本地
   [[NSUserDefaults standardUserDefaults] setObject:cookieArray forKey:@"cookieArray"];
   [[NSUserDefaults standardUserDefaults] synchronize];
}
WKWebView的Cookie機制
  • 在iOS11之前栅葡, WKWebView 加載HTML發(fā)起的URL請求不會自動帶上存儲于 NSHTTPCookieStorage 容器中的 Cookie茉兰,出現(xiàn)cookie丟失的問題;
  • 在iOS11之后欣簇,iOS提供了WKHTTPCookieStore類规脸,其類似于NSHTTPCookieStorage,是一個存儲cookie數(shù)據(jù)的容器熊咽,在發(fā)起URL請求時莫鸭,會自動保存網(wǎng)頁中的cookie,再加載其他的URL網(wǎng)頁請求网棍,會自動攜帶上cookie數(shù)據(jù)黔龟,但第一次發(fā)起URL請求時,依然存在cookie丟失的問題滥玷;
第一個問題:在 iOS11之前WKWebView第一次加載HTML時氏身,cookie丟失
  • 在iOS11之前,當WKWebView第一次加載HTML時惑畴,由于不會讀取NSHTTPCookieStorage中的cookie蛋欣,導(dǎo)致cookie丟失,需要我們將NSHTTPCookieStorage容器中cookie同步到WKWebView中如贷,實現(xiàn)第一次加載HTML的URL請求中攜帶上cookie數(shù)據(jù)陷虎;
  • 解決方案一:將NSHTTPCookieStorage容器中的cookie同步到URL請求頭參數(shù)中,代碼實現(xiàn)如下:
- (void)injectCookieInHeaderWithRequest:(NSMutableURLRequest *)request {
    NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
    NSMutableArray *mCookies = [NSMutableArray new];
    for (NSHTTPCookie *cookie in cookies) {
        [mCookies addObject:cookie];
    }
    NSDictionary *headerParams = [NSHTTPCookie requestHeaderFieldsWithCookies:mCookies];
    ///設(shè)置請求頭 -- 攜帶NSHTTPCookieStorage容器中cookie數(shù)據(jù)
    request.allHTTPHeaderFields = headerParams;
}
  • 解決方案二:將NSHTTPCookieStorage中的cookie以JS的形式杠袱,注入到WKWebView中尚猿,代碼實現(xiàn)如下:
- (void)syncCookieToWKWithJSForRequest:(NSMutableURLRequest *)request {
    ///讀取NSHTTPCookieStorage中的cookie 拼接成JS字符串
    NSArray<NSHTTPCookie *> *tmp = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
    NSMutableString *jscode_Cookie = [@"" mutableCopy];
    [tmp enumerateObjectsUsingBlock:^(NSHTTPCookie * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"%@ = %@",obj.name,obj.value);
        [jscode_Cookie appendString:[NSString stringWithFormat:@"document.cookie = '%@=%@';", obj.name, obj.value]];
    }];
     
    WKUserContentController *userContentController = WKUserContentController.new;
    WKUserScript *cookieScript = [[WKUserScript alloc] initWithSource: jscode_Cookie injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
    [userContentController addUserScript:cookieScript];
    WKWebViewConfiguration *webViewConfig = WKWebViewConfiguration.new;
    webViewConfig.userContentController = userContentController;
    
    WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, ScreenW, ScreenH) configuration:webViewConfig];
    [self.view addSubview:webView];
}
iOS11之前的跨域重定向的cookie同步問題,主要是針對上面的兩種方案的再處理楣富,在凿掂,代碼如下:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    ///出現(xiàn)跨域時 cookie重新同步 JS方式
    if (![self.currentUrl.host isEqualToString:navigationAction.request.URL.host]) {
        //跨域重定向注入cookieScript
        NSString *strDocumentCookie = [MKCookieManager genDocumentCookieForWKWebview:navigationAction.request.URL];
        WKUserScript *cookieScript = [[WKUserScript alloc] initWithSource:strDocumentCookie injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
        [webView.configuration.userContentController  addUserScript:cookieScript];
 
        self.currentUrl = [NSURL URLWithString:navigationAction.request.URL.absoluteString];
    }
    decisionHandler(WKNavigationActionPolicyAllow);
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    ///出現(xiàn)跨域時 cookie重新同步 請求頭參數(shù)
    if (![self.currentUrl.host isEqualToString:navigationAction.request.URL.host]) {
        self.currentUrl = [NSURL URLWithString:navigationAction.request.URL.absoluteString];
        NSMutableURLRequest *request = [navigationAction.request mutableCopy];
        NSString *cookieValue = @"session=4342235345345234;token_id=dsfdfdgsgerqereeytrertewqerw=;uid=543234;";
        [request setValue:cookieValue forHTTPHeaderField:@"Cookie"];
        [webView loadRequest:request];
        decisionHandler(WKNavigationActionPolicyCancel);
    } else {
        decisionHandler(WKNavigationActionPolicyAllow);
    }
}

在 iOS11之后WKWebView第一次加載HTML時,cookie丟失
  • 在iOS11之后,當WKWebView第一次加載HTML時庄萎,由于不會讀取NSHTTPCookieStorage中的cookie踪少,導(dǎo)致cookie丟失,需要我們將NSHTTPCookieStorage容器中cookie同步到WKWebView中糠涛,實現(xiàn)第一次加載HTML的URL請求中攜帶上cookie數(shù)據(jù)援奢;
  • 解決方案:將NSHTTPCookieStorage容器中的cookie同步到WKHTTPCookieStore中,只要存于WKHTTPCookieStore中的cookie忍捡,WKWebView在發(fā)起URL請求時都會帶上cookie集漾,代碼如下:
- (void)syncCookieWithFirstRequest:(NSMutableURLRequest *)request {
    NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
    if (@available(iOS 11.0, *)) {
        WKHTTPCookieStore *cookieStore = self.wkWebView.configuration.websiteDataStore.httpCookieStore;
        for (NSHTTPCookie *cookie in cookies) {
            [cookieStore setCookie:cookie completionHandler:^{
                
            }];
        }
    } 
}
  • 將上述的兩種情況合并,即iOS11前后锉罐,WKWebView第一次發(fā)起URL請求時帆竹,將NSHTTPCookieStorage中cookie同步到WKWebView中,代碼如下:
///WKWebView第一次發(fā)起URL請求時 將NSHTTPCookieStorage中cookie同步到WKWebView中
- (void)syncCookieToWKWithFirstRequest:(NSMutableURLRequest *)request {
    NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
    if (@available(iOS 11.0, *)) {
        WKHTTPCookieStore *cookieStore = self.wkWebView.configuration.websiteDataStore.httpCookieStore;
        for (NSHTTPCookie *cookie in cookies) {
            [cookieStore setCookie:cookie completionHandler:^{
                
            }];
        }
    } else {
        NSMutableArray *mCookies = [NSMutableArray new];
        for (NSHTTPCookie *cookie in cookies) {
            [mCookies addObject:cookie];
        }
        NSDictionary *headerParams = [NSHTTPCookie requestHeaderFieldsWithCookies:mCookies];
        ///設(shè)置請求頭 -- 攜帶NSHTTPCookieStorage容器中cookie數(shù)據(jù)
        request.allHTTPHeaderFields = headerParams;
    }
}

參考文章

iOS 深入淺出 網(wǎng)絡(luò)編程之 NSHTTPCookie/NSHTTPCookieStorage
iOS UIWebView 和 WKWebView 的 cookie 獲取,設(shè)置,刪除
https://blog.csdn.net/Forever_wj?type=blog

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末脓规,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子险领,更是在濱河造成了極大的恐慌侨舆,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绢陌,死亡現(xiàn)場離奇詭異挨下,居然都是意外死亡,警方通過查閱死者的電腦和手機脐湾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門臭笆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人秤掌,你說我怎么就攤上這事愁铺。” “怎么了闻鉴?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵茵乱,是天一觀的道長。 經(jīng)常有香客問我孟岛,道長瓶竭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任渠羞,我火速辦了婚禮斤贰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘次询。我一直安慰自己荧恍,他們只是感情好,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布渗蟹。 她就那樣靜靜地躺著块饺,像睡著了一般赞辩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上授艰,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天辨嗽,我揣著相機與錄音,去河邊找鬼淮腾。 笑死糟需,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的谷朝。 我是一名探鬼主播洲押,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼圆凰!你這毒婦竟也來了杈帐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤专钉,失蹤者是張志新(化名)和其女友劉穎挑童,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體跃须,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡站叼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了菇民。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片尽楔。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖第练,靈堂內(nèi)的尸體忽然破棺而出阔馋,到底是詐尸還是另有隱情,我是刑警寧澤复旬,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布垦缅,位于F島的核電站,受9級特大地震影響驹碍,放射性物質(zhì)發(fā)生泄漏壁涎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一志秃、第九天 我趴在偏房一處隱蔽的房頂上張望怔球。 院中可真熱鬧,春花似錦浮还、人聲如沸竟坛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽担汤。三九已至涎跨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間崭歧,已是汗流浹背隅很。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留率碾,地道東北人叔营。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像所宰,于是被迫代替她去往敵國和親绒尊。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

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