使用方法示例:
发送请求
- 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];
接收处理请求:
- NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
- if (!error) {
- NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
- // 解析 HTTP Response Header,存储cookie
- [[HTTPDNSCookieManager sharedInstance] handleHeaderFields:[httpResponse allHeaderFields] forURL:url];
- }
- }];
- [task resume];
通过 document.cookie 设置 Cookie 解决后续页面(同域)Ajax、iframe 请求的 Cookie 问题;
- WKUserContentController* userContentController = [WKUserContentController new];
- WKUserScript * cookieScript = [[WKUserScript alloc] initWithSource: @"document.cookie = 'skey=skeyValue';" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
- [userContentController addUserScript:cookieScript];
Cookie包含动态 IP 导致登陆失效问题
关于COOKIE失效的问题,假如客户端登录 session 存在 COOKIE,此时这个域名配置了多个IP,使用域名访问会读对应域名的COOKIE,使用IP访问则去读对应IP的COOKIE,假如前后两次使用同一个域名配置的不同IP访问,会导致COOKIE的登录session失效,
如果APP里面的webview页面需要用到系统COOKIE存的登录session,之前APP所有本地网络请求使用域名访问,是可以共用COOKIE的登录session的,但现在本地网络请求使用httpdns后改用IP访问,导致还使用域名访问的webview读不到系统COOKIE存的登录session了(系统COOKIE对应IP了)。IP直连后,服务端返回Cookie包含动态 IP 导致登陆失效。
使用IP访问后,服务端返回的cookie也是IP。导致可能使用对应的域名访问,无法使用本地cookie,或者使用隶属于同一个域名的不同IP去访问,cookie也对不上,导致登陆失效,是吧。
我这边的思路是这样的,
- 应该得干预cookie的存储,基于域名。
- 根源上,api域名返回单IP
第二种思路将失去DNS调度特性,故不考虑。第一种思路更为可行。
基于 iOS11 API WKHTTPCookieStore 来解决 WKWebView 的 Cookie 管理问题
当每次服务端返回cookie后,在存储前都进行下改造,使用域名替换下IP。之后虽然每次网络请求都是使用IP访问,但是host我们都手动改为了域名,这样本地存储的 cookie 也就能对得上了。
代码演示:
在网络请求成功后,或者加载网页成功后,主动将本地的 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];
- }];
- }];
- }
- }];
- }];
- }
iOS11中也提供了对应的 API 供我们来处理替换 Cookie 的时机,那就是下面的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];
- }
-updateWKHTTPCookieStoreDomainFromIP 方法的实现,在上文已经给出。
这个方案需要客户端维护一个IP —> HOST的映射关系,需要能从 IP 反向查找到 HOST,这个维护成本还时挺高的。下面介绍下,更通用的方法,也是iOS11 之前的处理方法: