iOS NSHTTPCookie&&NSHTTPCookieStorage 小结

前言

由于HTTP协议的无状态,客户端经常使用cookie来提供跨URL请求的数据持久存储。URL加载系统提供了创建和管理cookie的接口,作为HTTP请求的一部分发送cookies,并在Web服务器的响应时接收cookie

正常业务场景下,cookie最先是由服务器生成好
然后客服端请求接口,获取到cookie,将cookie存储起来。
最后,在每次网络请求的时候附带cookie

但是在iOS网络请求中使用cookie还有1个条件,那就是在iOS中网络请求类NSURLRequest中设置是否要使用cookie


/*
决定这个请求是否要使用cookie,默认为YES
*/
@property BOOL HTTPShouldHandleCookies;

iOS 中cookie是一个NSHTTPCookie对象,它包含了各种各样的属性(properties)

// cookie 版本
// 版本0:此版本是指由Netscape定义的原始cookie格式的“传统”或“旧式”cookie。遇到的大多数Cookie都是这种格式。
// 版本1:此版本是指RFC 2965(HTTP状态管理机制)中定义的Cookie。
@property (readonly) NSUInteger version;

// cookie存储信息的名字,比如:token
@property (readonly, copy) NSString *name;

// cookie存储的信息,比如:8d2je219jjd0120d12e1212e12(token的值)
@property (readonly, copy) NSString *value;

// cookie有效期(过期,NSHTTPCookieStorage会自动删除存储的cookie)
@property (nullable, readonly, copy) NSDate *expiresDate;

// 是否应在会话结束时被丢弃(不管过期日期如何)
@property (readonly, getter=isSessionOnly) BOOL sessionOnly;

// cookie的域名
@property (readonly, copy) NSString *domain;

// 路径
@property (readonly, copy) NSString *path;

// 该cookie是否应该仅通过安全通道发送
@property (readonly, getter=isSecure) BOOL secure;

// 是否应仅根据RFC 2965发送到HTTP服务器
@property (readonly, getter=isHTTPOnly) BOOL HTTPOnly;

// 端口列表
@property (nullable, readonly, copy) NSArray *portList;

平时开发中,在用户登录时服务器会将当前用户的token等相关信息存在cookie中返回给客户端,客户端在每次请求其他数据时都需要将此cookie信息携带,以便区分当前是哪个用户。为了接受这个cookie, iOS 中提供了NSHTTPCookieStorage这个类,来存储服务器给我们发送的cookie,NSURLResponse根据当前的NSHTTPCookieStorage接受策略自动接受返回的cookie并存储NSHTTPCookieStorage中,我们不需要做任何操作,在我们发送请求时,我们只需要设置HTTPShouldHandleCookies为YES(默认为YES),NSURLRequest会自动附带cookie的信息发送给服务器
NSHTPCookieStorage 对象是一个单列对象,它管理着所有的cookie,它提供了一些方法来允许客户端设置和移除cookie,以及获取当前的cookie设置

通过单例获取NSHTTPCookieStorage对象

NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];

NSHTPCookieStorage 设置、删除、获取


// cookie的接收策略
@property NSHTTPCookieAcceptPolicy cookieAcceptPolicy

// 获取NSHTTPCookieStorage存储的所有cookie
@property (nullable , readonly, copy) NSArray *cookies

// 设置cookie
- (void)setCookie:(NSHTTPCookie *)cookie

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

// 在某个时间点删除cookies
- (void)removeCookiesSinceDate:(NSDate *)date

// 获取指定URL的cookies
- (nullable NSArray *)cookiesForURL:(NSURL *)URL

// 获取指定域名指定URL的cookies
- (void)setCookies:(NSArray *)cookies forURL:(nullable NSURL *)URL mainDocumentURL:(nullable NSURL *)mainDocumentURL

// 用户退出登录, 清除cookie
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
    for (NSHTTPCookie *cookie in cookies) {
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
    }


API
NSHTTPCookie 相关
+ (NSDictionary *)requestHeaderFieldsWithCookies:(NSArray *)cookies;

根据NSHTTPCookie实例数组生成对应的HTTP cookie header

+ (NSArray *)cookiesWithResponseHeaderFields:(NSDictionary *)headerFields forURL:(NSURL *)theURL;

从headerFileds中读取到Cookie相关内容,生成NSHTTPCookie实例对象数组,该方法会忽略headerFileds中与cookie无关的字段,如果headerFileds中的cookie没有指定domain,则使用theURL的domain,如果没有指定path,则使用”/”.除非NSURLRequest明确指定不使用cookie(HTTPShouldHandleCookies设为NO),否则URL loading system会自动为NSURLRequest发送合适的存储cookie。从NSURLResponse返回的cookie也会根据当前的cookie访问策略(cookie acceptance policy)接收到系统中

