iOS WKWebViewとUIWebView Cookieメカニズムの同期
18716 ワード
WKWebViewの概要 WKWebViewは、アップルがWWDC 2014で発売した次世代WebViewコンポーネントで、iOS 8や従来のUIWebViewよりも明らかな優位性を持っています. より多くのHTML 5をサポートする機能 最大60 fpsのスクロールリフレッシュ率および内蔵ジェスチャー UIWebViewDelegateとUIWebViewを14クラスと3つのプロトコル に分割 Safari同じJSエンジン より少ないメモリ消費量
まず、WKWebViewの属性方法を簡単に理解してみましょう. JSとWebViewのコンテンツインタラクション WKUserScript
WKuserContentControlでは、WKuserScriptに使用されます.WKUSerContentControlはJSと対話するためのクラスであり、注入されたJSはWKUSerScriptオブジェクトである.すべてのプロパティとメソッドは次のとおりです. WKNavigationDelegate
WKWebViewとUIWebViewのCookie同期の問題 WKWebViewはiOS 9以降に発売されたwebviewコンポーネントであるため、それ以前はUIWebViewであった.したがって、iOS 8上のUIWebViewの使用に影響を及ぼさないためには、WKWebViewとUIWebViewに対してパッケージ化互換処理を行う必要があります.Cookieの同期問題は大きな穴の一つです.
WKWebViewのCookieメカニズム: WKWebViewのCookieは、プライベートストレージWKWebsiteDataStoreに格納されます.WKWebsiteDataStoreには、cookies、disk、memory caches、WebSQL、IndexedDBデータベース、ローカルストレージなどのWebコンテンツが格納されています. なぜWKWebsiteDataStoreがプライベートストレージコンテナだと言うのですかWKWebsiteDataStoreのヘッダファイルを開くと が表示されます
WKWebsiteDataStoreのWebデータはWKWebsiteDataRecordクラスとして保存されており、fetchDataRecordsOfTypeメソッドでdatastoreのrecordデータを取得できます.しかし、WKWebsiteDataRecordクラスのヘッダファイルを開くと、次のことがわかります.共有プロパティは、レコードデータのドメイン名とレコードに保存されているWebデータの2つです.この中に何が入っているのか見てみましょう. の結果は、 として出力される. だからWKWebsiteDataRecordではクッキーデータは何も手に入らない.
UIWebViewのCookieメカニズム
UIWebViewは、Webページを参照すると、WebページのクッキーをNSHTTPCookieStorage標準コンテナに自動的に格納します.後続のアクセスでクッキーはrequestリクエストに自動的に持ち込まれます.例えば、NSHTTPCookieStorageにはCookie、name=aが格納されている.value=b;domain=y.qq.com;expires=Sat,02 May 2017 23:20:25 GMT; UIWebViewによってリクエストが開始されますhttp://y.qq.comを選択すると、要求ヘッダは自動的にクッキーを持ち、WKWebViewによって要求が開始され、要求ヘッダは自動的にクッキーを持ちません.
初歩的な解決策
実は主にしなければならないのは2つのステップだけで、1、Cookieを取得して、2、Cookieを注入します
1、Cookieを取得する WKWebViewのCookie格納容器WKWebsiteDataStoreはプライベートストレージであるため、ここからCookieを取得することはできないが、現在の方法は(1)ウェブサイトから戻ってきたresponse headerfieldsから取得することである.(2)jsを呼び出す方法でクッキーを取得する. (1)Webサイトから返されたresponse headerfieldsから を取得 cookieはhttp responeのheaderfieldsが存在するため、responeを得ることができるWKWebViewコールバックを見つけ、 を印刷する.(2)jsを呼び出す方法によりクッキーを取得する. Cookieを取得中に遭遇するピット 1、(2)のdocumentを通過する.クッキーの方法でクッキーを取得するには一定の実行可能性があるが,実践後に取得したクッキーは全面的ではなく,httponlyのクッキーを取得できないことが分かった.https://www.zhihu.comで試してみるとz_c 0のクッキーはhttponly属性のクッキーであるため取得できません. 2、(1)方法でも(2)方法でも302リクエストのCookie問題は解決できないようです.例えば、サイトAにアクセスするとして、Aでログインをクリックし、ページをBアドレスにジャンプし、Bでログインが完了した後302でAサイトにジャンプします.このとき、クッキーはBアドレスのresponseに存在し、Aアドレスのresponseにはクッキーのフィールドは存在しない.しかし,Aアドレスのresponseしか取得できず,Bアドレスのresponseをキャプチャできない.そのため、このタイプのサイトのクッキーは取得できません. 2、注入Cookie注入Cookieとは、以前にクッキーを保存していたNSHTTPCookieStorageから関連Cookieを取り出し、再度アクセスを要求したときにrequestにCookieを注入することである.Cookieの注入にも様々な方法があります. (1)JS注入1 (2)JS注入2 (3)NSMutableURLRequest注入Cookie Cookie注入中に遭遇するピット (1)JSが注入するCookie、例えばPHPコードはCookie容器から取り出せない、直接js document.クッキーは
NSMutableURLRequestが注入したPHPなどの動的言語は直接$_からCOOKIEオブジェクトで取得した
(2)app終了後のクッキー紛失の問題開発中にクッキーの保存に問題があり、クッキーを注入したときに一部のクッキーが失われたことが分かった.ネット上で別の解決策を見つけたのは、クッキーが期限切れになったということで、手動で保存時間を設定する必要があります.デフォルトでは、クッキーの保存時間はappが終了するとクッキーが消去されます.参照:http://www.jianshu.com/p/1e402922ee32/.クッキーの有効期限を設定した後も、クッキーの紛失の問題があります.(ため息 はその後NSUserDefaultを用いてクッキーを取得するときにクッキーを保存し、再アクセスするときにNSUserDefaultからクッキーを取り出して再設定することで、以前に保存していたクッキーを得るrequestに を注入する.
締めくくり WKWebViewクッキーの同期問題には穴が多く、リダイレクトアドレスCookieを取得する問題はまだ解決されていません.アドバイスや困惑している学生はコメントしたり、プライベートチャットしたりして解決することができます.
まず、WKWebViewの属性方法を簡単に理解してみましょう.
// webview
@property (nonatomic, readonly, copy) WKWebViewConfiguration *configuration;
//
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
//WKPreferences
config.preferences = [[WKPreferences alloc] init];
// 0
config.preferences.minimumFontSize = 10;
// YES
config.preferences.javaScriptEnabled = YES;
// iOS NO,
config.preferences.javaScriptCanOpenWindowsAutomatically = NO;
//
@property (nullable, nonatomic, weak) id navigationDelegate;
//
@property (nullable, nonatomic, weak) id UIDelegate;
// UIWebView API
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
// HTML
- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
// data
- (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString*)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL NS_AVAILABLE(10_11, 9_0);
//
- (void)stopLoading;
// JS
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ __nullable)(__nullable id, NSError * __nullable error))completionHandler;
// , WKUserScript
@property (nonatomic, readonly, copy) NSArray *userScripts;
// JS
- (void)addUserScript:(WKUserScript *)userScript;
// JS
- (void)removeAllUserScripts;
// scriptMessageHandler frames ,
// window.webkit.messageHandlers..postMessage()
//
// JS
- (void)addScriptMessageHandler:(id)scriptMessageHandler name:(NSString *)name;
// name scriptMessageHandler
- (void)removeScriptMessageHandlerForName:(NSString *)name;
WKuserContentControlでは、WKuserScriptに使用されます.WKUSerContentControlはJSと対話するためのクラスであり、注入されたJSはWKUSerScriptオブジェクトである.すべてのプロパティとメソッドは次のとおりです.
// JS
@property (nonatomic, readonly, copy) NSString *source;
// JS
@property (nonatomic, readonly) WKUserScriptInjectionTime injectionTime;
// , JS frames main frame.
@property (nonatomic, readonly, getter=isForMainFrameOnly) BOOLforMainFrameOnly;
// , WKUserScript
// source:JS
// injectionTime:JS
// forMainFrameOnly: main frame
- (instancetype)initWithSource:(NSString *)source injectionTime:(WKUserScriptInjectionTime)injectionTime forMainFrameOnly:(BOOL)forMainFrameOnly;
@protocol WKNavigationDelegate
@optional
// , 。WebKit , ,
// 。 , Safari , 。
// Request
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
//
// response
// response, WKNavigationResponse
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
// main frame ,
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation;
// main frame ,
- (void)webView:(WKWebView *)webViewdidReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation*)navigation;
// main frame ,
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
// main frame web ,
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation;
// main frame ,
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation;
// main frame ,
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
// API, AFN、UIWebView API
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *__nullable credential))completionHandler;
// web content ,
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView NS_AVAILABLE(10_11, 9_0);
@end
typedef NS_ENUM(NSInteger, WKNavigationActionPolicy) {
WKNavigationActionPolicyCancel,
WKNavigationActionPolicyAllow,
} NS_ENUM_AVAILABLE(10_10, 8_0);
WKWebViewとUIWebViewのCookie同期の問題
WKWebViewのCookieメカニズム:
//defaultDataStore
+ (WKWebsiteDataStore *)defaultDataStore;
//nonPersistentDataStore ,
+ (WKWebsiteDataStore *)nonPersistentDataStore;
//
+ (NSSet *)allWebsiteDataTypes;
//
- (void)fetchDataRecordsOfTypes:(NSSet *)dataTypes completionHandler:(void (^)(NSArray *))completionHandler;
- (void)removeDataOfTypes:(NSSet *)dataTypes forDataRecords:(NSArray *)dataRecords completionHandler:(void (^)(void))completionHandler;
- (void)removeDataOfTypes:(NSSet *)websiteDataTypes modifiedSince:(NSDate *)date completionHandler:(void (^)(void))completionHandler;
WKWebsiteDataStoreのWebデータはWKWebsiteDataRecordクラスとして保存されており、fetchDataRecordsOfTypeメソッドでdatastoreのrecordデータを取得できます.しかし、WKWebsiteDataRecordクラスのヘッダファイルを開くと、次のことがわかります.
@interface WKWebsiteDataRecord : NSObject
/*! @abstract The display name for the data record. This is usually the domain name. */
@property (nonatomic, readonly, copy) NSString *displayName;
/*! @abstract The various types of website data that exist for this data record. */
@property (nonatomic, readonly, copy) NSSet *dataTypes;
@end
WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore];
[dataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] completionHandler:^(NSArray * _Nonnull records) {
for (WKWebsiteDataRecord *record in records)
{
NSLog(@"++++++++++++++++%@",[record description]);
}
}];
UIWebViewのCookieメカニズム
UIWebViewは、Webページを参照すると、WebページのクッキーをNSHTTPCookieStorage標準コンテナに自動的に格納します.後続のアクセスでクッキーはrequestリクエストに自動的に持ち込まれます.例えば、NSHTTPCookieStorageにはCookie、name=aが格納されている.value=b;domain=y.qq.com;expires=Sat,02 May 2017 23:20:25 GMT; UIWebViewによってリクエストが開始されますhttp://y.qq.comを選択すると、要求ヘッダは自動的にクッキーを持ち、WKWebViewによって要求が開始され、要求ヘッダは自動的にクッキーを持ちません.
初歩的な解決策
実は主にしなければならないのは2つのステップだけで、1、Cookieを取得して、2、Cookieを注入します
1、Cookieを取得する
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response;
NSArray *cookies =[NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:response.URL];
// wkwebview cookie 1
for (NSHTTPCookie *cookie in cookies) {
// [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
NSLog(@"wkwebview cookie:%@", cookie);
}
// wkwebview cookie 2 Set-Cookie
NSString *cookieString = [[response allHeaderFields] valueForKey:@"Set-Cookie"];
NSLog(@"wkwebview cookie:%@", cookieString);
// NSHTTPCookieStorage
NSHTTPCookieStorage *cookieJar2 = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in cookieJar2.cookies) {
NSLog(@"NSHTTPCookieStorage cookie%@", cookie);
}
decisionHandler(WKNavigationResponsePolicyAllow);
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation
{
[webView evaluateJavaScript:[NSString stringWithFormat:@"document.cookie"] completionHandler:^(id _Nullable response, NSError * _Nullable error) {
if (response != 0) {
NSLog(@"
document.cookie%@,%@",response,error);
}
}];
}
// storage cookie
NSArray *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: @"" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[userContentController addUserScript:cookieScript];
WKWebViewConfiguration* webViewConfig = WKWebViewConfiguration.new;
webViewConfig.userContentController = userContentController;
WKWebView * webView = [[WKWebView alloc] initWithFrame:CGRectMake(/*set your values*/) configuration:webViewConfig];
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
[webView evaluateJavaScript:@"document.cookie ='TeskCookieKey1=TeskCookieValue1';" completionHandler:^(id result, NSError *error) {
//...
}];
}
NSURL *url = request.URL;
NSMutableString *cookies = [NSMutableString string];
NSMutableURLRequest *requestObj = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
NSArray *tmp = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
NSDictionary *dicCookies = [NSHTTPCookie requestHeaderFieldsWithCookies:tmp];
NSString *cookie = [self readCurrentCookie];
[requestObj setValue:cookie forHTTPHeaderField:@"Cookie"];
[_webView loadRequest:requestObj];
-(NSString *)readCurrentCookie{
NSMutableDictionary *cookieDic = [NSMutableDictionary dictionary];
NSMutableString *cookieValue = [NSMutableString stringWithFormat:@""];
NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in [cookieJar cookies]) {
[cookieDic setObject:cookie.value forKey:cookie.name];
}
// cookie , ,
for (NSString *key in cookieDic) {
NSString *appendString = [NSString stringWithFormat:@"%@=%@;", key, [cookieDic valueForKey:key]];
[cookieValue appendString:appendString];
}
return cookieValue;
}
NSMutableURLRequestが注入したPHPなどの動的言語は直接$_からCOOKIEオブジェクトで取得した
(2)app終了後のクッキー紛失の問題
// cookie storage
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(nonnull WKNavigationResponse *)navigationResponse decisionHandler:(nonnull void (^)(WKNavigationResponsePolicy))decisionHandler{
NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response;
NSArray *cookies =[NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:response.URL];
NSString *cookieString = [[response allHeaderFields] valueForKey:@"Set-Cookie"];
for (NSHTTPCookie *cookie in cookies) {
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
}
NSMutableArray *cookieArray = [[NSMutableArray alloc] init];
for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
[cookieArray addObject:cookie.name];
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:[NSNumber numberWithInt:cookie.version] forKey:NSHTTPCookieVersion];
[cookieProperties setObject:[[NSDate date] dateByAddingTimeInterval:2629743] forKey:NSHTTPCookieExpires];
[[NSUserDefaults standardUserDefaults] setValue:cookieProperties forKey:cookie.name];
[[NSUserDefaults standardUserDefaults] synchronize];
}
[[NSUserDefaults standardUserDefaults] setValue:cookieArray forKey:@"cookieArray"];
[[NSUserDefaults standardUserDefaults] synchronize];
// WKWebView Cookie,
WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore];
[dataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes]
completionHandler:^(NSArray * __nonnull records) {
for (WKWebsiteDataRecord *record in records)
{
// if ( [record.displayName containsString:@"baidu"]) // , ,
// {
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:record.dataTypes
forDataRecords:@[record]
completionHandler:^{
NSLog(@"Cookies for %@ deleted successfully",record.displayName);
}];
// }
}
}];
decisionHandler(WKNavigationResponsePolicyAllow);
}
// storage cookie
-(NSString *)readCurrentCookie{
NSMutableArray* cookieDictionary = [[NSUserDefaults standardUserDefaults] valueForKey:@"cookieArray"];
NSLog(@"cookie dictionary found is %@",cookieDictionary);
for (int i=0; i < cookieDictionary.count; i++)
{
NSLog(@"cookie found is %@",[cookieDictionary objectAtIndex:i]);
NSMutableDictionary* cookieDictionary1 = [[NSUserDefaults standardUserDefaults] valueForKey:[cookieDictionary objectAtIndex:i]];
NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieDictionary1];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
}
NSMutableDictionary *cookieDic = [NSMutableDictionary dictionary];
NSMutableString *cookieValue = [NSMutableString stringWithFormat:@""];
NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in [cookieJar cookies]) {
[cookieDic setObject:cookie.value forKey:cookie.name];
}
// cookie , ,
for (NSString *key in cookieDic) {
// if ([key isEqualToString:@"nweb_qa"] || [key isEqualToString:@"z_c0"] || [key isEqualToString:@"_xsrf"]) {
NSString *appendString = [NSString stringWithFormat:@"%@=%@;", key, [cookieDic valueForKey:key]];
[cookieValue appendString:appendString];
// }
}
return cookieValue;
}
締めくくり