Henry的博客

有理想的人,生活总是火热的

欢迎来到Henry的博客,希望与您在iOS开发领域共同交流与学习


iOS开发-浅谈webViewCookie机制

当你访问一个网站时,浏览器都会帮你主动记录下来你访问的站点设置的Cookie,如果 Cookie 存在的话,会把这些信息放在 CookieStorage 容器中共享,当你下次再访问这个站点时,浏览器会拿着上次保存下来了的Cookie继续去请求。同样适用于ASIHTTPRequest,AFNetworking, Webview等,Cookie常用于一些基于认证的网络请求

Cookie简介

Cookie是由服务器端生成,发送给User-Agent(一般是浏览器或者客户端),浏览器会将Cookie的key/value保存到某个目录下的文本文件内,下次请求同一网站地址时就发送该Cookie给服务器。Cookie分类两类:

  • 会话cookie
  • 持久cookie

会话 cookie 和持久 cookie 之间唯一的区别就是它们的过期时间。至于cookie会不会持久化到cookie文件中主要看这个cookie的生命周期,和Max-Age或者Expires有关,后面会讲到。

Cookie的属性:

  • name:一个唯一确定的cookie名称。通常来讲cookie的名称是不区分大小写的。
  • value:存储在cookie中的字符串值。最好为cookie的name和value进行url编码
  • domain:cookie对于哪个域是有效的。所有向该域发送的请求中都会包含这个cookie信息。这个值可以包含子域(如: yq.aliyun.com),也可以不包含它(如:.aliyun.com,则对于aliyun.com的所有子域都有效).
  • path: 表示这个cookie影响到的路径,浏览器跟会根据这项配置,像指定域中匹配的路径发送cookie。
  • expires:失效时间,表示cookie何时应该被删除的时间戳(也就是,何时应该停止向服务器发送这个cookie)。如果不设置这个时间戳,浏览器会在页面关闭时即将删除所有cookie;不过也可以自己设置删除时间。这个值是GMT时间格式,如果客户端和服务器端时间不一致,使用expires就会存在偏差
  • max-age: 与expires作用相同,用来告诉浏览器此cookie多久过期(单位是秒),而不是一个固定的时间点。正常情况下,max-age的优先级高于expires。
  • HttpOnly: 告知浏览器不允许通过脚本document.cookie去更改这个值,同样这个值在document.cookie中也不可见。但在http请求张仍然会携带这个cookie。注意这个值虽然在脚本中不可获取,但仍然在浏览器安装目录中以文件形式存在。这项设置通常在服务器端设置。
  • secure: 安全标志,指定后,只有在使用SSL链接时候才能发送到服务器,如果是http链接则不会传递该信息。就算设置了secure 属性也并不代表他人不能看到你机器本地保存的 cookie 信息,所以不要把重要信息放cookie就对了服务器端设置。

如何查看Cookie:

浏览器打开百度,按F12

可以看到响应头里有个Set-Cookie字段,这个是由服务端设置的cookie,在iOS端会根据cookie的生命周期来自动存储到NSHTTPCookieStorage中,在下次请求时会根据相应的URL domain自动带上相应的Cookie

认识下NSHTTPCookieStorage

NSHTTPCookieStorage 实现了一个管理cookie的单例对象(只有一个实例),每个Cookie都是NSHTTPCookie类的实例,最为一个规则,Cookie在所有应用之间共享并在不同进程之间保持同步。Session Cookie(一个isSessionOnly方法返回YES的Cookie)只能在单一进程中使用。

iOS htttp网络请求Cookie的读取与写入:

Cookie必然会通过HTTP的Respone传过来,并且Cookie在Respone中的HTTP header中。不管是什么请求框架,必然会存在Respone对象,比如AFNetworking2.x的operation.response,AFNetworking3.x的task.response等等。。。。

