WKWebview之Cookie小结

首先来看一下 NSHTTPCookieStorage 这个类:

/*!
    @class NSHTTPCookieStorage 
    @discussion NSHTTPCookieStorage implements a singleton object (shared
    instance) which manages the shared cookie store.  It has methods
    to allow clients to set and remove cookies, and get the current
    set of cookies.  It also has convenience methods to parse and
    generate cookie-related HTTP header fields.
*/
@interface NSHTTPCookieStorage : NSObject

Each stored cookie is represented by an instance of the NSHTTPCookie class.

可以得知这是一个管理cookie的单例容器,并且存储的都是NSHTTPCookie的实例,可以使用这个类来对cookie进行增删改查。

//获取已经保存了的所有cookie
@property (nullable , readonly, copy) NSArray *cookies;

//设置cookie 注意:会覆盖有相同name domain path的cookie
- (void)setCookie:(NSHTTPCookie *)cookie;

//删除cookie
- (void)deleteCookie:(NSHTTPCookie *)cookie;

- (void)removeCookiesSinceDate:(NSDate *)date API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));

//返回指定url的cookies
- (nullable NSArray *)cookiesForURL:(NSURL *)URL;
//这个方法我们可以使用NSHTTPCookie的一个方法将这个返回的数组转化成请求头所需的字典(下面会提到)

然后再来看一下 NSHTTPCookie 这个类:

/*!
    @class NSHTTPCookie
    @abstract NSHTTPCookie represents an http cookie.
    @discussion A NSHTTPCookie instance represents a single http cookie. It is
    an immutable object initialized from a dictionary that contains
    the various cookie attributes. It has accessors to get the various
    attributes of a cookie.
*/
@interface NSHTTPCookie : NSObject

An NSHTTPCookie object is immutable, initialized from a dictionary containing the attributes of the cookie. This class supports two different cookie versions:

  • Version 0: The original cookie format defined by Netscape.Most cookies are in this format.

  • Version 1: The cookie format defined in RFC 6265, HTTP State Management Mechanism.

每一个NSHTTPCookie实例对象是不可变的,代表了一个http cookie。只能从key包含了cookie特有属性的字典中实例化。支持这两种cookie。

- (nullable instancetype)initWithProperties:(NSDictionary *)properties;

+ (nullable NSHTTPCookie *)cookieWithProperties:(NSDictionary *)properties;

/*会返回一个autoreleased的NSHTTPCookie实例,如果字典不含cookie必要的key或者key对应的值是非法的,则会返回nil*/


+ (NSDictionary *)requestHeaderFieldsWithCookies:(NSArray *)cookies;

/*@discussion This method will ignore irrelevant header fields so
you can pass a dictionary containing data other than cookie data.
*/
+ (NSArray *)cookiesWithResponseHeaderFields:(NSDictionary *)headerFields forURL:(NSURL *)URL;

我们可以先创建一个字典(key可以在),然后用字典生成我们需要的cookie:

FOUNDATION_EXPORT NSHTTPCookiePropertyKey const NSHTTPCookieName;
FOUNDATION_EXPORT NSHTTPCookiePropertyKey const NSHTTPCookieValue;
FOUNDATION_EXPORT NSHTTPCookiePropertyKey const NSHTTPCookieVersion;
FOUNDATION_EXPORT NSHTTPCookiePropertyKey const NSHTTPCookieDomain;
FOUNDATION_EXPORT NSHTTPCookiePropertyKey const NSHTTPCookiePath;
FOUNDATION_EXPORT NSHTTPCookiePropertyKey const NSHTTPCookieSecure;
FOUNDATION_EXPORT NSHTTPCookiePropertyKey const NSHTTPCookieExpires;
...
//可以在NSHTTPCookie.h文件中查看更多key
//1.创建cookie字典
NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
[cookieProperties setObject:cookieInfo.cookieName forKey:NSHTTPCookieName];
[cookieProperties setObject:cookieInfo.cookieValue forKey:NSHTTPCookieValue];
[cookieProperties setObject:cookieInfo.cookieDomain forKey:NSHTTPCookieDomain];
[cookieProperties setObject:@"/" forKey:NSHTTPCookiePath];
[cookieProperties setObject:@"0" forKey:NSHTTPCookieVersion];

//2.生成NSHTTPCookie对象
NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieProperties];
    
//3.保存cookie
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];

cookie的保存&使用流程:

一般当我们用wkwebview发起一个http请求时,服务器会将生成的cookie通过响应头Reponse的header返回到app端,我们此时可以将cookie交给NSHTTPCookieStorage单例容器保存,或者可以自己来保存起来。等下次访问的时候,将保存起来的cookie取出来放在http的请求头上,这样服务器就可以通过我们设置的cookie来识别我们。

cookie的获取:

wkwebview有个代理方法这里面可以获得响应头:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
//方式1
    NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)navigationResponse.response;
