前言
由于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