原生NSURLConnection写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
一.获取cookie
- (IBAction)cookieTouched:(id)sender {
NSURL *url = [NSURL URLWithString:@"http://api.skyfox.org/api-test.php"];
NSURLRequest *request = [NSURLRequest requestWithURL:url]
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:3];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[NSURLConnection sendAsynchronousRequest:request
queue:queue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
//转换NSURLResponse成为HTTPResponse
NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse *)response;
//获取headerfields
NSDictionary *fields = [HTTPResponse allHeaderFields];//原生NSURLConnection写法
// NSDictionary *fields = [operation.response allHeaderFields]; //afnetworking写法
NSLog(@"fields = %@",[fields description]);
//获取cookie方法1
// NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:fields forURL:url];
//获取cookie方法2
//NSString *cookieString = [[HTTPResponse allHeaderFields] valueForKey:@"Set-Cookie"];
//获取cookie方法3
NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in [cookieJar cookies]) {
NSLog(@"cookie%@", cookie);
}
}];
}

AFNetworking 写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.responseSerializer = [AFCompoundResponseSerializer serializer];
//demo中的api返回的是html数据,不是json
[manager POST:@"http://dev.skyfox.org/cookie.php" parameters:nil progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"\n======================================\n");
NSDictionary *fields = ((NSHTTPURLResponse*)task.response).allHeaderFields;
NSLog(@"fields = %@",[fields description]);
NSURL *url = [NSURL URLWithString:@"http://dev.skyfox.org/cookie.php"];
NSLog(@"\n======================================\n");
//获取cookie方法1
NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:fields forURL:url];
for (NSHTTPCookie *cookie in cookies) {
NSLog(@"cookie,name:= %@,valuie = %@",cookie.name,cookie.value);
}
NSLog(@"\n======================================\n");
// //获取cookie方法2
// NSString *cookies2 = [((NSHTTPURLResponse*)task.response) valueForKey:@"Set-Cookie"];
// NSLog(@"cookies2 = %@",[cookies2 description]);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];

