您的位置:首页 > 理论基础 > 计算机网络

[置顶] 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();
}
点击打开链接
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