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

客戶端登錄接口中的Cookie

  • 由于HTTP協(xié)議是無(wú)狀態(tài)的,客戶端需借助cookie來(lái)實(shí)現(xiàn)跨URL的請(qǐng)求;
  • 實(shí)現(xiàn)原理為:客戶端登錄服務(wù)端,服務(wù)端下發(fā)cookie數(shù)據(jù)給客戶端,客戶端的NSURLResponse根據(jù)會(huì)當(dāng)前的NSHTTPCookieStorage接受策略自動(dòng)接收服務(wù)端返回的cookie并存儲(chǔ)在NSHTTPCookieStorage容器中,我們不需要做任何操作,當(dāng)我們發(fā)送其他網(wǎng)絡(luò)請(qǐng)求(NSURLRequest)時(shí),我們只需要設(shè)置NSURLRequestHTTPShouldHandleCookies屬性為YES(默認(rèn)為YES),NSURLRequest會(huì)自動(dòng)附帶cookie的信息發(fā)送給服務(wù)器;
  • NSHTTPCookieStorage可設(shè)置cookieAcceptPolicy屬性,定義其接收cookie的策略,總共有三種接收策略,如下所示:
typedef NS_ENUM(NSUInteger, NSHTTPCookieAcceptPolicy) {
    NSHTTPCookieAcceptPolicyAlways,
    NSHTTPCookieAcceptPolicyNever,
    NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain
};
  • NSHTTPCookieAcceptPolicyAlways:永遠(yuǎn)接收Cookie,這種情況下,NSHTTPCookieStorage會(huì)將接收到的cookie 存儲(chǔ)在偏好設(shè)置中;
  • NSHTTPCookieAcceptPolicyNever:永遠(yuǎn)不接受Cookie,這種情況下,NSHTTPCookieStorage不會(huì)存儲(chǔ)cookie到本地;
  • NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain:只接收指定域名的Cookie;
  • NSHTTPCookieStorage的常見(jiàn)Api操作如下所示:
// cookie的接收策略
@property NSHTTPCookieAcceptPolicy cookieAcceptPolicy

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

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

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

// 在某個(gè)時(shí)間點(diǎn)刪除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容器中存儲(chǔ)的cookie數(shù)據(jù)是NSHTTPCookie實(shí)例對(duì)象,涉及的Api如下所示:
// 下面兩個(gè)方法用于對(duì)象的創(chuàng)建和初始化 都是通過(guò)字典進(jìn)行鍵值設(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;

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

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

// 請(qǐng)求相應(yīng)的值
@property (readonly, copy) NSString *value;

// 過(guò)期時(shí)間
@property (nullable, readonly, copy) NSDate *expiresDate;

// 請(qǐng)求的域名
@property (readonly, copy) NSString *domain;

//請(qǐng)求的路徑
@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;
  • 在客戶端當(dāng)用戶推出登錄時(shí),我們需要清除NSHTTPCookieStorage容器中存儲(chǔ)的關(guān)于用戶的所有cookie數(shù)據(jù);
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
for (NSHTTPCookie *cookie in cookies) {
   [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
}

UIWebView中Cookie機(jī)制

  • UIWebView在加載HTML網(wǎng)頁(yè)時(shí),會(huì)自動(dòng)將網(wǎng)頁(yè)中cookie數(shù)據(jù)存儲(chǔ)到NSHTTPCookieStorage容器中,然后再加載其他的URL網(wǎng)頁(yè)請(qǐng)求時(shí),請(qǐng)求會(huì)自動(dòng)攜帶NSHTTPCookieStorage容器中的cookie數(shù)據(jù),基本原理與上述的客戶端登錄接口中的Cookie的原理是一致的;
  • 在同一個(gè)App內(nèi),多個(gè)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)頁(yè)加載完成取出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機(jī)制
  • 在iOS11之前, WKWebView 加載HTML發(fā)起的URL請(qǐng)求不會(huì)自動(dòng)帶上存儲(chǔ)于 NSHTTPCookieStorage 容器中的 Cookie,出現(xiàn)cookie丟失的問(wèn)題;
  • 在iOS11之后,iOS提供了WKHTTPCookieStore類,其類似于NSHTTPCookieStorage,是一個(gè)存儲(chǔ)cookie數(shù)據(jù)的容器,在發(fā)起URL請(qǐng)求時(shí),會(huì)自動(dòng)保存網(wǎng)頁(yè)中的cookie,再加載其他的URL網(wǎng)頁(yè)請(qǐng)求,會(huì)自動(dòng)攜帶上cookie數(shù)據(jù),但第一次發(fā)起URL請(qǐng)求時(shí),依然存在cookie丟失的問(wèn)題;
第一個(gè)問(wèn)題:在 iOS11之前WKWebView第一次加載HTML時(shí),cookie丟失
  • 在iOS11之前,當(dāng)WKWebView第一次加載HTML時(shí),由于不會(huì)讀取NSHTTPCookieStorage中的cookie,導(dǎo)致cookie丟失,需要我們將NSHTTPCookieStorage容器中cookie同步到WKWebView中,實(shí)現(xiàn)第一次加載HTML的URL請(qǐng)求中攜帶上cookie數(shù)據(jù);
  • 解決方案一:將NSHTTPCookieStorage容器中的cookie同步到URL請(qǐng)求頭參數(shù)中,代碼實(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è)置請(qǐng)求頭 -- 攜帶NSHTTPCookieStorage容器中cookie數(shù)據(jù)
    request.allHTTPHeaderFields = headerParams;
}
  • 解決方案二:將NSHTTPCookieStorage中的cookie以JS的形式,注入到WKWebView中,代碼實(shí)現(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同步問(wèn)題,主要是針對(duì)上面的兩種方案的再處理,在,代碼如下:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    ///出現(xiàn)跨域時(shí) 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)跨域時(shí) cookie重新同步 請(qǐng)求頭參數(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時(shí),cookie丟失
  • 在iOS11之后,當(dāng)WKWebView第一次加載HTML時(shí),由于不會(huì)讀取NSHTTPCookieStorage中的cookie,導(dǎo)致cookie丟失,需要我們將NSHTTPCookieStorage容器中cookie同步到WKWebView中,實(shí)現(xiàn)第一次加載HTML的URL請(qǐng)求中攜帶上cookie數(shù)據(jù);
  • 解決方案:將NSHTTPCookieStorage容器中的cookie同步到WKHTTPCookieStore中,只要存于WKHTTPCookieStore中的cookie,WKWebView在發(fā)起URL請(qǐng)求時(shí)都會(huì)帶上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請(qǐng)求時(shí),將NSHTTPCookieStorage中cookie同步到WKWebView中,代碼如下:
///WKWebView第一次發(fā)起URL請(qǐng)求時(shí) 將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è)置請(qǐng)求頭 -- 攜帶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)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容