iOS端WebView " IP直連 " 如何處理 Cookie
本文主要介紹防DNS污染方案在WebView場(chǎng)景下所遇到的一些問(wèn)題及解決方案。
當(dāng)前最佳實(shí)踐文檔只針對(duì)結(jié)合使用時(shí),如何使用HTTPDNS解析出的IP,關(guān)于HTTPDNS本身的解析服務(wù),請(qǐng)先查看iOS SDK 開(kāi)發(fā)手冊(cè)。
WKWebView無(wú)法使用NSURLProtocol攔截請(qǐng)求
針對(duì)該問(wèn)題方案如下:
換用UIWebView
換用UIWebView方案不做贅述,說(shuō)明下使用私有API進(jìn)行注冊(cè)攔截的方法 :
// 注冊(cè)自己的 protocol [NSURLProtocol registerClass:[CustomProtocol class]]; // 創(chuàng)建 WKWebview WKWebViewConfiguration * config = [[WKWebViewConfiguration alloc] init]; WKWebView * wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height) configuration:config]; [wkWebView loadRequest:webViewReq]; [self.view addSubview:wkWebView]; //注冊(cè) scheme Class cls = NSClassFromString(@"WKBrowsingContextController"); SEL sel = NSSelectorFromString(@"registerSchemeForCustomProtocol:"); if ([cls respondsToSelector:sel]) { // 通過(guò) http 和 https 的請(qǐng)求,同理可通過(guò)其他的 Scheme 但是要滿足 ULR Loading System [cls performSelector:sel withObject:@"http"]; [cls performSelector:sel withObject:@"https"]; }
使用私有API的另一風(fēng)險(xiǎn)是兼容性問(wèn)題,比如上面的
browsingContextController
就只能在iOS 8.4以后才能用,反注冊(cè)scheme的方法unregisterSchemeForCustomProtocol :
也是在iOS 8.4 以后才被添加進(jìn)來(lái)的,要支持iOS 8.0 ~ 8.3機(jī)型的話,只能通過(guò)動(dòng)態(tài)生成字符串的方式拿到WKBrowsingContextController
,而且還不能反注冊(cè),不過(guò)這些問(wèn)題都不大。至于向后兼容,這個(gè)也不用太擔(dān)心,因?yàn)閕OS發(fā)布新版本之前都會(huì)有開(kāi)發(fā)者預(yù)覽版的,那個(gè)時(shí)候再測(cè)一下也不遲。對(duì)于本文的例子來(lái)說(shuō),如果將來(lái)哪個(gè)iOS版本移除了這個(gè)API,那很可能是因?yàn)楣俜教峁┝送暾慕鉀Q方案,到那時(shí)候自然也不需要本文介紹的方法了 。重要避免執(zhí)行太晚,如果在
- (void)viewDidLoad
中注冊(cè),可能會(huì)因?yàn)樽?cè)太晚,引發(fā)問(wèn)題。建議在+load
方法中執(zhí)行 。然后同樣會(huì)遇到iOS端HTTPS(含SNI)業(yè)務(wù)場(chǎng)景:IP直連方案說(shuō)明里提到的各種NSURLProtocol相關(guān)的問(wèn)題,可以參照里面的方法解決。
WebView中的Cookie處理業(yè)務(wù)場(chǎng)景“IP直連”方案說(shuō)明
本章節(jié)將討論類(lèi)似這樣的問(wèn)題:
WKWebView對(duì)于Cookie的管理一直是它的短板,那么iOS11是否有改進(jìn),如果有,如何利用這樣的改進(jìn)?
采用IP直連方案后,服務(wù)端返回的Cookie里的Domain字段也會(huì)使用IP。如果IP是動(dòng)態(tài)的,就有可能導(dǎo)致一些問(wèn)題:由于許多H5業(yè)務(wù)都依賴(lài)于Cookie作登錄態(tài)校驗(yàn) ,而WKWebView上請(qǐng)求不會(huì)自動(dòng)攜帶 Cookie。
WKWebView使用NSURLProtocol攔截請(qǐng)求無(wú)法獲取Cookie信息
iOS 11推出了新的APIWKHT
TPC
ookieStore
可以用來(lái)攔截WKWebView的Cookie信息。
用法示例如下:
WKHTTPCookieStore *cookieStroe = self.webView.configuration.websiteDataStore.httpCookieStore;
// get cookies
[cookieStroe getAllCookies:^(NSArray<NSHTTPCookie *> * _Nonnull cookies) {
NSLog(@"All cookies %@",cookies);
}];
// set cookie
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[NSHTTPCookieName] = @"userid";
dict[NSHTTPCookieValue] = @"123";
dict[NSHTTPCookieDomain] = @"xxxx.com";
dict[NSHTTPCookiePath] = @"/";
NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:dict];
[cookieStroe setCookie:cookie completionHandler:^{
NSLog(@"set cookie");
}];
// delete cookie
[cookieStroe deleteCookie:cookie completionHandler:^{
NSLog(@"delete cookie");
}];
利用iOS 11 API WKHTTPCookieStore解決WKWebView首次請(qǐng)求不攜帶Cookie的問(wèn)題
問(wèn)題說(shuō)明:由于許多H5業(yè)務(wù)都依賴(lài)于Cookie作登錄態(tài)校驗(yàn),而WKWebView上請(qǐng)求不會(huì)自動(dòng)攜帶 Cookie。比如,如果你在Native層面做了登錄操作,獲取了Cookie信息,也使用 NSHTTPCookieStorage存到了本地,但是使用WKWebView打開(kāi)對(duì)應(yīng)網(wǎng)頁(yè)時(shí),網(wǎng)頁(yè)依然處于未登錄狀態(tài)。如果是登錄也在WebView里做的,就不會(huì)有這個(gè)問(wèn)題。
iOS 11的API可以解決該問(wèn)題,只要是存在WKHTTPCookieStore里的cookie,WKWebView每次請(qǐng)求都會(huì)攜帶,存在NSHTTPCookieStorage的cookie,并不會(huì)每次都攜帶。于是會(huì)發(fā)生首次WKWebView 請(qǐng)求不攜帶Cookie的問(wèn)題。
解決方法:
在執(zhí)行
-[WKWebView loadReques:]
前將NSHT
TPC
ookieStorage
中的內(nèi)容復(fù)制到WKHTTPCookieStore
中,以此來(lái)達(dá)到 WKWebView Cookie 注入的目的。示例代碼如下:[self copyNSHTTPCookieStorageToWKHTTPCookieStoreWithCompletionHandler:^{ NSURL *url = [NSURL URLWithString:@"https://www.v2ex.com"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; [_webView loadRequest:request]; }];
- (void)copyNSHTTPCookieStorageToWKHTTPCookieStoreWithCompletionHandler:(nullable void (^)())theCompletionHandler; { NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]; WKHTTPCookieStore *cookieStroe = self.webView.configuration.websiteDataStore.httpCookieStore; if (cookies.count == 0) { !theCompletionHandler ?: theCompletionHandler(); return; } for (NSHTTPCookie *cookie in cookies) { [cookieStroe setCookie:cookie completionHandler:^{ if ([[cookies lastObject] isEqual:cookie]) { !theCompletionHandler ?: theCompletionHandler(); return; } }]; } }
這個(gè)是iOS 11的API ,針對(duì)iOS 11之前的系統(tǒng) ,需要另外處理。
利用 iOS 11之前的API解決WKWebView首次請(qǐng)求不攜帶Cookie的問(wèn)題
通過(guò)讓所有WKWebView共享同一個(gè)WKProcessPool實(shí)例,可以實(shí)現(xiàn)多個(gè)WKWebView之間共享Cookie(session Cookie and persistent Cookie)數(shù)據(jù)。不過(guò)WKWebView WKProcessPool實(shí)例在app殺進(jìn)程重啟后會(huì)被重置,導(dǎo)致WKProcessPool中的Cookie、session Cookie數(shù)據(jù)丟失,目前也無(wú)法實(shí)現(xiàn) WKProcessPool實(shí)例本地化保存。可以采取cookie放入Header的方法來(lái)做。
WKWebView * webView = [WKWebView new];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://xxx.com/login"]];
[request addValue:@"skey=skeyValue" forHTTPHeaderField:@"Cookie"];
[webView loadRequest:request];
其中對(duì)于skey=skeyValue
這個(gè)cookie值的獲取,也可以統(tǒng)一通過(guò)domain獲取,獲取的方法,可以參照下面的工具類(lèi):
HTTPDNSCookieManager.h
#ifndef HTTPDNSCookieManager_h
#define HTTPDNSCookieManager_h
// URL匹配Cookie規(guī)則
typedef BOOL (^HTTPDNSCookieFilter)(NSHTTPCookie *, NSURL *);
@interface HTTPDNSCookieManager : NSObject
+ (instancetype)sharedInstance;
/**
指定URL匹配Cookie策略
@param filter 匹配器
*/
- (void)setCookieFilter:(HTTPDNSCookieFilter)filter;
/**
處理HTTP Response攜帶的Cookie并存儲(chǔ)
@param headerFields HTTP Header Fields
@param URL 根據(jù)匹配策略獲取查找URL關(guān)聯(lián)的Cookie
@return 返回添加到存儲(chǔ)的Cookie
*/
- (NSArray<NSHTTPCookie *> *)handleHeaderFields:(NSDictionary *)headerFields forURL:(NSURL *)URL;
/**
匹配本地Cookie存儲(chǔ),獲取對(duì)應(yīng)URL的request cookie字符串
@param URL 根據(jù)匹配策略指定查找URL關(guān)聯(lián)的Cookie
@return 返回對(duì)應(yīng)URL的request Cookie字符串
*/
- (NSString *)getRequestCookieHeaderForURL:(NSURL *)URL;
/**
刪除存儲(chǔ)cookie
@param URL 根據(jù)匹配策略查找URL關(guān)聯(lián)的cookie
@return 返回成功刪除cookie數(shù)
*/
- (NSInteger)deleteCookieForURL:(NSURL *)URL;
@end
#endif /* HTTPDNSCookieManager_h */
HTTPDNSCookieManager.m
#import <Foundation/Foundation.h>
#import "HTTPDNSCookieManager.h"
@implementation HTTPDNSCookieManager
{
HTTPDNSCookieFilter cookieFilter;
}
- (instancetype)init {
if (self = [super init]) {
/**
此處設(shè)置的Cookie和URL匹配策略比較簡(jiǎn)單,檢查URL.host是否包含Cookie的domain字段
通過(guò)調(diào)用setCookieFilter接口設(shè)定Cookie匹配策略,
比如可以設(shè)定Cookie的domain字段和URL.host的后綴匹配 | URL是否符合Cookie的path設(shè)定
細(xì)節(jié)匹配規(guī)則可參考RFC 2965 3.3節(jié)
*/
cookieFilter = ^BOOL(NSHTTPCookie *cookie, NSURL *URL) {
if ([URL.host containsString:cookie.domain]) {
return YES;
}
return NO;
};
}
return self;
}
+ (instancetype)sharedInstance {
static id singletonInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (!singletonInstance) {
singletonInstance = [[super allocWithZone:NULL] init];
}
});
return singletonInstance;
}
+ (id)allocWithZone:(struct _NSZone *)zone {
return [self sharedInstance];
}
- (id)copyWithZone:(struct _NSZone *)zone {
return self;
}
- (void)setCookieFilter:(HTTPDNSCookieFilter)filter {
if (filter != nil) {
cookieFilter = filter;
}
}
- (NSArray<NSHTTPCookie *> *)handleHeaderFields:(NSDictionary *)headerFields forURL:(NSURL *)URL {
NSArray *cookieArray = [NSHTTPCookie cookiesWithResponseHeaderFields:headerFields forURL:URL];
if (cookieArray != nil) {
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in cookieArray) {
if (cookieFilter(cookie, URL)) {
NSLog(@"Add a cookie: %@", cookie);
[cookieStorage setCookie:cookie];
}
}
}
return cookieArray;
}
- (NSString *)getRequestCookieHeaderForURL:(NSURL *)URL {
NSArray *cookieArray = [self searchAppropriateCookies:URL];
if (cookieArray != nil && cookieArray.count > 0) {
NSDictionary *cookieDic = [NSHTTPCookie requestHeaderFieldsWithCookies:cookieArray];
if ([cookieDic objectForKey:@"Cookie"]) {
return cookieDic[@"Cookie"];
}
}
return nil;
}
- (NSArray *)searchAppropriateCookies:(NSURL *)URL {
NSMutableArray *cookieArray = [NSMutableArray array];
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in [cookieStorage cookies]) {
if (cookieFilter(cookie, URL)) {
NSLog(@"Search an appropriate cookie: %@", cookie);
[cookieArray addObject:cookie];
}
}
return cookieArray;
}
- (NSInteger)deleteCookieForURL:(NSURL *)URL {
int delCount = 0;
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in [cookieStorage cookies]) {
if (cookieFilter(cookie, URL)) {
NSLog(@"Delete a cookie: %@", cookie);
[cookieStorage deleteCookie:cookie];
delCount++;
}
}
return delCount;
}
@end
發(fā)送請(qǐng)求使用方法示例:
WKWebView * webView = [WKWebView new];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://xxx.com/login"]];
NSString *value = [[HTTPDNSCookieManager sharedInstance] getRequestCookieHeaderForURL:url];
[request setValue:value forHTTPHeaderField:@"Cookie"];
[webView loadRequest:request];
接收處理請(qǐng)求:
NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
// 解析 HTTP Response Header,存儲(chǔ)cookie
[[HTTPDNSCookieManager sharedInstance] handleHeaderFields:[httpResponse allHeaderFields] forURL:url];
}
}];
[task resume];
通過(guò)document.cookie
設(shè)置Cookie解決后續(xù)頁(yè)面(同域)Ajax、iframe請(qǐng)求的Cookie問(wèn)題。
WKUserContentController* userContentController = [WKUserContentController new];
WKUserScript * cookieScript = [[WKUserScript alloc] initWithSource: @"document.cookie = 'skey=skeyValue';" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[userContentController addUserScript:cookieScript];
Cookie包含動(dòng)態(tài)IP導(dǎo)致登錄失效問(wèn)題
關(guān)于Cookie失效的問(wèn)題,假如客戶(hù)端登錄session存在Cookie,此時(shí)這個(gè)域名配置了多個(gè)IP,使用域名訪問(wèn)會(huì)讀對(duì)應(yīng)域名的Cookie,使用IP訪問(wèn)則去讀對(duì)應(yīng)IP的Cookie,假如前后兩次使用同一個(gè)域名配置的不同IP訪問(wèn),會(huì)導(dǎo)致Cookie的登錄session失效。
如果App里面的WebView頁(yè)面需要用到系統(tǒng)Cookie存的登錄session,之前App所有本地網(wǎng)絡(luò)請(qǐng)求使用域名訪問(wèn),是可以共用Cookie的登錄session的,但現(xiàn)在本地網(wǎng)絡(luò)請(qǐng)求使用HTTPDNS后改用IP訪問(wèn),導(dǎo)致還使用域名訪問(wèn)的WebView讀不到系統(tǒng)Cookie存的登錄session了(系統(tǒng)Cookie對(duì)應(yīng)IP了)。IP直連后,服務(wù)端返回Cookie包含動(dòng)態(tài)IP導(dǎo)致登錄失效。
使用IP訪問(wèn)后,服務(wù)端返回的cookie也是IP。導(dǎo)致可能使用對(duì)應(yīng)的域名訪問(wèn),無(wú)法使用本地Cookie,或者使用隸屬于同一個(gè)域名的不同IP去訪問(wèn),Cookie也對(duì)不上,導(dǎo)致登錄失效。
我這邊的思路是這樣的:
應(yīng)該得干預(yù)Cookie的存儲(chǔ),基于域名。
根源上,API域名返回單IP。
第二種思路將失去DNS調(diào)度特性,故不考慮。第一種思路更為可行。
基于iOS11 API WKHTTPCookieStore來(lái)解決WKWebView的Cookie管理問(wèn)題
當(dāng)每次服務(wù)端返回Cookie后,在存儲(chǔ)前都進(jìn)行下改造,使用域名替換下IP。之后雖然每次網(wǎng)絡(luò)請(qǐng)求都是使用IP訪問(wèn),但是host我們都手動(dòng)改為了域名,這樣本地存儲(chǔ)的Cookie也就能對(duì)得上了。
代碼演示:
在網(wǎng)絡(luò)請(qǐng)求成功后,或者加載網(wǎng)頁(yè)成功后,主動(dòng)將本地的domain字段為IP的Cookie替換IP為host域名地址。
- (void)updateWKHTTPCookieStoreDomainFromIP:(NSString *)IP toHost:(NSString *)host {
WKHTTPCookieStore *cookieStroe = self.webView.configuration.websiteDataStore.httpCookieStore;
[cookieStroe getAllCookies:^(NSArray<NSHTTPCookie *> * _Nonnull cookies) {
[[cookies copy] enumerateObjectsUsingBlock:^(NSHTTPCookie * _Nonnull cookie, NSUInteger idx, BOOL * _Nonnull stop) {
if ([cookie.domain isEqualToString:IP]) {
NSMutableDictionary<NSHTTPCookiePropertyKey, id> *dict = [NSMutableDictionary dictionaryWithDictionary:cookie.properties];
dict[NSHTTPCookieDomain] = host;
NSHTTPCookie *newCookie = [NSHTTPCookie cookieWithProperties:[dict copy]];
[cookieStroe setCookie:newCookie completionHandler:^{
[self logCookies];
[cookieStroe deleteCookie:cookie
completionHandler:^{
[self logCookies];
}];
}];
}
}];
}];
}
iOS 11中也提供了對(duì)應(yīng)的API供我們來(lái)處理替換Cookie的時(shí)機(jī),那就是下面的API:
@protocol WKHTTPCookieStoreObserver <NSObject>
@optional
- (void)cookiesDidChangeInCookieStore:(WKHTTPCookieStore *)cookieStore;
@end
//WKHTTPCookieStore
/*! @abstract Adds a WKHTTPCookieStoreObserver object with the cookie store.
@param observer The observer object to add.
@discussion The observer is not retained by the receiver. It is your responsibility
to unregister the observer before it becomes invalid.
*/
- (void)addObserver:(id<WKHTTPCookieStoreObserver>)observer;
/*! @abstract Removes a WKHTTPCookieStoreObserver object from the cookie store.
@param observer The observer to remove.
*/
- (void)removeObserver:(id<WKHTTPCookieStoreObserver>)observer;
用法如下:
@interface WebViewController ()<WKHTTPCookieStoreObserver>
- (void)viewDidLoad {
[super viewDidLoad];
[NSURLProtocol registerClass:[WebViewURLProtocol class]];
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
[cookieStorage setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];
WKHTTPCookieStore *cookieStroe = self.webView.configuration.websiteDataStore.httpCookieStore;
[cookieStroe addObserver:self];
[self.view addSubview:self.webView];
//... ...
}
#pragma mark -
#pragma mark - WKHTTPCookieStoreObserver Delegate Method
- (void)cookiesDidChangeInCookieStore:(WKHTTPCookieStore *)cookieStore {
[self updateWKHTTPCookieStoreDomainFromIP:CYLIP toHost:CYLHOST];
}
-updateWKHT
TPC
ookieStoreDomainFromIP
方法的實(shí)現(xiàn),在上文已經(jīng)給出。
這個(gè)方案需要客戶(hù)端維護(hù)一個(gè)IP —> HOST的映射關(guān)系,需要能從IP反向查找到HOST,這個(gè)維護(hù)成本還時(shí)挺高的。下面介紹下,更通用的方法,也是iOS11之前的處理方法:
iOS 11之前的處理方法:NSURLProtocal攔截后,手動(dòng)管理Cookie的存儲(chǔ):
步驟:做IP替換時(shí)將原始URL保存到Header中
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
NSMutableURLRequest *mutableReq = [request mutableCopy];
NSString *originalUrl = mutableReq.URL.absoluteString;
NSURL *url = [NSURL URLWithString:originalUrl];
// 異步接口獲取IP地址
NSString *ip = [[HttpDnsService sharedInstance] getIpByHostAsync:url.host];
if (ip) {
NSRange hostFirstRange = [originalUrl rangeOfString:url.host];
if (NSNotFound != hostFirstRange.location) {
NSString *newUrl = [originalUrl stringByReplacingCharactersInRange:hostFirstRange withString:ip];
mutableReq.URL = [NSURL URLWithString:newUrl];
[mutableReq setValue:url.host forHTTPHeaderField:@"host"];
// 添加originalUrl保存原始URL
[mutableReq addValue:originalUrl forHTTPHeaderField:@"originalUrl"];
}
}
NSURLRequest *postRequestIncludeBody = [mutableReq cyl_getPostRequestIncludeBody];
return postRequestIncludeBody;
}
然后獲取到數(shù)據(jù)后,手動(dòng)管理Cookie :
- (void)handleCookiesFromResponse:(NSURLResponse *)response {
NSString *originalURLString = [self.request valueForHTTPHeaderField:@"originalUrl"];
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
NSDictionary<NSString *, NSString *> *allHeaderFields = httpResponse.allHeaderFields;
if (originalURLString && originalURLString.length > 0) {
NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:allHeaderFields forURL: [[NSURL alloc] initWithString:originalURLString]];
if (cookies && cookies.count > 0) {
NSURL *originalURL = [NSURL URLWithString:originalURLString];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:cookies forURL:originalURL mainDocumentURL:nil];
}
}
}
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)newRequest completionHandler:(void (^)(NSURLRequest *))completionHandler {
NSString *location = response.allHeaderFields[@"Location"];
NSURL *url = [[NSURL alloc] initWithString:location];
NSMutableURLRequest *mRequest = [newRequest mutableCopy];
mRequest.URL = url;
if (location && location.length > 0) {
if ([[newRequest.HTTPMethod lowercaseString] isEqualToString:@"post"]) {
// POST重定向?yàn)镚ET
mRequest.HTTPMethod = @"GET";
mRequest.HTTPBody = nil;
}
[mRequest setValue:nil forHTTPHeaderField:@"host"];
// 在這里為 request 添加 cookie 信息。
[self handleCookiesFromResponse:response];
[XXXURLProtocol removePropertyForKey:XXXURLProtocolHandledKey inRequest:mRequest];
completionHandler(mRequest);
} else{
completionHandler(mRequest);
}
}
發(fā)送請(qǐng)求前,向請(qǐng)求中添加Cookie信息:
+ (void)handleCookieWithRequest:(NSMutableURLRequest *)request {
NSString* originalURLString = [request valueForHTTPHeaderField:@"originalUrl"];
if (!originalURLString || originalURLString.length == 0) {
return;
}
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
if (cookies && cookies.count >0) {
NSDictionary *cookieHeaders = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
NSString *cookieString = [cookieHeaders objectForKey:@"Cookie"];
[request addValue:cookieString forHTTPHeaderField:@"Cookie"];
}
}
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
NSMutableURLRequest *mutableReq = [request mutableCopy];
//...
[self handleCookieWithRequest:mutableReq];
return [mutableReq copy];
}
相關(guān)的文章:
302重定向問(wèn)題.
上面提到的Cookie方案無(wú)法解決302請(qǐng)求的Cookie問(wèn)題,比如,第一個(gè)請(qǐng)求是http://www.example1.com,我們通過(guò)在request header里帶上Cookie解決該請(qǐng)求的Cookie問(wèn)題,接著頁(yè)面302跳轉(zhuǎn)到http://www.example2.com,這個(gè)時(shí)候http://www.example2.com這個(gè)請(qǐng)求就可能因?yàn)闆](méi)有攜帶Cookie而無(wú)法訪問(wèn)。當(dāng)然,由于每一次頁(yè)面跳轉(zhuǎn)前都會(huì)調(diào)用回調(diào)函數(shù):
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
可以在該回調(diào)函數(shù)里攔截302請(qǐng)求,copy request,在request header中帶上Cookie并重新 loadRequest。不過(guò)這種方法依然解決不了頁(yè)面iframe跨域請(qǐng)求的Cookie問(wèn)題,畢竟 -[WKWebView loadRequest:]
只適合加載mainFrame請(qǐng)求。
相關(guān)參考
相關(guān)的庫(kù):
相關(guān)的文章:
可以參考的Demo: