文章目录
- NSHTTPCookieStorage 管理cookie
- NSURLSession管理cookie
- 1.使用系统管理cookie的类NSHTTPCookieStorage管理cookie
- 2.自己手动管理cookie
- AFNetworking自己管理cookie
- ASIHTTPRequest自己管理cookie
- UIWebView和WKWebView的cookie管理机制
- 最近公司项目安全检查检测到缓存中的用户名和密码,虽然已经加密,但还是不安全;所以公司要求用
cookie
或者token
去请求数据。由于公司项目比较老,里面既用了ASIHTTPRequest
框架,又用了AFNetworking
框架,发现其实这两个框架默认都是自动保持cookie
的,我们不用去刻意处理它(获取与上传)。但是由于想搞清楚还是研究了一下cookie
,并用NSURLSession
、ASIHTTPRequest
、AFNetworking
自己手动管理cookie
。 -
cookie
: 是网站服务端为了辩别用户身份,在服务器端生生成并存储在用户本地终端(电脑、手机)上的数据。其实cookie
主要是用来免密登录的,我们这只是通过用户名和密码获取一个身份令牌,用于后面的接口调用,更像是token
。
NSHTTPCookieStorage 管理cookie
-
NSHTTPCookieStorage
提供了管理所有NSHTTPCookie
对象的接口,在OS X里,cookie
是在所有程序中共享的,而在iOS
中,cookie
只在当当前应用中有效。。Session Cookie(SessionOnly返回YES的Cookie)只能在单一进程中使用。
-
NSHTTPCookieStorage
可以获取,删除,设置单例里面的cookies
,设置cookie
的管理策略等。
//只读的单例对象
@property(class, readonly, strong) NSHTTPCookieStorage *sharedHTTPCookieStorage;
//获取里面的cookie对象数组
@property (nullable , readonly, copy) NSArray<NSHTTPCookie *> *cookies;
//添加cookie
- (void)setCookie:(NSHTTPCookie *)cookie;
//删除cookie
- (void)deleteCookie:(NSHTTPCookie *)cookie;
//删除某个日期之前的cookie
- (void)removeCookiesSinceDate:(NSDate *)date API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));
//设置cookie的管理策略
@property NSHTTPCookieAcceptPolicy cookieAcceptPolicy;
//获取对应url返回的cookie数组
- (nullable NSArray<NSHTTPCookie *> *)cookiesForURL:(NSURL *)URL;
-
NSHTTPCookie
里面有个properties
的属性,其组成如下:
<__NSArrayM 0x283bec540>(
<NSHTTPCookie
version:0
name:JSESSIONID
value:A0787301B667F5E1C7B5BABAAE58B104
expiresDate:'(null)'
created:'2018-12-27 02:39:06 +0000'
sessionOnly:TRUE
domain:192.168.1.121
partition:none
sameSite:none
path:/inner
isSecure:FALSE
isHTTPOnly: YES
path:"/inner" isSecure:FALSE isHTTPOnly: YES>,
<NSHTTPCookie
version:0
name:token
value:1545878346436_111_cd90cdd1-9938-4e18-8ccd-79ad54d5527c
expiresDate:'2019-01-26 02:39:06 +0000'
created:'2018-12-27 02:39:06 +0000'
sessionOnly:FALSE
domain:192.168.1.121
partition:none
sameSite:none
path:/inner
isSecure:FALSE
path:"/inner" isSecure:FALSE>
)
name | 必选 | 规定 cookie 的名称 |
value | 必选 | 规定 cookie 的值 |
expire | 可选 | 规定 cookie 的有效期 |
path | 可选 | 规定 cookie 的服务器路径,只有该路劲下的文件接口才能使用 |
domain | 可选 | 规定 cookie 的域名 |
secure | 可选 | 规定是否通过安全的 HTTPS 连接来传输 cookie |
其中sessionOnly:TRUE
的cookie是后台服务器自动创建的。
-
cookie
和session
默认都是后台创建,后端会将cookie
放在在response
的header
中返回给前端,将session
缓存在服务器中;前端获取到cookie
可以自己手动保存,也可以交给NSHTTPCookieStorage
单例来保存;前端将cookie
放在request
的header
中传给服务器,服务器会去查对应的session
,然后去交换获得用户信息(用户登录ID等)请求接口,这就是整个cookie
获取、设置和请求数据的流程。 - 获取
cookie
的几种方法:
//方式1 : NSHTTPCookie获取cookie
NSArray * array = [NSHTTPCookie cookiesWithResponseHeaderFields:httpresponse.allHeaderFields forURLresponse.URL];
for (NSHTTPCookie * cookie in array) {
//保存到sharedHTTPCookieStorage中
[[NSHTTPCookieStorage sharedHTTPCookieStorage]setCookie:cookie];
}
//方式2:NSHTTPURLResponse获取
NSHTTPURLResponse * httpresponse = (NSHTTPURLResponse *)response;
NSDictionary * dic = httpresponse.allHeaderFields;
//获取cookie字符串
NSString * cookiesStr = [dic valueForKey:@"Set-Cookie"];
//方式3:NSHTTPCookieStorage获取cookie
NSArray * cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage]cookiesForURL:response.URL];
- 多个cookie的格式通常为:cookie1=value1; cookie2=value2; cookie3=value3; 这里特别要注意,多个cookie之间用分号+空格分隔开,不是&也不是单纯的空格。我所了解到的iOS设置Cookie的方法有两种。
设置cookie方式1:NSDictionary * dic = httpresponse.allHeaderFields;
返回的字典有个"Set-Cookie"的字符串值就是多个cookie的
默认样式,我们获取到后可以直接设置cookie
到request的 header
中。
{
"Cache-Control" = "no-cache,must-revalidate";
"Content-Length" = 7;
Date = "Thu, 27 Dec 2018 07:11:51 GMT";
Expires = "Thu, 01 Jan 1970 00:00:00 GMT";
Pragma = "no-cache";
Server = "Apache-Coyote/1.1";
"Set-Cookie" = "JSESSIONID=D6F65DA599B3B6B22C1927C5D85B53F0; Path=/inner; HttpOnly, token=1545894711778_111_17906b71-9c77-48c5-8e5a-b9e52522a538; Expires=Sat, 26-Jan-2019 07:11:51 GMT; Path=/inner";
}
NSString * cookiesStr = [dic valueForKey:@"Set-Cookie"];
//设置cookies
[request setValue:cookiesaStr forHTTPHeaderField:@"Cookie"];
设置cookie方式2:构建多个NSHTTPCookie
实例对象的数组,根据NSHTTPCookie
实例数组生成对应的HTTP cookie header
,设置cookie
到request
的header
中。
NSDictionary *properties1 = [NSDictionary dictionaryWithObjectsAndKeys:
@"domain.com", NSHTTPCookieDomain,
@"/", NSHTTPCookiePath,
@"userid", NSHTTPCookieName,
strUserId, NSHTTPCookieValue,
nil];
NSDictionary *properties2 = [NSDictionary dictionaryWithObjectsAndKeys:
@"domain.com", NSHTTPCookieDomain,
@"/", NSHTTPCookiePath,
@"pid", NSHTTPCookieName,
pid, NSHTTPCookieValue,
nil];
NSHTTPCookie *cookie1 = [NSHTTPCookie cookieWithProperties:properties1];
NSHTTPCookie *cookie2 = [NSHTTPCookie cookieWithProperties:properties2];
NSArray* cookies = [NSArray arrayWithObjects: cookie1, cookie2, nil];
//根据NSHTTPCookie实例数组生成对应的HTTP cookie header
NSDictionary * headers = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
//设置cookie到header中
request.allHTTPHeaderFields = headers;
NSURLSession管理cookie
-
NSURLSession
中有两个+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;
初始化方法,这个configuration
可以给NSURLSession
实例对象配置一下属性策略等,与cookie
有关的属性有下:
//发送请求时是否设置cookie
@property BOOL HTTPShouldSetCookies;
//设置cookie的接收策略
@property NSHTTPCookieAcceptPolicy HTTPCookieAcceptPolicy;
typedef NS_ENUM(NSUInteger, NSHTTPCookieAcceptPolicy) {
NSHTTPCookieAcceptPolicyAlways,//接收所有的cookie,默认策略.
NSHTTPCookieAcceptPolicyNever,//不接收所有的cookie
NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain//只接收main document domain中的cookie.
};
//保存设置cookie的,如果不设置默认是[NSHTTPCookieStorage sharedHTTPCookieStorage],设置为nil管理cookie
@property (nullable, retain) NSHTTPCookieStorage *HTTPCookieStorage;
所以NSURLSession
多了一种设置cookie的方式,只要设置了HTTPCookieStorage
属性就可以自动设置了cookie。
1.使用系统管理cookie的类NSHTTPCookieStorage管理cookie
我们请求完成后必须手动将cookie
保存到[NSHTTPCookieStorage sharedHTTPCookieStorage]
中,不会自动将响应体header中的cookie保存,下面是具体实例。
//获取cookie
- (void)getCookies1{
NSURL * url = [NSURL URLWithString:@"http://192.168.1.121:8080/inner/mobile/actionSh/Abc!login.action?"];
NSString * post = [NSString stringWithFormat:@"loginId=%@",@"111"];
NSData * postData = [post dataUsingEncoding:NSUTF8StringEncoding];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBody = postData;
//defaultSessionConfiguration 使用默认session配置,类似NSURLConnection的标准配置,使用硬盘来存储缓存数据,会将缓存、cookie等存在本地
//ephemeralSessionConfiguration 临时session配置,与默认配置相比,不使用永久持存cookie、证书、缓存的配置,最佳优化数据传输。
//backgroundSessionConfiguration 后台session配置,与默认配置类似,不同的是会在后台开启另一个线程来处理网络数据
NSURLSessionConfiguration * defultConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
//不允许设置cookie
defultConfiguration.HTTPShouldSetCookies = NO;
//设置不允许缓存cookiechelue
defultConfiguration.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicyNever;
//清除所有的cookie
for(NSHTTPCookie *cookie in [NSHTTPCookieStorage sharedHTTPCookieStorage].cookies)
{
[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie: cookie];
}
NSLog(@"清除所有的cookies : %@",[NSHTTPCookieStorage sharedHTTPCookieStorage].cookies);
defultConfiguration.HTTPCookieStorage = nil;
NSURLSession * session = [NSURLSession sessionWithConfiguration:defultConfiguration delegate:nil delegateQueue:[NSOperationQueue currentQueue]];
NSURLSessionTask * task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"请求完成!!!");
if (!error) {
NSString * result = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
if (result) {
NSLog(@"解析成功:%@",result);
NSLog(@"response:%@",response);
NSHTTPURLResponse * httpresponse = (NSHTTPURLResponse *)response;
NSDictionary * dic = httpresponse.allHeaderFields;
// NSString * cookies = [dic valueForKey:@"Set-Cookie"];
// NSLog(@"cookies : %@ --- %@",cookies,[NSThread currentThread]);
// NSArray * NSArray = [[NSHTTPCookieStorage sharedHTTPCookieStorage]cookiesForURL:response.URL];
//获取cookie
NSArray * array = [NSHTTPCookie cookiesWithResponseHeaderFields:dic forURL:response.URL];
for (NSHTTPCookie * cookie in array) {
//保存到sharedHTTPCookieStorage中
[[NSHTTPCookieStorage sharedHTTPCookieStorage]setCookie:cookie];
}
NSLog(@"NSHTTPCookieStorage : %@",[NSHTTPCookieStorage sharedHTTPCookieStorage].cookies);
}else{
NSLog(@"解析失败!!!");
}
}else{
NSLog(@"请求失败 : %@",error.localizedDescription);
}
}];
[task resume];
}
//设置cookie
- (void)requestWithCookies1{
NSString * urlStr = @"http://192.168.1.121:8080/inner/mobile/actionSh/Abc!getData.action?";
//encode去掉中午和特殊字符
urlStr = [urlStr stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
NSURL * url = [NSURL URLWithString:urlStr];
NSURLRequest * request = [NSURLRequest requestWithURL:url];
NSURLSessionConfiguration * defultConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
defultConfiguration.HTTPShouldSetCookies = YES;
defultConfiguration.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicyAlways;
NSLog(@"NSHTTPCookieStorage : %@",[NSHTTPCookieStorage sharedHTTPCookieStorage].cookies);
defultConfiguration.HTTPCookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSURLSession * session = [NSURLSession sessionWithConfiguration:defultConfiguration delegate:nil delegateQueue:[NSOperationQueue currentQueue]];
NSURLSessionTask * task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"请求完成!!!");
if (!error) {
NSString * result = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
if (result) {
NSLog(@"解析成功:%@",result);
}else{
NSLog(@"解析失败!!!");
}
}else{
NSLog(@"请求失败 : %@",error.localizedDescription);
}
}];
[task resume];
}
2.自己手动管理cookie
上面的方法有时候并不能立即将cookie设置进去,所以我们自己手动管理cookie
比较保险,就是在request
的header
中设置cookie
,在response
的header
中获取到得cookie
保存到偏好设置或单例中自己管理。
//获取cookie
- (void)getCookies2{
NSURL * url = [NSURL URLWithString:@"http://192.168.1.121:8080/inner/mobile/actionSh/Abc!login.action?"];
NSString * post = [NSString stringWithFormat:@"loginId=%@",@"111"];
NSData * postData = [post dataUsingEncoding:NSUTF8StringEncoding];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBody = postData;
NSURLSessionConfiguration * defultConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
//不允许设置cookie
defultConfiguration.HTTPShouldSetCookies = NO;
//不接受cookie
defultConfiguration.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicyNever;
NSURLSession * session = [NSURLSession sessionWithConfiguration:defultConfiguration delegate:nil delegateQueue:[NSOperationQueue currentQueue]];
NSURLSessionTask * task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"请求完成!!!");
if (!error) {
NSString * result = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
if (result) {
// NSArray * cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage]cookiesForURL:response.URL];
NSLog(@"解析成功:%@",result);
NSLog(@"response:%@",response);
NSHTTPURLResponse * httpresponse = (NSHTTPURLResponse *)response;
NSDictionary * dic = httpresponse.allHeaderFields;
//获取cookie字符串
NSString * cookiesStr = [dic valueForKey:@"Set-Cookie"];
NSData * cookiesData = [NSKeyedArchiver archivedDataWithRootObject:cookiesStr];
//保存到偏好设置中
[[NSUserDefaults standardUserDefaults] setValue:cookiesData forKey:@"BoncUserDefaultsCookie"];
[[NSUserDefaults standardUserDefaults]synchronize];
}else{
NSLog(@"解析失败!!!");
}
}else{
NSLog(@"请求失败 : %@",error.localizedDescription);
}
}];
[task resume];
}
//设置cookie
- (void)requestWithCookies2{
NSString * urlStr = @"http://192.168.1.121:8080/inner/mobile/actionSh/Abc!getData.action?";
//encode去掉中午和特殊字符
urlStr = [urlStr stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
NSURL * url = [NSURL URLWithString:urlStr];
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
NSData * cookiesData = [[NSUserDefaults standardUserDefaults] valueForKey:@"BoncUserDefaultsCookie"];
NSString * cookiesaStr = [NSKeyedUnarchiver unarchiveObjectWithData:cookiesData];
//设置cookies
[request setValue:cookiesaStr forHTTPHeaderField:@"Cookie"];
//如果是NSURLRequest,可用下面方式设置
// request.allHTTPHeaderFields = nil;
NSURLSessionConfiguration * defultConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
defultConfiguration.HTTPShouldSetCookies = NO;
defultConfiguration.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicyNever;
NSURLSession * session = [NSURLSession sessionWithConfiguration:defultConfiguration delegate:nil delegateQueue:[NSOperationQueue currentQueue]];
NSURLSessionTask * task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"请求完成!!!");
if (!error) {
NSString * result = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
if (result) {
NSLog(@"解析成功:%@",result);
}else{
NSLog(@"解析失败!!!");
}
}else{
NSLog(@"请求失败 : %@",error.localizedDescription);
}
}];
[task resume];
}
AFNetworking自己管理cookie
-
AFNetworking
是自动保持cookie
的,我们不用去刻意处理它(获取与上传),除非你有一些需要。这里要讲的是我们手动管理cookie
,AFNetworking
中并没有专门为cookie
封装的代码,不过底层使用的是NSURLRequest
,所以我们可以获取到请求时服务器返回的cookie
,然后保存起来(删除和保存由我们自己管理),请求时候设置到request
到header
中即可。 - 设置不保持cookie
//默认是YES,cookie会被存储在共享的 NSHTTPCookieStorage 容器中
_manager.requestSerializer.HTTPShouldHandleCookies = NO;
- 获取cookie,并保存起来
// 获取所有数据报头信息
NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse *)task.response;
NSDictionary *fields = [HTTPResponse allHeaderFields];
// 获取cookie
NSString *cookieString = [fields valueForKey:@"Set-Cookie"];
//保存到偏好设置中
[[NSUserDefaults standardUserDefaults] setObject:cookieString forKey:@"BoncUserDefaultsCookie"];
[[NSUserDefaults standardUserDefaults]synchronize];
- 设置cookie
NSString * cookie = [[NSUserDefaults standardUserDefaults] valueForKey:@"BoncUserDefaultsCookie"];
[self.manager.requestSerializer setValue:cookie forHTTPHeaderField:@"Cookie"];
ASIHTTPRequest自己管理cookie
ASIHTTPRequest
是自动保持cookie
的,如果我们所用cookie
请求数据,默认情况下我们不需要做其他任何操作。不同于AFNetworking
的是ASIHTTPRequest
对cookie
进行了封装,不管是获取还是设置都及其方便,这里是我们自己管理cookie
的做法。- 设置不保持cookie
//默认是YES,cookie会被存储在共享的 NSHTTPCookieStorage 容器中,并且会自动被其他request重用。
request.useCookiePersistence = NO;
//清空session期间创建的所有cookie
//[ASIFormDataRequest setSessionCookies:nil];
//清除session期间产生的所有的cookie和缓存的授权数据。
// [ASIFormDataRequest clearSession];
- 获取和保存cookie:
//获取cookie
NSArray * cookies = [request responseCookies];
//获取responseHeader
// NSDictionary *headers = [request responseHeaders];
//保存cookie
NSData * cookiesData = [NSKeyedArchiver archivedDataWithRootObject:cookies];
[[NSUserDefaults standardUserDefaults]setValue:cookiesData forKey:@"BoncUserDefaultsCookies"];
[[NSUserDefaults standardUserDefaults]synchronize];
- 设置cookie
//依然要设置不保持cookie,否则传递cookie就不是我们自己保存到cookie
[request setUseCookiePersistence:NO];
//取出cookie
NSData * cookiesData = [[NSUserDefaults standardUserDefaults] valueForKey:@"BoncUserDefaultsCookies"];
NSArray * cookies = [NSKeyedUnarchiver unarchiveObjectWithData:cookiesData];
//设置cookie
[request setRequestCookies:cookies.mutableCopy];
-
AFNetworking
和ASIHTTPRequest
默认都是开启cookie
的,并且都是用[NSHTTPCookieStorage sharedHTTPCookieStorage]
管理cookie
的,所有二者可以通用,我们这里手动管理cookie
,可以自己控制cookie的生命周期,避免过期的情况。其实默认情况已经够我们正常使用了,这里需要注意点是默认都是开启cookie
的情况下,如在我们登录获取cookie
时候,一定要先清除cookie
,否则可能出现退出登录(不退出应用)换了个用户登录,登录后的数据还是上个用户的信息(未清除jsessionID
),虽然可以交给后台取处理(后台判断是不是登录接口,如果是就不取cookie
),但我们客户端也应该处理一下,避免该错误的发生。
AFNetworking
请求登录接口时候清除cookie:
AFHTTPSessionManager * manager = [AFHTTPSessionManager manager];
[manager.requestSerializer setValue:nil forHTTPHeaderField:@"Cookie"];
//有时候这样设置并不好使,虽然也是在不保持cookie,但[NSHTTPCookieStorage sharedHTTPCookieStorage]还是保持了cookie,这样jsessionID并没有清除,所以为保险起见要清空[NSHTTPCookieStorage sharedHTTPCookieStorage]里面的cookies
- (void)clearCookies{
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *_tmpArray = [NSArray arrayWithArray:[cookieStorage cookies]];
for (id obj in _tmpArray) {
[cookieStorage deleteCookie:obj];
}
}
ASIHTTPRequest
请求登录接口时候清除cookie:
[ASIFormDataRequest setSessionCookies:nil];
UIWebView和WKWebView的cookie管理机制
UIWebView
会将NSHttpRequest
的所有请求产生的cookie自动保存到NSHTTPCookieStorage
容器中,并且在同一个app
内多个UIWebView
之间共享,不需要我们做任何操作,在后续访问中会将cookie
自动带到request
请求当中。WKWebView
的cookie
问题在于WKWebView
发起的请求不会自动带上存储于NSHTTPCookieStorage
容器中的Cookie
,实践发现WKWebView
实例其实也会将cookie
存储于NSHTTPCookieStorage
中,但存储时机有延迟,在iOS 8上,当页面跳转的时候,当前页面的cookie
会写入NSHTTPCookieStorage
中,而在 iOS 10 上,JS 执行document.cookie
或服务器set-cookie
注入的cookie
会很快同步到NSHTTPCookieStorage
中,在执行[WKWebView loadReques:]
前将NSHTTPCookieStorage
中的内容复制到WKHTTPCookieStore
中,以此来达到WKWebView cookie
注入的目的。
- Demo下载地址: OS开发之cookie研究demo
- 参考文章:
iOS平台下cookie的使用ios AFNetworking—cookie(session)保持登陆会话状态ASIHTTPRequest-Cookie的使用UIWebView和WKWebView的cookie管理机制