[置顶] HTTPDNS域名解析场景下如何使用Cookie?
2017-01-06 15:41
381 查看
1. Cookie
由于HTTP协议是无状态的,为了维护服务端和客户端的会话状态,客户端可存储服务端返回的Cookie,之后请求中可携带Cookie标识状态。客户端根据服务端返回的携带
Set-Cookie的HTTP Header来创建一个Cookie,
Set-Cookie为字符串,主要字段如下:
Set-Cookie: [name1=value1, name2=value2;],[expires=date;],[path=path;],[domain=domain;]
Cookie信息为形如
name=value的字符串;
expires,Cookie过期时间;
domain,Cookie适用域名;
path,请求资源URL中必须存在指定的路径时,才会发送该Cookie。
2. Cookie存储策略
基于iOS平台说明服务端Set-Cookie配置和客户端Cookie存储策略。
2.1 准备工作
访问域名test.com,假定HTTPDNS域名解析结果为
201.87.1.125;
Web服务器设置Cookie如下,
domain字段待定:
"Set-Cookie" = "name1=value1; expires=Wed, 15-Nov-17 15:41:02 GMT; path=/"
客户端发送普通HTTP请求:
- (void)connectToUrlString:(NSString *)urlString { NSURL *url = [NSURL URLWithString:urlString]; NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData; NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (error) { NSLog(@"error: %@", error); } else { NSLog(@"response: %@", response); NSLog(@"data: %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); } }]; [task resume]; }
客户端使用HTTPDNS服务发送HTTP请求:
- (void)connectToUrlStringUsingHTTPDNS:(NSString *)urlString { NSURL *url = [NSURL URLWithString:urlString]; NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData; NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSString *ip = [[HttpDnsService sharedInstance] getIpByHost:url.host]; if (ip) { NSLog(@"Get IP(%@) for host(%@) from HTTPDNS Successfully!", ip, url.host); NSRange hostFirstRange = [urlString rangeOfString:url.host]; if (hostFirstRange.location != NSNotFound) { NSString * newUrlString = [urlString stringByReplacingCharactersInRange:hostFirstRange withString:ip]; request.URL = [NSURL URLWithString:newUrlString]; [request setValue:url.host forHTTPHeaderField:@"host"]; } } NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (error) { NSLog(@"error: %@", error); } else { NSLog(@"response: %@", response); NSLog(@"data: %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); } }]; [task resume]; }
查询本App存储全部Cookie:
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; for (NSHTTPCookie *cookie in [cookieStorage cookies]) { NSLog(@"cookie: %@", cookie); }
查询本App存储指定URL对应适配Cookie:
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; NSDictionary *cookiesDic = [NSHTTPCookie requestHeaderFieldsWithCookies:[cookieStorage cookiesForURL:url]];
2.2 指定domain的Cookie
服务端配置Set-Cookie如下,
domain设置为
.test.com,
"Set-Cookie" = "name1=value1; expires=Wed, 15-Nov-17 15:41:02 GMT; path=/; domain=.test.com"
客户端调用
[self connectToUrlString:@"http://test.com"];发送HTTP请求后查询本地Cookie如下;再次访问时,HTTP头部自动添加该Cookie并发送到服务端。
name1 = value1; expires = Wed, 15-Nov-17 15:41:02 GMT; path = /; domain = .test.com;
客户端调用
[self connectToUrlStringUsingHTTPDNS:@"http://test.com"];,使用HTTPDNS服务发送HTTP请求,客户端同样收到上述
domain为
.test.com的Cookie,iOS网络库关于Cookie的默认存储策略为
NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain,
domain与
URL无法匹配时(使用HTTPDNS服务发送HTTP请求时,原生URL.host被替换为IP地址),该Cookie不会存储,因此再次发送请求时无法使用Cookie。
2.3 未指定domain的Cookie
若服务端配置Set-Cookie的
domain不配置,
"Set-Cookie" = "name1=value1; expires=Wed, 15-Nov-17 15:41:02 GMT; path=/;
客户端发送HTTP请求返回Cookie如下,
domain字段为空。
name1 = value1; expires = Wed, 15-Nov-17 15:41:02 GMT; path = /;
iOS网络库存储该Cookie时,自动填充Cookie的
domain字段为HTTP请求的
URL.host,即普通HTTP请求存储Cookie如下:
name1 = value1; expires = Wed, 15-Nov-17 15:41:02 GMT; path = /; domain = .test.com;
使用HTTPDNS访问的Cookie存储如下,再次使用HTTPDNS进行HTTP请求时,网络库默认Cookie匹配规则可以匹配到该Cookie。(此场景下使用HTTPDNS服务发送HTTP请求,虽然默认Cookie匹配规则可正确匹配Cookie,但是该场景依赖服务端Cookie的配置,为了安全性,通常服务端返回Set-Cookie的domain字段不为空。)
name1 = value1; expires = Wed, 15-Nov-17 15:41:02 GMT; path = /;domain = 201.87.1.125;
3. 适配策略
适配目的是在使用HTTPDNS服务,可以像通用HTTP请求对Cookie进行存储、
匹配和
发送,这就需要自行管理使用HTTPDNS服务的HTTP请求的Cookie。
存储,收到服务端返回的HTTP Header
Set-Cookie,可以正确解析并存储;
匹配,发送HTTP请求前,可正确搜索匹配Cookie;
发送,将匹配的Cookie放入HTTP请求中发送到服务端。
3.1 iOS适配
根据苹果文档说明,按照以下方式可以改变Cookie接受策略,文档说明默认策略为NSHTTPCookieAcceptPolicyAlways,经测试发现默认策略其实为
NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain;使用以下方式手动将策略修改为
NSHTTPCookieAcceptPolicyAlways,测试发现
Accept Cookie Always没有生效,原因需要进一步确定。
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; [cookieStorage setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];
实现了简单的HTTPDNS专用的
HTTPDNSCookieManager(【注意】此处仅提供Cookie管理的示例,Cookie管理细节如生命周期、匹配规则等不涉及,如有需要可参考RFC
2965),适用于使用HTTPDNS服务的HTTP请求,
HTTPDNSCookieManager.h
#ifndef HTTPDNSCookieManager_h #define HTTPDNSCookieManager_h // URL匹配Cookie规则 typedef BOOL (^HTTPDNSCookieFilter)(NSHTTPCookie *, NSURL *); @interface HTTPDNSCookieManager : NSObject + (instancetype)sharedInstance; /** 指定URL匹配Cookie策略 @param filter 匹配器 */ - (void)setCookieFilter:(HTTPDNSCookieFilter)filter; /** 处理HTTP Reponse携带的Cookie并存储 @param headerFields HTTP Header Fields @param URL 根据匹配策略获取查找URL关联的Cookie @return 返回添加到存储的Cookie */ - (NSArray<NSHTTPCookie *> *)handleHeaderFields:(NSDictionary *)headerFields forURL:(NSURL *)URL; /** 匹配本地Cookie存储,获取对应URL的request cookie字符串 @param URL 根据匹配策略指定查找URL关联的Cookie @return 返回对应URL的request Cookie字符串 */ - (NSString *)getRequestCookieHeaderForURL:(NSURL *)URL; /** 删除存储cookie @param URL 根据匹配策略查找URL关联的cookie @return 返回成功删除cookie数 */ - (NSInteger)deleteCookieForURL:(NSURL *)URL; @end #endif /* HTTPDNSCookieManager_h */
HTTPDNSCookieManager.m
#import <Foundation/Foundation.h> #import "HTTPDNSCookieManager.h" @implementation HTTPDNSCookieManager { HTTPDNSCookieFilter cookieFilter; } - (instancetype)init { if (self = [super init]) { /** 此处设置的Cookie和URL匹配策略比较简单,检查URL.host是否包含Cookie的domain字段 通过调用setCookieFilter接口设定Cookie匹配策略, 比如可以设定Cookie的domain字段和URL.host的后缀匹配 | URL是否符合Cookie的path设定 细节匹配规则可参考RFC 2965 3.3节 */ cookieFilter = ^BOOL(NSHTTPCookie *cookie, NSURL *URL) { if ([URL.host containsString:cookie.domain]) { return YES; } return NO; }; } return self; } + (instancetype)sharedInstance { static id singletonInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if (!singletonInstance) { singletonInstance = [[super allocWithZone:NULL] init]; } }); return singletonInstance; } + (id)allocWithZone:(struct _NSZone *)zone { return [self sharedInstance]; } - (id)copyWithZone:(struct _NSZone *)zone { return self; } - (void)setCookieFilter:(HTTPDNSCookieFilter)filter { if (filter != nil) { cookieFilter = filter; } } - (NSArray<NSHTTPCookie *> *)handleHeaderFields:(NSDictionary *)headerFields forURL:(NSURL *)URL { NSArray *cookieArray = [NSHTTPCookie cookiesWithResponseHeaderFields:headerFields forURL:URL]; if (cookieArray != nil) { NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; for (NSHTTPCookie *cookie in cookieArray) { if (cookieFilter(cookie, URL)) { NSLog(@"Add a cookie: %@", cookie); [cookieStorage setCookie:cookie]; } } } return cookieArray; } - (NSString *)getRequestCookieHeaderForURL:(NSURL *)URL { NSArray *cookieArray = [self searchAppropriateCookies:URL]; if (cookieArray != nil && cookieArray.count > 0) { NSDictionary *cookieDic = [NSHTTPCookie requestHeaderFieldsWithCookies:cookieArray]; if ([cookieDic objectForKey:@"Cookie"]) { return cookieDic[@"Cookie"]; } } return nil; } - (NSArray *)searchAppropriateCookies:(NSURL *)URL { NSMutableArray *cookieArray = [NSMutableArray array]; NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; for (NSHTTPCookie *cookie in [cookieStorage cookies]) { if (cookieFilter(cookie, URL)) { NSLog(@"Search an appropriate cookie: %@", cookie); [cookieArray addObject:cookie]; } } return cookieArray; } - (NSInteger)deleteCookieForURL:(NSURL *)URL { int delCount = 0; NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; for (NSHTTPCookie *cookie in [cookieStorage cookies]) { if (cookieFilter(cookie, URL)) { NSLog(@"Delete a cookie: %@", cookie); [cookieStorage deleteCookie:cookie]; delCount++; } } return delCount; } @end
使用HTTPDNS处理Cookie示例(使用上述默认简单匹配策略):
- (void)connectToUrlStringUsingHTTPDNS:(NSString *)urlString { NSURL *url = [NSURL URLWithString:urlString]; NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData; NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSString *ip = [[HttpDnsService sharedInstance] getIpByHost:url.host]; if (ip) { NSLog(@"Get IP(%@) for host(%@) from HTTPDNS Successfully!", ip, url.host); NSRange hostFirstRange = [urlString rangeOfString:url.host]; if (hostFirstRange.location != NSNotFound) { NSString *newUrlString = [urlString stringByReplacingCharactersInRange:hostFirstRange withString:ip]; NSLog(@"New URL: %@", newUrlString); request.URL = [NSURL URLWithString:newUrlString]; [request setValue:url.host forHTTPHeade点击打开链接rField:@"host"]; // 匹配合适Cookie添加到request中,这里传入的是原生URL [request setValue:[[HTTPDNSCookieManager sharedInstance] getRequestCookieHeaderForURL:url] forHTTPHeaderField:@"Cookie"]; // 删除Cookie [[HTTPDNSCookieManager sharedInstance] deleteCookieForURL:url]; } } NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { if (error) { NSLog(@"error: %@", error); } else { NSLog(@"response: %@", response); NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; // 解析HTTP Response Header,存储cookie [[HTTPDNSCookieManager sharedInstance] handleHeaderFields:[httpResponse allHeaderFields] forURL:url]; NSLog(@"data: %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage]; } }]; [task resume]; }
3.2 Android适配
使用如下方式HTTPDNS解析"http://test.com" 域名进行访问,String urlStr = "http://test.com"; // HTTPDNS解析"test.com"域名,发HTTP请求访问 new DownloadWebPageTask().execute(urlStr); private class DownloadWebPageTask extends AsyncTask<String, Void, String> { @Override protected String doInBackground(String... params) { try { return downloadUrl(params[0]); } catch (IOException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(String result) { if (result != null) { Log.e(TAG, "Get response: " + result); } else { Log.e(TAG, "Get response error."); } } } private String downloadUrl(String urlStr) throws IOException { try { URL url = new URL(urlStr); conn = (HttpURLConnection) url.openConnection(); String ip = httpDnsService.getIpByHost(urlStr); if (urlStr.equals(url2)) { ip = IP; } if (ip != null) { Log.d(TAG, "Get IP: " + ip + " for host: " + url.getHost() + " from HTTPDNS successfully!"); String newUrlStr = urlStr.replaceFirst(url.getHost(), ip); conn = (HttpURLConnection) new URL(newUrlStr).openConnection(); conn.setRequestProperty("Host", url.getHost()); } conn.setConnectTimeout(1000 * 15); int responseCode = conn.getResponseCode(); if (responseCode == 200) { Log.i(TAG, "Reponse code is 200."); dis = new DataInputStream(conn.getInputStream()); int len; byte[] buff = new byte[4096]; StringBuilder response = new StringBuilder(); while ((len = dis.read(buff)) != -1) { response.append(new String(buff, 0, len)); } return response.toString(); } else { Log.e(TAG, "Response code is " + responseCode); return null; } } catch (MalformedURLException e) { e.printStackTrace(); } return null; }
CookieManager配置如下:
/** * 配置cookie manager */ private void setCookieHandler() { /** * 指定cookie存储policy * 系统已定义策略有: * CookiePolicy.ACCEPT_ALL,存储全部cookie * CookiePolicy.ACCEPT_NONE,不存储cookie * CookiePolicy.ACCEPT_ORIGINAL_SERVER,按照RFC 2965 3.3节标准域名匹配存储cookie * */ cookieManager.setCookiePolicy(new CookiePolicy() { @Override public boolean shouldAccept(URI uri, HttpCookie cookie) { // 为方便测试,此处都返回true,存储全部cookie Log.i(TAG, "Uri: " + uri.toString() + ", cookie: " + cookie.toString() + ", domain: " + cookie.getDomain()); return true; } }); CookieHandler.setDefault(cookieManager); }
使用
CookieManager管理App的Cookie缓存,当对应Cookie满足策略存储到本地时,针对使用HTTPDNS域名解析进行HTTP访问的场景,不用修改即可完成适配。
基于HTTPDNS访问
http://test.com,获取到Cookie如下,
shouldAccept()返回true后网络库自动缓存该Cookie;
name1 = value1; expires = Wed, 15-Nov-17 15:41:02 GMT; path = /; domain = .test.com;
Cookie存储时,该Cookie直接和访问的URL(
http://201.87.1.125)相关联,并且Cookie的
domain为
test.com;所以,再次使用HTTPDNS服务防伪URL(
http://201.87.1.125)时,系统可自动获取到该Cookie。Cookie存储后可按照下面代码测试,基于域名/IP的URL查询缓存Cookie,都可以正确获取该Cookie,因此使用HTTPDNS服务时可自动完成适配。
String url1 = "http://test.com"; String url2 = "http://201.87.1.125"; CookieStore cookieStore = cookieManager.getCookieStore(); try { Log.e(TAG, "store cookie is " + cookieStore.get(new URI(url1))); Log.e(TAG, "store cookie is " + cookieStore.get(new URI(url2))); } catch (URISyntaxException e) { e.printStackTrace(); }
点击打开链接
相关文章推荐
- VB.net使用httpWebRequest访问带cookie的网站时如何传递cookie值
- HttpClient4.5.2 使用cookie保持会话——获取cookie之后如何传递它
- HttpClient4.5.2 使用cookie保持会话——获取cookie之后如何传递它(2)
- C#如何HttpWebRequest模拟登陆,获取服务端返回Cookie以便登录请求后使用
- 如何使用单元测试 测试WEB组件方法中的含有Session、Cookie等HttpContext功能调用的方法?
- 如何使用单元测试 测试WEB组件方法中的含有Session、Cookie等HttpContext功能调用的方法? 转
- HttpClient4.5.2 使用cookie保持会话——如何传递多个cookie(3)
- js_html_input中autocomplete="off"在chrom中失效的解决办法 使用JS模拟锚点跳转 js如何获取url参数 C#模拟httpwebrequest请求_向服务器模拟cookie发送 实习期学到的技术(一) LinqPad的变量比较功能 ASP.NET EF 使用LinqPad 快速学习Linq
- 如何使用单元测试 测试WEB组件方法中的含有Session、Cookie等HttpContext功能调用的方法?[转载]
- 如何让idhttp直接使用ie登录好的cookie来工作
- 学习如何使用 Cookie 编程
- 使用HttpWebRequest提交ASP.NET表单并保持Session和Cookie
- WebService开发(二) 如何使用Soap扩展 http://www.cnblogs.com/wayne-ivan/archive/2007/06/19/788541.html
- 使用 IIS 时如何修改 Cache-Control HTTP 头
- 使用HttpWebRequest提交ASP.NET表单并保持Session和Cookie
- 如何在AjaxPro.net的AjaxMethod中使用session和cookie
- 如何在WCF中使用自定义的Header,(典型场景:业务系统的登陆与使用)
- 如何使用 Visual C# .NET 创建 ASP.NET HTTP 模块
- 如何在AjaxPro.net的AjaxMethod中使用session和cookie
- [dotNET]使用HttpWebRequest请求远端服务器时如何加载SSL证书