NSURLProtocol
需在控制器里面进行注册 [NSURLProtocol registerClass:自定义NSURLProtocol子类];
注册之后 所有的request都要通过下面的方法判断是否需要被处理
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
当返回的是NO的时候 代表request不遵守这个协议 当返回YES的时候代表遵守协议
NSURLProtocol 是无法对WkWebView进行拦截的,因为WkWebView 走的是WebKit内核 cookie也是走的这个流程
可以用来筛选request 重定向,监听进行操作,处理http https
- (void)startLoading
当在+ (BOOL)canInitWithRequest:(NSURLRequest *)request
返回YES 要在startLoading里面进行相关的操作,如果在startLoading里面没有进行相应的处理,那么就相关链接会加载不出来
有以下用法
- 可以拦截图片 进行替换
if ([[self.request.URL absoluteString] isEqualToString:@"https://m.baidu.com/static/index/plus/plus_logo_web.png"]) {
NSString *fileName = [[NSBundle mainBundle] pathForResource:@"lufei.jpg" ofType:@""];
NSDate *data = [NSData dataWithContentsOfFile:fileName];
[self.client URLProtocol:self didLoadData:data];
}
- 可以拦截所有的图片 进行替换
NSArray *array = @[@"png",@"jpg",@"jpeg"];
if ([array containsObject:[self.request.URL pathExtension]]) {
NSString *fileName = [[NSBundle mainBundle] pathForResource:@"lufei.jpg" ofType:@""];
NSDate *data = [NSData dataWithContentsOfFile:fileName];
[self.client URLProtocol:self didLoadData:data];
}
- 拦截url地址 进行重定向
由于这个是自循环的 重定向的时候 不希望他再被+ (BOOL)canInitWithRequest:(NSURLRequest *)request 拦截,可以为不想被拦截的url设置一个key 然后在+ (BOOL)canInitWithRequest:(NSURLRequest *)request 方法中
static NSString *const lmProtocolKey = @"lmProtocolKey";
if ([NSURLProtocol propertyForKey:lmProtocolKey inRequest:request]) {
return NO;
}
在- (void)startLoading 中
[NSURLProtocol setProperty:@(YES) forKey:lmProtocolKey inRequest:request];
NSURLProtocol的其他方法
+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b {
return [super requestIsCacheEquivalent:a toRequest:b];
}
用来判断两个请求是否是同一个请求
如果是,则可以使用缓存数据,通常只需要调用父类的实现即可,默认为YES,而且一般不在这里做事情
session无法hook成功
使用NSURLProtocol hook成功的标准是原先的request没有发送出去,对于url来说 可以直接hook成功 但是 对于session来说 没有完全成功拦截
(因为session.configuration.protocolClasses不含有NSURLProtocol的类 )需要对session的protocolClasses进行方法交换来实现hook
+ (void)hookNSURLSessionConfiguration{
Class cls = NSClassFromString(@"__NSCFURLSessionConfiguration") ?: NSClassFromString(@"NSURLSessionConfiguration");
Method originalMethod = class_getInstanceMethod(cls, @selector(protocolClasses));
Method stubMethod = class_getInstanceMethod([self class], @selector(protocolClasses));
if (!originalMethod || !stubMethod) {
[NSException raise:NSInternalInconsistencyException format:@"没有这个方法 无法交换"];
}
method_exchangeImplementations(originalMethod, stubMethod);
}
- (NSArray *)protocolClasses {
return @[[LMURLProtocol class],[NSURLHTTPProtocol class],[NSURLDataProtocol Class],[NSURLFTPProtocol Class],
[NSURLFileProtocol Class],[NSAboutURLProtocol Class]];
//如果还有其他的监控protocol,也可以在这里加进去
}
在数组里面加上原先的class对象 以免造成不可预知的问题
然后在控制器里面进行注册
[LMURLProtocol hookNSURLSessionConfiguration];
WKWebView的cookie
对于WKWebView无法得到cookie的问题 可以直接把cookie加到header
[self setCookieWithDomain:@"http://www.baidu.com" sessionName:@"cooci_token_UIWebView" sessionValue:@"123456789" expiresDate:nil];
NSDictionary *headerDict = [NSHTTPCookie requestHeaderFieldsWithCookies:[NSHTTPCookieStorage sharedHTTPCookieStorage].cookies];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]];
request.allHTTPHeaderFields = headerDict;
[self.webView loadRequest:request];
所有的请求都会经过下列方法
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
当WKWebView跨域,使用request.allHTTPHeaderFields方法无法解决cookie丢失的问题 用如下方法解决
- 在- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler方法里面进行cookie的注入
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
[[WKCookieManager shareManager] fixNewRequestCookieWithRequest:navigationAction.request];
decisionHandler(WKNavigationActionPolicyAllow);
}
- (NSURLRequest *)fixNewRequestCookieWithRequest:(NSURLRequest *)originalRequest{
NSMutableURLRequest *fixedRequest;
if ([originalRequest isKindOfClass:[NSMutableURLRequest class]]) {
fixedRequest = (NSMutableURLRequest *)originalRequest;
} else {
fixedRequest = originalRequest.mutableCopy;
}
NSDictionary *dict = [NSHTTPCookie requestHeaderFieldsWithCookies:[NSHTTPCookieStorage sharedHTTPCookieStorage].cookies];
if (dict.count) {
NSMutableDictionary *mDict = originalRequest.allHTTPHeaderFields.mutableCopy;
[mDict setValuesForKeysWithDictionary:dict];
fixedRequest.allHTTPHeaderFields = mDict;
}
return fixedRequest;
}
- 脚本注入(效果不是太好)
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
WKUserContentController *contoller = [[WKUserContentController alloc] init];
[contoller addUserScript:[[WKCookieManager shareManager] futhureCookieScript]];
configuration.userContentController = contoller;
self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
self.webView.navigationDelegate = self;
[self.view addSubview:self.webView];
[self.webView loadRequest:[self cookieAppendRequest]];
- (NSURLRequest *)cookieAppendRequest{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]];
NSArray *cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage].cookies;
//Cookies数组转换为requestHeaderFields
NSDictionary *requestHeaderFields = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
//设置请求头
request.allHTTPHeaderFields = requestHeaderFields;
NSLog(@"%@",request.allHTTPHeaderFields);
return request;
}
- (WKUserScript *)futhureCookieScript{
WKUserScript * cookieScript = [[WKUserScript alloc] initWithSource:[self cookieString] injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
return cookieScript;
}
- (NSString *)cookieString
{
NSMutableString *script = [NSMutableString string];
[script appendString:@"var cookieNames = document.cookie.split('; ').map(function(cookie) { return cookie.split('=')[0] } );\n"];
for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
if ([cookie.value rangeOfString:@"'"].location != NSNotFound) {
continue;
}
[script appendFormat:@"if (cookieNames.indexOf('%@') == -1) { document.cookie='%@'; };\n", cookie.name, cookie.kc_formatCookieString];
}
return script;
}
- WKWebsiteDataStore(iOS11以上)
[[WKWebsiteDataStore defaultDataStore] httpCookieStore]
设置一个WKWebsiteDataStore就可以解决cookie丢失的问题,当你需要更新cookie的时候重新设置即可
网友评论