iOS下的cookies使用

今天无意间翻到了以前有关 cookies 的代码,就顺便整理了一下,和大家分享下。

Cookies 简介

这里有比较详细的介绍,本文就不再赘述了。click me

在 iOS 当中需要注意以下几点:

  1. 不管是 NSURLConnection 还是 UIWebView 都会保留并传递服务端的 cookie,就是说基本所有的网络请求都会使用 cookie
  2. 多个应用之间默认不共享 cookie
  3. 临时 cookie 在应用重启之后就会消失
  4. 持久 cookie 在应用或系统重启之后不会消失
  5. cookie 不能跨域,像.baidu.com 和 image.baidu.com 这种也是跨域
  6. WKWebView 因为没有缓存,不能使用 cookie,想要使用,需做特殊处理。

注:cookie 不能跨域是通用的,在所有情况下都是这样,并非在 iOS 系统上如此。.baidu.com 和 image.baidu.com 可以使用彼此的 cookie 是因为服务端做了特殊的处理。

Cookies 操作

iOS 系统当中的 cookies 由系统底层实现和使用,同时提供了NSHTTPCookieStorageNSHTTPCookie来统一操作 cookie。NSHTTPCookieStorage是一个单例,我们所有的 cookies 都存放在这里,当我们发起网络请求时,系统会默认从NSHTTPCookieStorage当中匹配相应 cookie 并带上去,所以我们只需要操作NSHTTPCookieStorage即可。

以下是一些简单的使用

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/**
* 获得url对应的cookie
*
* @param url url
*
* @return url对应的cookie
*/
- (NSArray *)getCookiesForUrl:(NSString *)url
{
NSHTTPCookieStorage *sharedHTTPCookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];

if ([sharedHTTPCookieStorage cookieAcceptPolicy] != NSHTTPCookieAcceptPolicyAlways) {
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];
}

NSArray *cookies = [sharedHTTPCookieStorage cookiesForURL:[NSURL URLWithString:url]];

return cookies;
}

/**
* 获取应用下所有的cookie
*
* @return 所有的cookie
*/
- (NSArray *)getAllCookies
{
NSHTTPCookieStorage *sharedHTTPCookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];

if ([sharedHTTPCookieStorage cookieAcceptPolicy] != NSHTTPCookieAcceptPolicyAlways) {
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];
}

return sharedHTTPCookieStorage.cookies;
}

/**
* 将cookie数组当中的cookie设置到cookie缓存中
*
* @param mArrCookie cookie数组
*/
- (void)setCookiesFromArr:(NSArray *)mArrCookie
{
if (mArrCookie != nil && [mArrCookie count]>0) {
NSHTTPCookieStorage *sharedHTTPCookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];

for (NSHTTPCookie *cookie in mArrCookie) {
[sharedHTTPCookieStorage setCookie:cookie];
}
}
}

/**
* 为特殊的url设置cookie
*
* @param mArrCookie cookie数组
* @param url 地址,此处地址格式需为.baidu.com这样
*/
- (void)setCookie:(NSArray *)mArrCookie forUrl:(NSString *)url
{
if (mArrCookie != nil && [mArrCookie count]>0) {
NSHTTPCookieStorage *sharedHTTPCookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];

for (NSHTTPCookie *cookie in mArrCookie) {
NSMutableDictionary *cookieDic = [NSMutableDictionary dictionaryWithDictionary:[cookie properties]];
[cookieDic setObject:url forKey:@"Domain"];
NSHTTPCookie *tempCookie = [NSHTTPCookie cookieWithProperties:cookieDic];
[sharedHTTPCookieStorage setCookie:tempCookie];
}
}
}

/**
获取特定url相应key的value值

@param key cookie的key值
@param url url,一个完整的url地址,例如https://www.baidu.com/
@return 当前url下cookie当中当前key对应的value值
*/
- (NSString *)cookieValueWithKey:(NSString *)key forUrl:(NSURL *)url
{
NSHTTPCookieStorage *sharedHTTPCookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];

if ([sharedHTTPCookieStorage cookieAcceptPolicy] != NSHTTPCookieAcceptPolicyAlways) {
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];
}

NSArray *cookies = [sharedHTTPCookieStorage cookiesForURL:url];
for (NSHTTPCookie *item in cookies) {
if ([[item name] isEqualToString:key]) {
return [NSString stringWithString:[[item value] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
}
}
return nil;
}

//以下是一些将cookies本地化的操作

- (void)saveCacheCookie:(id)sender {
NSArray *mArrCookie = [self getAllCookies];
NSData *cookieData = [NSKeyedArchiver archivedDataWithRootObject:mArrCookie];
[[NSUserDefaults standardUserDefaults] setObject:cookieData forKey:@"cookieData"];
}

- (void)unzipCookie:(id)sender {
NSData *cookieData = [[NSUserDefaults standardUserDefaults] objectForKey:@"cookieData"];
NSArray *mArrCookie = [NSKeyedUnarchiver unarchiveObjectWithData:cookieData];
[self setCookiesFromArr:mArrCookie];
}

跨域问题

针对跨域问题,可以在服务器和客户端分别做处理解决

服务器:在服务器返回 cookie 时同时将相关联的其他域的 cookie 返回

客户端:在每次请求完成后,对想跨域的域名进行设置,添加对应的 cookie

推荐在服务器配置相关策略

因为 WKWebView 没有缓存,不使用NSHTTPCookieStorage,其 cookie 只存在内存当中,当前页面被释放其 cookie 也就消失了。因此 WKWebView 当中的请求不能使用其他请求的 cookie,同时两个 WKWebView 页面也不共享 cookie。

所以想要 WKWebView 使用其他请求的 cookie 或者前一个 WKWebView 页面的 cookie 需要在 WKWebView 加载前执行一段 js 代码,将 cookie 设置进去,而每次 WKWebView 请求之后获取 cookie,将其保存起来。至于存在哪里,NSHTTPCookieStorageNSUserDefaults都是不错的选择。