//这里是将NSURlResponse强制转化为NSHTTPURLResponse 
//因为我们发起的是http请求 并且 NSHTTPURLResponse 是 NSURlResponse的子类
    NSDictionary* dict = httpResponse.allHeaderFields;
    NSString* cookiesStr = [dict valueForKey:@"Set-Cookie"];

//方式2    
    NSArray* array = [NSHTTPCookie cookiesWithResponseHeaderFields:httpResponse.allHeaderFields forURL:httpResponse.URL];
    for (NSHTTPCookie * cookie in array) {
        //保存到sharedHTTPCookieStorage中
        [[NSHTTPCookieStorage sharedHTTPCookieStorage]setCookie:cookie];
    }

}

根据实例情况和 参考文档 发现

业界普遍认为 WKWebView 拥有自己的私有存储,不会将 Cookie 存入到标准的 Cookie 容器 NSHTTPCookieStorage 中,实践发现 WKWebView 实例其实也会将 Cookie 存储于 NSHTTPCookieStorage 中,但存储时机有延迟。

WKWebView Cookie 问题在于 WKWebView 发起的请求不会自动带上存储于 NSHTTPCookieStorage 容器中的 Cookie

这就造成当我们使用WKWebview访问一个网页的时候,不会自动带上我们已经保存了的cookie,需要我们手动来设置。

需要在执行 [WKWebView loadReques:] 前将 NSHTTPCookieStorage中的cookie内容放到wkwebview的请求中,以此来达到 WKWebView cookie注入的目的。

cookie的设置:

方式1:在请求头上添加

NSString* cookiesStr = [dict valueForKey:@"Set-Cookie"];

在上面响应头上获得到的cookie ,可以直接赋值到request的header上,

NSMutableURLRequest* mutableRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30];
[mutableRequest setValue:cookiesStr forHTTPHeaderField:@"Cookie"];

方式2:构建多个NSHTTPCookie实例对象的数组,根据NSHTTPCookie实例数组生成对应的HTTP cookieheader,设置cookierequestheader中。

NSDictionary *properties1 = [NSDictionary dictionaryWithObjectsAndKeys:
                                @"domain.com", NSHTTPCookieDomain,
                                @"/", NSHTTPCookiePath,
                                @"Name1", NSHTTPCookieName,
                                Value, NSHTTPCookieValue,
                                nil];
    
NSDictionary *properties2 = [NSDictionary dictionaryWithObjectsAndKeys:
                                 @"domain.com", NSHTTPCookieDomain,
                                 @"/", NSHTTPCookiePath,
                                 @"Name2", NSHTTPCookieName,
                                 Value2, NSHTTPCookieValue,
                                 nil];
NSHTTPCookie *cookie1 = [NSHTTPCookie cookieWithProperties:properties1];
NSHTTPCookie *cookie2 = [NSHTTPCookie cookieWithProperties:properties2];
NSArray* cookies = [NSArray arrayWithObjects: cookie1, cookie2, nil];
//根据NSHTTPCookie实例数组生成对应的HTTP cookie header
NSDictionary * headers = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
//设置cookie到header中
request.allHTTPHeaderFields = headers;

方式3: 

NSString *cookieStr = [self cookieStrWithCookies:[NSHTTPCookieStorage sharedHTTPCookieStorage].cookies];
WKUserScript * cookieScript = [[WKUserScript alloc]initWithSource:cookieStr injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];

WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
WKUserContentController *userContentController = [[WKUserContentController alloc] init];
[userContentController addUserScript:cookieScript];
configuration.userContentController = userContentController;
_webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];

- (NSString *)cookieStrWithCookies:(NSArray *)cookies {
    NSMutableString *result = [NSMutableString string];
    for (NSHTTPCookie *cookie in cookies) {
        if (![cookie.domain isEqualToString:@".domain.cn"]) {
            continue;
        }
        //这里需要根据实际情况来配置字符串
        NSString *temp = [NSString stringWithFormat:@"document.cookie = '%@=%@; domain=%@; path=%@; expires=%@; %@=%zd';", cookie.name, cookie.value, cookie.domain, cookie.path, cookie.expiresDate, NSHTTPCookieVersion, cookie.version];
        [result appendString:temp];
    }
    return result;
}

iOS11之后,更加方便:

ios11之后,推出了WKHTTPCookieStore专门用来管理wkwebview的cookie

@interface WKHTTPCookieStore : NSObject
An object that manages the HTTP cookies associated with a particular WKWebsiteDataStore.
NSArray* cookiesArr = [NSHTTPCookieStorage sharedHTTPCookieStorage].cookies;
if (@available(iOS 11.0, *)) {
      WKHTTPCookieStore* wkhttpStore = configuration.websiteDataStore.httpCookieStore;;
      for (NSHTTPCookie* coo in cookiesArr) {
          [wkhttpStore setCookie:coo completionHandler:^{ }];
      }
  } else {
      // Fallback on earlier versions
}

可以这样很方便地给wkwebview设置cookie了。

 

参考1

参考2

参考3

参考4

你可能感兴趣的:(iOS)