HTTPDNS域名解析场景下如何使用Cookie?

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 1. Cookie 由于HTTP协议是无状态的,为了维护服务端和客户端的会话状态,客户端可存储服务端返回的Cookie,之后请求中可携带Cookie标识状态。 客户端根据服务端返回的携带Set-Cookie的HTTP Header来创建一个Cookie,Set-Cookie为字符串,主要字段如下

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] getIpByHostAsync: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的默认存储策略为NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomaindomainURL无法匹配时(使用HTTPDNS服务发送HTTP请求时,原生URL.host被替换为IP地址),该Cookie不会存储,因此再次发送请求时无法使用Cookie。

2.3 未指定domain的Cookie

若服务端配置Set-Cookiedomain不配置,

"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] getIpByHostAsync: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 forHTTPHeaderField:@"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.getIpByHostAsync(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的domaintest.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();
}
相关文章
|
1月前
|
并行计算 数据挖掘 大数据
[go 面试] 并行与并发的区别及应用场景解析
[go 面试] 并行与并发的区别及应用场景解析
|
3月前
|
弹性计算 缓存 应用服务中间件
阿里云服务器2核2G99元和2核4G199元实例规格性能及适用场景解析
2024年阿里云推出了两款云服务器,2核2G3M带宽40G ESSD Entry盘价格只要99元1年,2核4G5M带宽80G ESSD Entry盘价格只要199元1年,这两款云服务器的活动截止日期为2026年3月31日,活动期间新购、续费同价。那么这两款云服务器怎么样呢?可以用来做什么?本文将对这两款云服务器进行深度解析,包括配置介绍、实例规格、使用场景以及购买建议,以供选择参考。
阿里云服务器2核2G99元和2核4G199元实例规格性能及适用场景解析
|
1月前
|
机器学习/深度学习 存储 人工智能
提升深度学习性能的利器—全面解析PAI-TorchAcc的优化技术与应用场景
在当今深度学习的快速发展中,模型训练和推理的效率变得尤为重要。为了应对计算需求不断增长的挑战,AI加速引擎应运而生。其中,PAI-TorchAcc作为一个新兴的加速引擎,旨在提升PyTorch框架下的计算性能。本文将详细介绍PAI-TorchAcc的基本概念、主要特性,并通过代码实例展示其性能优势。
18076 166
|
2月前
|
设计模式 Java
Java中的设计模式及其应用场景解析
Java中的设计模式及其应用场景解析
|
18天前
|
运维 监控 数据可视化
Elasticsearch全观测技术解析问题之面对客户不同的场景化如何解决
Elasticsearch全观测技术解析问题之面对客户不同的场景化如何解决
|
21天前
|
存储 数据挖掘 大数据
深度解析Hologres计算资源配置:如何根据业务场景选择合适的计算类型?
【8月更文挑战第22天】Hologres是一款由阿里云提供的分布式分析型数据库,支持高效的大数据处理与分析。本文通过电商优化商品推荐策略的案例,介绍了Hologres中的计算组型与通用型配置。计算组型提供弹性扩展资源,适合大规模数据及高并发查询;通用型则适用于多数数据分析场景,具备良好计算性能。通过实例创建、数据加载、计算任务建立及结果查询的步骤展示,读者可理解两种配置的差异并根据业务需求灵活选择。
32 2
|
1月前
|
存储 数据库 C++
"深入剖析Python元组(tuple):与列表的对比、特性解析及高效应用场景展示"
【8月更文挑战第9天】Python元组与列表虽均用于存储元素集合,但有本质差异。元组不可变,创建后无法修改,适合保护数据不被意外更改的场景,如作字典键或传递固定值。列表则可变,支持动态增删改,适用于需频繁调整的数据集。元组因不可变性而在性能上有优势,可用于快速查找。两者各有千秋,根据具体需求选择使用。例如,元组可用于表示坐标点或日期,而列表更适合管理用户列表或库存。
35 1
|
2月前
|
Oracle NoSQL 固态存储
阿里云服务器ESSD Entry云盘与ESSD云盘选择指南:性能与场景解析
在我们选择阿里云服务器的时候,有部分云服务器同时支持ESSD Entry云盘和ESSD云盘,选择不同的云盘,价格也有所差异,有的用户还不清楚他们之间的区别,因此不知道选择哪种更好更能满足自己场景的需求,本文为大家介绍一下阿里云服务器ESSD Entry云盘和ESSD云盘的区别及选择参考。
阿里云服务器ESSD Entry云盘与ESSD云盘选择指南:性能与场景解析
|
2月前
|
域名解析 安全 物联网
阿里云EMAS HTTPDNS 扩展全球服务节点:提升解析安全性与网络覆盖
阿里云EMAS HTTPDNS新增国内西南、华南及国际欧洲、美东服务节点,提升了全球覆盖能力与性能。作为高效域名解析服务,EMAS HTTPDNS针对互联网、汽车、物流、IOT等行业提供支持,解决了传统解析易遭劫持等问题。新增节点优化了就近调度功能,显著缩短响应时间并增强了服务稳定性和连续性,尤其为中国企业的海外业务提供了强有力的支持。此次扩展展现了阿里云对服务质量的持续追求和全球市场布局的战略思考。
|
21天前
|
NoSQL Serverless API
Serverless 架构实现弹幕场景问题之API Gateway和OSS域名未绑定成功的问题如何解决
Serverless 架构实现弹幕场景问题之API Gateway和OSS域名未绑定成功的问题如何解决
26 0

相关产品

  • 云解析DNS
  • 推荐镜像

    更多