NSHTTPCookieStorage相关

NSHTTPCookieStorage 提供了管理所有NSHTTPCookie对象的接口在OSX里,cookie是所有程序中共享的,而在iOS中,cookie只在当前应用中有效

+ (NSHTTPCookieStorage *)sharedHTTPCookieStorage;

获取共享的NSHTTPCookieStorage单例对象

- (NSArray *)cookies

获取当前存储的所有cookie


- (NSArray *)cookiesForURL:(NSURL *)theURL

获取特定URL的cookie

- (void)setCookie:(NSHTTPCookie *)cookie;

- (void)deleteCookie:(NSHTTPCookie *)cookie;

添加/删除cookie

- (void)setCookies:(NSArray *)cookies forURL:(nullable NSURL *)URL mainDocumentURL:(nullable NSURL *)mainDocumentURL;

通过NSHTTPCookieStorage可读取、修改cookie接受策略,默认为NSHTTPCookieAcceptPolicyAlways

- (NSHTTPCookieAcceptPolicy)cookieAcceptPolicy;

- (void)setCookieAcceptPolicy:(NSHTTPCookieAcceptPolicy)aPolicy.

一共有三种cookie accept policy,以下是三种接受策略


typedef NS_ENUM(NSUInteger, NSHTTPCookieAcceptPolicy) {
    // 永远接收Cookie,这种情况下,NSHTTPCookieStorage会将接收到的cookie 存储在偏好设置中
    NSHTTPCookieAcceptPolicyAlways,
    // 永远不接受Cookie,这种情况下,NSHTTPCookieStorage不会存储cookie到本地
    NSHTTPCookieAcceptPolicyNever,
    // 只接收指定域名的Cookie
    NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain
};

FOUNDATION_EXPORT NSNotificationName const NSHTTPCookieManagerCookiesChangedNotification;

当NSHTTPCookieStorage实例中的cookies变化时发出此通知


FOUNDATION_EXPORT NSNotificationName const NSHTTPCookieManagerAcceptPolicyChangedNotification;

当NSHTTPCookieStorage实例的cookie acceptance policy 变化时发出此通知

实际应用
用户登录

- (IBAction)login:(id)sender {
    
    //保存NSHTTPCookieStorage对象里的所有cookie,当然也可以使用cookieForUrl来获取cookie进行保存,方便后续的使用。此操作是在登录的时候进行。
    
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSArray *allCookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
    
    for (NSHTTPCookie *cookie in allCookies)
    {
        if ([cookie.name isEqualToString:kServerSessionCookie])
        {
            NSMutableDictionary *cookieDictionary = [NSMutableDictionary dictionaryWithDictionary:[defaults dictionaryForKey:kLocalCookieName]];
            [cookieDictionary setValue:cookie.properties forKey:@"cookieDict"];
            [defaults setObject:cookieDictionary forKey:kLocalCookieName];
            [defaults synchronize];
            break;
        }
        
        NSLog(@"----cookie.name>>>>>---%@", cookie.name);

    }
    
    NSLog(@"----allcookies>>>>>---%@", allCookies);
    
    NSLog(@"--login--allcookies>>>>>---%lu", (unsigned long)allCookies.count);
    
    //[defaults objectForKey:@"kLocalCookieName"];
    //NSLog(@"-----login-----%@", [defaults objectForKey:@"kLocalCookieName"]);
    
    
}

- (IBAction)update:(id)sender {
    
    
    //更新cookie。在程序每次启动的时候调用一下。用来确保每次的cookie是最新的
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSDictionary *cookieDict = [defaults dictionaryForKey:kLocalCookieName];
    NSDictionary *cookiePropeties = [cookieDict valueForKey:@"cookieDict"];
    
    if (cookiePropeties != nil)
    {
        NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookiePropeties];
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
        
    }
    
    NSLog(@"----update----------");
}

- (IBAction)logout:(id)sender {
    
    //移除保存的cookie。因为你在退出账户的时候需要重新获取新的cookie,
    //需要在退出账户的时候清空你上次保存的cookie.
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults removeObjectForKey:kLocalCookieName];
    [defaults synchronize];
    
    NSLog(@"----logout----------");
    
}

cookie与Html5交互
UIWebView

