客戶端登錄接口中的Cookie
- 由于HTTP協(xié)議是無狀態(tài)的惕耕,客戶端需借助cookie來實現(xiàn)跨URL的請求;
- 實現(xiàn)原理為:客戶端登錄服務(wù)端,服務(wù)端下發(fā)cookie數(shù)據(jù)給客戶端肋坚,客戶端的
NSURLResponse
根據(jù)會當前的NSHTTPCookieStorage
接受策略自動接收服務(wù)端返回的cookie并存儲在NSHTTPCookieStorage容器中飞蹂,我們不需要做任何操作几苍,當我們發(fā)送其他網(wǎng)絡(luò)請求(NSURLRequest)時,我們只需要設(shè)置NSURLRequest
的HTTPShouldHandleCookies屬性
為YES(默認為YES)陈哑,NSURLRequest會自動附帶cookie的信息發(fā)送給服務(wù)器妻坝; -
NSHTTPCookieStorage
可設(shè)置cookieAcceptPolicy
屬性,定義其接收cookie的策略芥颈,總共有三種接收策略惠勒,如下所示:
typedef NS_ENUM(NSUInteger, NSHTTPCookieAcceptPolicy) {
NSHTTPCookieAcceptPolicyAlways,
NSHTTPCookieAcceptPolicyNever,
NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain
};
- NSHTTPCookieAcceptPolicyAlways:永遠接收Cookie,這種情況下爬坑,NSHTTPCookieStorage會將接收到的cookie 存儲在偏好設(shè)置中纠屋;
- NSHTTPCookieAcceptPolicyNever:永遠不接受Cookie,這種情況下盾计,NSHTTPCookieStorage不會存儲cookie到本地售担;
- NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain:只接收指定域名的Cookie赁遗;
-
NSHTTPCookieStorage
的常見Api操作如下所示:
// cookie的接收策略
@property NSHTTPCookieAcceptPolicy cookieAcceptPolicy
// 獲取NSHTTPCookieStorage存儲的所有cookie
@property (nullable , readonly, copy) NSArray<NSHTTPCookie *> *cookies
// 設(shè)置cookie
- (void)setCookie:(NSHTTPCookie *)cookie
// 刪除cookie
- (void)deleteCookie:(NSHTTPCookie *)cookie
// 在某個時間點刪除cookies
- (void)removeCookiesSinceDate:(NSDate *)date
// 獲取指定URL的cookies
- (nullable NSArray<NSHTTPCookie *> *)cookiesForURL:(NSURL *)URL
// 獲取指定域名指定URL的cookies
- (void)setCookies:(NSArray<NSHTTPCookie *> *)cookies forURL:(nullable NSURL *)URL mainDocumentURL:(nullable NSURL *)mainDocumentURL
- NSHTTPCookieStorage容器中存儲的cookie數(shù)據(jù)是
NSHTTPCookie
實例對象,涉及的Api如下所示:
// 下面兩個方法用于對象的創(chuàng)建和初始化 都是通過字典進行鍵值設(shè)置
- (nullable instancetype)initWithProperties:(NSDictionary<NSString *, id> *)properties;
+ (nullable NSHTTPCookie *)cookieWithProperties:(NSDictionary<NSString *, id> *)properties;
// 返回Cookie數(shù)據(jù)中可用于添加HTTP頭字段的字典
+ (NSDictionary<NSString *, NSString *> *)requestHeaderFieldsWithCookies:(NSArray<NSHTTPCookie *> *)cookies;
// 從指定的響應(yīng)頭和URL地址中解析出Cookie數(shù)據(jù)
+ (NSArray<NSHTTPCookie *> *)cookiesWithResponseHeaderFields:(NSDictionary<NSString *, NSString *> *)headerFields forURL:(NSURL *)URL;
// Cookie數(shù)據(jù)中的屬性字典
@property (nullable, readonly, copy) NSDictionary<NSString *, id> *properties;
// 請求響應(yīng)的版本
@property (readonly) NSUInteger version;
// 請求相應(yīng)的名稱
@property (readonly, copy) NSString *name;
// 請求相應(yīng)的值
@property (readonly, copy) NSString *value;
// 過期時間
@property (nullable, readonly, copy) NSDate *expiresDate;
// 請求的域名
@property (readonly, copy) NSString *domain;
//請求的路徑
@property (readonly, copy) NSString *path;
// 是否是安全傳輸
@property (readonly, getter=isSecure) BOOL secure;
// 是否只發(fā)送HTTP的服務(wù)
@property (readonly, getter=isHTTPOnly) BOOL HTTPOnly;
// 響應(yīng)的文檔
@property (nullable, readonly, copy) NSString *comment;
// 相應(yīng)的文檔URL
@property (nullable, readonly, copy) NSURL *commentURL;
// 服務(wù)端口列表
@property (nullable, readonly, copy) NSArray<NSNumber *> *portList;
- 在客戶端當用戶推出登錄時族铆,我們需要清除
NSHTTPCookieStorage
容器中存儲的關(guān)于用戶的所有cookie數(shù)據(jù)岩四;
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
for (NSHTTPCookie *cookie in cookies) {
[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
}
UIWebView中Cookie機制
- UIWebView在加載HTML網(wǎng)頁時,會自動將網(wǎng)頁中cookie數(shù)據(jù)存儲到
NSHTTPCookieStorage
容器中哥攘,然后再加載其他的URL網(wǎng)頁請求時剖煌,請求會自動攜帶NSHTTPCookieStorage
容器中的cookie數(shù)據(jù),基本原理與上述的客戶端登錄接口中的Cookie的原理是一致的逝淹; - 在同一個App內(nèi)耕姊,多個UIWebView之間共享cookie數(shù)據(jù);
UIWebView中cookie的獲取與持久化
- (void)webViewDidFinishLoad:(UIWebView *)webView {
NSString *requestUrl = webView.request.URL.absoluteString;
NSLog(@" requestUrl: %@",requestUrl);
NSMutableArray *cookieArray = [[NSMutableArray alloc] init];
///網(wǎng)頁加載完成取出cookies
NSArray *nCookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
for (NSHTTPCookie *cookie in nCookies) {
///設(shè)置原始 cookie
NSMutableDictionary *cookieProperties = [NSMutableDictionary dictionary];
[cookieProperties setObject:cookie.name forKey:NSHTTPCookieName];
[cookieProperties setObject:cookie.value forKey:NSHTTPCookieValue];
[cookieProperties setObject:cookie.domain forKey:NSHTTPCookieDomain];
[cookieProperties setObject:cookie.path forKey:NSHTTPCookiePath];
[cookieProperties setObject:[[NSDate date] dateByAddingTimeInterval:2629743] forKey:NSHTTPCookieExpires];
[cookieArray addObject:cookieProperties];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
}
///cookie 持久化 存入本地
[[NSUserDefaults standardUserDefaults] setObject:cookieArray forKey:@"cookieArray"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
WKWebView的Cookie機制
- 在iOS11之前栅葡, WKWebView 加載HTML發(fā)起的URL請求不會自動帶上存儲于 NSHTTPCookieStorage 容器中的 Cookie茉兰,出現(xiàn)cookie丟失的問題;
- 在iOS11之后欣簇,iOS提供了
WKHTTPCookieStore
類规脸,其類似于NSHTTPCookieStorage
,是一個存儲cookie數(shù)據(jù)的容器熊咽,在發(fā)起URL請求時莫鸭,會自動保存網(wǎng)頁中的cookie,再加載其他的URL網(wǎng)頁請求网棍,會自動攜帶上cookie數(shù)據(jù)黔龟,但第一次發(fā)起URL請求時,依然存在cookie丟失的問題滥玷;
第一個問題:在 iOS11之前WKWebView第一次加載HTML時氏身,cookie丟失
- 在iOS11之前,當WKWebView第一次加載HTML時惑畴,由于不會讀取NSHTTPCookieStorage中的cookie蛋欣,導(dǎo)致cookie丟失,需要我們將NSHTTPCookieStorage容器中cookie同步到WKWebView中如贷,實現(xiàn)第一次加載HTML的URL請求中攜帶上cookie數(shù)據(jù)陷虎;
- 解決方案一:將NSHTTPCookieStorage容器中的cookie同步到
URL請求頭參數(shù)中
,代碼實現(xiàn)如下:
- (void)injectCookieInHeaderWithRequest:(NSMutableURLRequest *)request {
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
NSMutableArray *mCookies = [NSMutableArray new];
for (NSHTTPCookie *cookie in cookies) {
[mCookies addObject:cookie];
}
NSDictionary *headerParams = [NSHTTPCookie requestHeaderFieldsWithCookies:mCookies];
///設(shè)置請求頭 -- 攜帶NSHTTPCookieStorage容器中cookie數(shù)據(jù)
request.allHTTPHeaderFields = headerParams;
}
- 解決方案二:將
NSHTTPCookieStorage
中的cookie以JS的形式
杠袱,注入到WKWebView中尚猿,代碼實現(xiàn)如下:
- (void)syncCookieToWKWithJSForRequest:(NSMutableURLRequest *)request {
///讀取NSHTTPCookieStorage中的cookie 拼接成JS字符串
NSArray<NSHTTPCookie *> *tmp = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
NSMutableString *jscode_Cookie = [@"" mutableCopy];
[tmp enumerateObjectsUsingBlock:^(NSHTTPCookie * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSLog(@"%@ = %@",obj.name,obj.value);
[jscode_Cookie appendString:[NSString stringWithFormat:@"document.cookie = '%@=%@';", obj.name, obj.value]];
}];
WKUserContentController *userContentController = WKUserContentController.new;
WKUserScript *cookieScript = [[WKUserScript alloc] initWithSource: jscode_Cookie injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[userContentController addUserScript:cookieScript];
WKWebViewConfiguration *webViewConfig = WKWebViewConfiguration.new;
webViewConfig.userContentController = userContentController;
WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, ScreenW, ScreenH) configuration:webViewConfig];
[self.view addSubview:webView];
}
iOS11之前的跨域重定向的cookie同步問題,主要是針對上面的兩種方案的再處理楣富,在凿掂,代碼如下:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
///出現(xiàn)跨域時 cookie重新同步 JS方式
if (![self.currentUrl.host isEqualToString:navigationAction.request.URL.host]) {
//跨域重定向注入cookieScript
NSString *strDocumentCookie = [MKCookieManager genDocumentCookieForWKWebview:navigationAction.request.URL];
WKUserScript *cookieScript = [[WKUserScript alloc] initWithSource:strDocumentCookie injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[webView.configuration.userContentController addUserScript:cookieScript];
self.currentUrl = [NSURL URLWithString:navigationAction.request.URL.absoluteString];
}
decisionHandler(WKNavigationActionPolicyAllow);
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
///出現(xiàn)跨域時 cookie重新同步 請求頭參數(shù)
if (![self.currentUrl.host isEqualToString:navigationAction.request.URL.host]) {
self.currentUrl = [NSURL URLWithString:navigationAction.request.URL.absoluteString];
NSMutableURLRequest *request = [navigationAction.request mutableCopy];
NSString *cookieValue = @"session=4342235345345234;token_id=dsfdfdgsgerqereeytrertewqerw=;uid=543234;";
[request setValue:cookieValue forHTTPHeaderField:@"Cookie"];
[webView loadRequest:request];
decisionHandler(WKNavigationActionPolicyCancel);
} else {
decisionHandler(WKNavigationActionPolicyAllow);
}
}
在 iOS11之后WKWebView第一次加載HTML時,cookie丟失
- 在iOS11之后,當WKWebView第一次加載HTML時庄萎,由于不會讀取NSHTTPCookieStorage中的cookie踪少,導(dǎo)致cookie丟失,需要我們將NSHTTPCookieStorage容器中cookie同步到WKWebView中糠涛,實現(xiàn)第一次加載HTML的URL請求中攜帶上cookie數(shù)據(jù)援奢;
- 解決方案:將
NSHTTPCookieStorage容器中的cookie
同步到WKHTTPCookieStore
中,只要存于WKHTTPCookieStore中的cookie忍捡,WKWebView在發(fā)起URL請求時都會帶上cookie集漾,代碼如下:
- (void)syncCookieWithFirstRequest:(NSMutableURLRequest *)request {
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
if (@available(iOS 11.0, *)) {
WKHTTPCookieStore *cookieStore = self.wkWebView.configuration.websiteDataStore.httpCookieStore;
for (NSHTTPCookie *cookie in cookies) {
[cookieStore setCookie:cookie completionHandler:^{
}];
}
}
}
- 將上述的兩種情況合并,即iOS11前后锉罐,WKWebView第一次發(fā)起URL請求時帆竹,將NSHTTPCookieStorage中cookie同步到WKWebView中,代碼如下:
///WKWebView第一次發(fā)起URL請求時 將NSHTTPCookieStorage中cookie同步到WKWebView中
- (void)syncCookieToWKWithFirstRequest:(NSMutableURLRequest *)request {
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
if (@available(iOS 11.0, *)) {
WKHTTPCookieStore *cookieStore = self.wkWebView.configuration.websiteDataStore.httpCookieStore;
for (NSHTTPCookie *cookie in cookies) {
[cookieStore setCookie:cookie completionHandler:^{
}];
}
} else {
NSMutableArray *mCookies = [NSMutableArray new];
for (NSHTTPCookie *cookie in cookies) {
[mCookies addObject:cookie];
}
NSDictionary *headerParams = [NSHTTPCookie requestHeaderFieldsWithCookies:mCookies];
///設(shè)置請求頭 -- 攜帶NSHTTPCookieStorage容器中cookie數(shù)據(jù)
request.allHTTPHeaderFields = headerParams;
}
}
參考文章
iOS 深入淺出 網(wǎng)絡(luò)編程之 NSHTTPCookie/NSHTTPCookieStorage
iOS UIWebView 和 WKWebView 的 cookie 獲取,設(shè)置,刪除
https://blog.csdn.net/Forever_wj?type=blog