手动设置Cookie

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//第一次请求手动设置个cookie
-(void)test1:(NSString*)urlString{
NSURL *url = [NSURL URLWithString:@"http://dev.skyfox.org/cookie.php"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
[cookieProperties setObject:@"username" forKey:NSHTTPCookieName];
[cookieProperties setObject:@"my ios cookie" forKey:NSHTTPCookieValue];
[cookieProperties setObject:@"dev.skyfox.org" forKey:NSHTTPCookieDomain];
[cookieProperties setObject:@"dev.skyfox.org" forKey:NSHTTPCookieOriginURL];
[cookieProperties setObject:@"/" forKey:NSHTTPCookiePath];
[cookieProperties setObject:@"0" forKey:NSHTTPCookieVersion];
//注意:Expires是HTTP 1.0标准缓存控制,不建议使用,请使用Cache-Control:max-age代替
//[cookieProperties setObject:[NSDate dateWithTimeIntervalSinceNow:60*60] forKey:NSHTTPCookieExpires];//HTTP 1.0标准缓存控制,不建议使用
[cookieProperties setObject:@"30000" forKey:NSHTTPCookieMaximumAge];//设置最大生命周期 设置后会持久化cookie直到生命周期结束
//[cookieProperties setObject:@"0" forKey:NSHTTPCookieDiscard]; //设置sessionOnly
NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieProperties];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
[self.myWebView loadRequest:request];
}
//第二次请求会自动带上Cookie
- (IBAction)test2:(id)sender {
NSURL *url = [NSURL URLWithString:@"http://dev.skyfox.org/cookie.php"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[self.mywebview2 loadRequest:request];
}

request还可以这样设置个Cookie

1
2
[request setHTTPShouldHandleCookies:YES];
[request setValue:[NSString stringWithFormat:@"%@=%@", [cookie name], [cookie value]] forHTTPHeaderField:@"Cookie"];

Cookie的本地缓存策略

1
2
3
4
//NSHTTPCookieAcceptPolicyAlways:保存所有cookie,这个是默认值
//NSHTTPCookieAcceptPolicyNever:不保存任何响应头中的cookie
//NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain:只保存域请求匹配的cookie
[[NSHTTPCookieStorage sharedHTTPCookieStorage]setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyNever];

清空Cookie

1
2
3
4
5
NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookieArray = [NSArray arrayWithArray:[cookieJar cookies]];
for (NSHTTPCookie *obj in cookieArray) {
[cookieJar deleteCookie:obj];
}

Cookie的持久化存储

1.服务器端设置Cookie,以PHP为例

1
2
setcookie("TestCookie","my cookie value"); //没设置失效时间 关闭app后系统不会持久化Cookie
setcookie("TestCookie","my cookie value",time()+3600*24); //设置expire失效时间 关闭app后系统自动持久化Cookie

如果服务器设置了Cookie失效时间expiresDate ,sessionOnly:FALSE会被持久化到文件中,kill掉后系统自动保存,下次启动app会自动加载.binarycookies中的Cookies,以下是一条Cookie输出

1
<NSHTTPCookie version:0 name:"TestCookie" value:"my+cookie+value" expiresDate:2017-06-15 09:31:09 +0000 created:2017-06-15 09:30:49 +0000 sessionOnly:FALSE domain:"dev.skyfox.org" path:"/" isSecure:FALSE>

持久化到了文件中,地址是 沙盒的 /Library/Cookies/org.skyfox.iOS-Cookie.binarycookies

2.app端手动存储Cookie

如果没置 NSHTTPCookieMaximumAge 或者Cookie失效时间expiresDate:(null),sessionOnly:true,kill掉后系统不会自动保存Cookie,如果想持久化Cookie 模仿浏览器存住Cookie,使用NSUserDefaults存下即可,以下是一条Cookie输出

1
<NSHTTPCookie version:0 name:"TestCookie" value:"my+cookie+value" expiresDate:(null) created:2017-06-15 09:33:34 +0000 sessionOnly:TRUE domain:"dev.skyfox.org" path:"/" isSecure:FALSE>

手动保存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//合适的时机持久化Cookie
- (void)saveCookies{
NSData *cookiesData = [NSKeyedArchiver archivedDataWithRootObject: [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject: cookiesData forKey:@"org.skyfox.cookiesave"];
[defaults synchronize];
}
//合适的时机加载持久化后Cookie 一般都是app刚刚启动的时候
- (void)loadSavedCookies{
NSArray *cookies = [NSKeyedUnarchiver unarchiveObjectWithData: [[NSUserDefaults standardUserDefaults] objectForKey: @"org.skyfox.cookiesave"]];
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in cookies){
NSLog(@"cookie,name:= %@,valuie = %@",cookie.name,cookie.value);
[cookieStorage setCookie: cookie];
}
}

对于同名cookie后者会覆盖前者

demo地址

最近的文章

iOS开发-RunLoop实例应用

之前看过很多有关RunLoop的文章,其中要么是主要介绍RunLoop的基本概念,要么是主要讲解RunLoop的底层原理,很少用真正的实例来讲解RunLoop的,这其中有大部分原因是由于大家在项目中很少能用到RunLoop吧。基于这种原因,本文中将用很少的篇幅来对基础内容做以介绍,然后主要利用实例来 …

于  iOS开发 继续阅读
更早的文章

iOS开发-Safari调试WebView页面

在iOS开发过程中,难免会加入html5页面来实现文章详情等等类似功能。我们都知道火狐等PC浏览器有类似firebug,审查元素等工具来调试网页样式与脚本,查看请求参数与请求头等等。在iOS开发中,这些网页检查器功能也是存在的(无论是模拟器还是真机),我们需要用到Mac自带的浏览器Safari。所以 …

于  iOS开发 继续阅读