UIWebView使用时间较长,只要在cookieStorage中设置了相应的cookie,每次就会自动带上;但是这样的弊端是随着与H5的交互增加,Cookie占用的空间越来越大,每次交互都夹带大量的cookie,不仅增加服务器端压力,也浪费用户的流量。比如每次交互都夹带5kb的cookie内容,但是真正用到的只有两三百字节


// 工厂类中存储cookie的方法
+ (void)saveCookies
{
    // 创建一个可变字典存放cookie
    NSMutableDictionary *fromappDict = [NSMutableDictionary dictionary];
    [fromappDict setObject:@"fromapp" forKey:NSHTTPCookieName];
    [fromappDict setObject:@"ios" forKey:NSHTTPCookieValue];
    // kDomain是公司app网址
    [fromappDict setObject:kDomain forKey:NSHTTPCookieDomain];
    [fromappDict setObject:kDomain forKey:NSHTTPCookieDomain];
    [fromappDict setObject:@"/" forKey:NSHTTPCookiePath];
    [fromappDict setObject:@"0" forKey:NSHTTPCookieVersion];
    
    // 将可变字典转化为cookie
    NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:fromappDict];
    
    // 获取cookieStorage
    NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    
    // 存储cookie
    [cookieStorage setCookie:cookie];
}

WKWebView

WKWebView相比于UIWebView, 速度快了一倍,内存却减少为原来的一半.cookie不再是自动携带,需要手动设置;交互更加顺畅,比如app底部四个tabBar也都是网页的,在UIWebView下点击,整个H5页面都会闪白一下,但是在WKWebView下点击,四个tabBar效果与原生app效果更加类似,不会有闪白现象。增减了一些代理方法,更方便的进行协议拦截和进度条展示.

在创建的时候存放到WKUserScript中进行添加cookie, 代码如下:

#pragma mark - 在创建的时候存放到WKUserScript中进行添加cookie
- (void)_testWKUserScript
{

    WKWebViewConfiguration *webConfig = [[WKWebViewConfiguration alloc] init];
    // 设置偏好设置
    webConfig.preferences = [[WKPreferences alloc] init];
    // 默认为0
    webConfig.preferences.minimumFontSize = 10;
    // 默认认为YES
    webConfig.preferences.javaScriptEnabled = YES;
    // 在iOS上默认为NO,表示不能自动通过窗口打开
    webConfig.preferences.javaScriptCanOpenWindowsAutomatically = NO;
    
    // web内容处理池
    webConfig.processPool = [[WKProcessPool alloc] init];
    // 将所有cookie以document.cookie = 'key=value';形式进行拼接

    //MARK:-#warning 然而这里的单引号一定要注意是英文的,不要问我为什么告诉你这个(手动微笑)
    NSString *cookieValue = @"document.cookie = 'fromapp=ios';document.cookie = 'channel=appstore';";
    
    // 加cookie给h5识别,表明在ios端打开该地址
    WKUserContentController* userContentController = WKUserContentController.new;
    
    WKUserScript *script = [[WKUserScript alloc] initWithSource:cookieValue injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
    
    [userContentController addUserScript:script];
    
    webConfig.userContentController = userContentController;
    
    //WKWebView *wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height) configuration:webConfig];
    
    WKWebView *wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, kScreenH - 200, 200, 200) configuration:webConfig];
    
    
    [self.view addSubview:wkWebView];
    wkWebView.backgroundColor = [UIColor greenColor];
    
    wkWebView.UIDelegate = wkWebView;
    
    wkWebView.navigationDelegate = wkWebView;
    
    
}

加载某个url的时候添加cookie,如果WKWebView在加载url的时候需要添加cookie,需要先手动获取当前NSHTTPCookieStorage中的所有cookie,然后将cookie放到NSMutableURLRequest请求头中


- (void)loadRequestWithUrlString:(NSString *)urlString
{
    
    // 在此处获取返回的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) {
        NSString *appendString = [NSString stringWithFormat:@"%@=%@;", key, [cookieDic valueForKey:key]];
        [cookieValue appendString:appendString];
    }
    
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
    [request addValue:cookieValue forHTTPHeaderField:@"Cookie"];
    
    [self loadRequest:request];
    
}

- (void)loadRequest:(NSURLRequest *)request
{
    
    //NSURL *url = [NSURL URLWithString:@"https://kyfw.12306.cn/otn/leftTicket/init"];
    //NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
    [task resume];
}

参考链接
https://www.jianshu.com/p/f61834025588
https://www.jianshu.com/p/d2c478bbcca5

你可能感兴趣的:(iOS NSHTTPCookie&&NSHTTPCookieStorage 小结)