HTTP网络请求Cookie的读取与写入(NSHTTPCookieStorage)

原文

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

认识下NSHTTPCookieStorage

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

Cookie

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

HTTP header
HTTP header中包含HTTP请求与响应的操作参数. header属性定义了所传输数据的各种特性. header属性以属性名开始,以冒号结尾,最后是属性值.属性名及值会因应用的不同

一.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);
    }
    }];
    }
  • 2.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
NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookieArray = [NSArray arrayWithArray:[cookieJar cookies]];
for (NSHTTPCookie *obj in cookieArray) {
[cookieJar deleteCookie:obj];
}

三.手动设置Cookie 手动设置的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
第一次请求手动设置个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];
[cookieProperties setObject:[NSDate dateWithTimeIntervalSinceNow:60*60] forKey:NSHTTPCookieExpires];//设置失效时间
[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.服务器端设置Cookie,以PHP为例

语法

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

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

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

使用终端执行 BinaryCookieReader.py脚本 解析 org.skyfox.iOS-Cookie.binarycookies 结果如下:

1
<NSHTTPCookie version:0 name:"TestCookie" value:"my+cookie+value" expiresDate:(null) created:2016-04-08 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);
}
}

DEMO

iOS-Cookie