使用NSURLProtocol和NSURLSession拦截UIWebView的HTTP请求(包括ajax请求)
问题:服务器端有一个网站需要AD认证,整站都开了Basic认证,包括图片,CSS等资源,我在HTTP请求头里面添加认证所需的用户名和密码,传递到服务器端可以认证通过。我在UIWebView的shouldStartLoadWithRequest代理方法中拦截WebView的请求,然后在请求的Header中添加认证所需的用户名和密码,然后使用NSURLSession重新发出HTTP的请求,这种方法可以解决大部分的网络请求,但是无法拦截到网页内部的ajax请求,所以所有的ajax请求都会失败,一旦遇到ajax请求,认证都会失败,并且网页会失去响应?
解决思路:使用NSURLProtocol拦截UIWebView内部的所有请求,包括Ajax请求,在所有的请求头中添加认证所需的用户名和密码。
注意:NSURLProtocol只能拦截UIWebView、NSURLConnection、NSURLSession和基于NSURLConnenction、NSURLSession实现的第三方框架(如AFNetworking)发出的网络请求,无法拦截WKWebview、CFNetwork以及基于CFNetwork实现的第三方框架(如MKNetworkit)发出的网络请求。 //update on 2017-02-28
下面提供一个完整的NSURLProtocol的实现类:
RichURLSessionProtocol.h
#import <Foundation/Foundation.h> @interface RichURLSessionProtocol : NSURLProtocol @end
RichURLSessionProtocol.m
#import "RichURLSessionProtocol.h" static NSString * const RichURLProtocolHandledKey = @"RichURLProtocolHandledKey"; @interface RichURLSessionProtocol()<NSURLSessionDelegate> @property (atomic,strong,readwrite) NSURLSessionDataTask *task;
@property (nonatomic,strong) NSURLSession *session; @end @implementation RichURLSessionProtocol + (BOOL)canInitWithRequest:(NSURLRequest *)request
{
//只处理http和https请求
NSString *scheme = [[request URL] scheme];
if ( ([scheme caseInsensitiveCompare:@"http"] == NSOrderedSame ||
[scheme caseInsensitiveCompare:@"https"] == NSOrderedSame))
{
// NSLog(@"====>%@",request.URL); //看看是否已经处理过了,防止无限循环
if ([NSURLProtocol propertyForKey:RichURLProtocolHandledKey inRequest:request]) {
return NO;
} return YES;
}
return NO;
} + (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
/** 可以在此处添加头等信息 */
NSMutableURLRequest *mutableReqeust = [request mutableCopy];
return mutableReqeust;
}
- (void)startLoading
{
NSMutableURLRequest *mutableReqeust = [[self request] mutableCopy];
//打标签,防止无限循环
[NSURLProtocol setProperty:@YES forKey:RichURLProtocolHandledKey inRequest:mutableReqeust]; NSURLSessionConfiguration *configure = [NSURLSessionConfiguration defaultSessionConfiguration]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; self.session = [NSURLSession sessionWithConfiguration:configure delegate:self delegateQueue:queue];
self.task = [self.session dataTaskWithRequest:mutableReqeust];
[self.task resume];
} - (void)stopLoading
{
[self.session invalidateAndCancel];
self.session = nil;
} - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
if (error != nil) {
[self.client URLProtocol:self didFailWithError:error];
}else
{
[self.client URLProtocolDidFinishLoading:self];
}
} - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; completionHandler(NSURLSessionResponseAllow);
} - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
[self.client URLProtocol:self didLoadData:data];
} - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse * _Nullable))completionHandler
{
completionHandler(proposedResponse);
} //TODO: 重定向
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)newRequest completionHandler:(void (^)(NSURLRequest *))completionHandler
{
NSMutableURLRequest* redirectRequest;
redirectRequest = [newRequest mutableCopy];
[[self class] removePropertyForKey:RichURLProtocolHandledKey inRequest:redirectRequest];
[[self client] URLProtocol:self wasRedirectedToRequest:redirectRequest redirectResponse:response]; [self.task cancel];
[[self client] URLProtocol:self didFailWithError:[NSError errorWithDomain:NSCocoaErrorDomain code:NSUserCancelledError userInfo:nil]];
} - (instancetype)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id<NSURLProtocolClient>)client
{ NSMutableURLRequest* redirectRequest;
redirectRequest = [request mutableCopy]; //添加认证信息
NSString *authString = [[[NSString stringWithFormat:@"%@:%@", kGlobal.userInfo.sAccount, kGlobal.userInfo.sPassword] dataUsingEncoding:NSUTF8StringEncoding] base64EncodedString];
authString = [NSString stringWithFormat: @"Basic %@", authString];
[redirectRequest setValue:authString forHTTPHeaderField:@"Authorization"];
NSLog(@"拦截的请求:%@",request.URL.absoluteString); self = [super initWithRequest:redirectRequest cachedResponse:cachedResponse client:client];
if (self) { // Some stuff
}
return self;
} - (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler{ NSLog(@"自定义Protocol开始认证...");
NSString *authMethod = [[challenge protectionSpace] authenticationMethod];
NSLog(@"%@认证...",authMethod); if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
NSURLCredential *card = [[NSURLCredential alloc] initWithTrust:challenge.protectionSpace.serverTrust];
completionHandler(NSURLSessionAuthChallengeUseCredential,card);
} if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodNTLM]) {
if ([challenge previousFailureCount] == ) {
NSURLCredential *credential = [NSURLCredential credentialWithUser:kGlobal.userInfo.sAccount password:kGlobal.userInfo.sPassword persistence:NSURLCredentialPersistenceForSession];
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
}else{
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge,nil);
}
} NSLog(@"自定义Protocol认证结束");
} @end
使用自定义NSURLProtocol类的方法:
1.在单个的UIViewController中使用
//导入自定义NSURLProtocol类
#import "RichURLSessionProtocol.h"
//在ViewDidLoad中添加拦截网络请求的代码
//注册网络请求拦截
[NSURLProtocol registerClass:[RichURLSessionProtocol class]];
//在ViewWillDisappear中添加取消网络拦截的代码
//取消注册网络请求拦截
[NSURLProtocol unregisterClass:[RichURLSessionProtocol class]];
2.拦截整个App中所有的网络请求
//直接在AppDelegate中的didFinishLaunchingWithOptions注册网络拦截代码
//注册Protocol
[NSURLProtocol registerClass:[RichURLSessionProtocol class]];
使用NSURLProtocol和NSURLSession拦截UIWebView的HTTP请求(包括ajax请求)的更多相关文章
- Java过滤器处理Ajax请求,Java拦截器处理Ajax请求,java 判断请求是不是ajax请求
Java过滤器处理Ajax请求,Java拦截器处理Ajax请求,java 判断请求是不是ajax请求 Java过滤器处理Ajax请求,Java拦截器处理Ajax请求,拦截器Ajax请求 java ...
- ajax请求响应中用window.open打开新窗口会被浏览器拦截的解决方式
一.问题描述 ajax 异步请求成功后需要新开窗口打开 url,使用的是 window.open() 方法,但是会被浏览器给拦截了,需要用户点下. 二.问题分析 浏览器之所以拦截新开窗口是因为该操作并 ...
- $.ajax 请求 拦截器 重定向 无效 解决办法
在ajax 异步请求下 拦截器过滤器中使用 重定向 页面响应无效 我这里用的是springboot框架,用拦截器实现 对请求的拦截 ,session超时直接跳转到login.html页面. 后台代码: ...
- java端拦截器判断客户的的请求是否是ajax请求
java端拦截器判断客户的的请求是否是ajax请求 发表于 2014-08-22 23:38:08 普通请求与ajax请求的报文头不一样,通过如下 String requestType = reque ...
- ajax请求成功后打开新开窗口(window.open())被拦截的解决方法
问题:今天在做项目时需要在ajax请求成功后打开一个新的窗口,此时遇到浏览拦截了新窗口的问题,尝试在ajax 回调函数中模拟执行 click 或者 submit 等用户行为(trigger('clic ...
- 通过配置http拦截器,来进行ajax请求验证用户登录的页面跳转
在.NET中验证用户是否登录或者是否过期,若需要登录时则将请求转向至登录页面. 这个流程在进行页面请求时是没问题的,能正确进行页面跳转. 然而在使用xmlhttprequest时,或者jq的getJs ...
- ajax请求成功后新开窗口window.open()被拦截解决方法
ajax 异步请求成功后需要新开窗口打开 url,使用的是 window.open() 方法,但是很可惜被浏览器给拦截了,怎么解决这个问题呢 问题: 前面开发项目时碰到一个问题,ajax 异步请求 ...
- ajax请求成功后新窗口window.open()被拦截的解决方法
ajax 异步请求成功后需要新开窗口打开 url,使用的是 window.open() 方法,但是该操作并不是用户主动触发的,所以它认为这是不安全的就拦截了(不过如果是 _self 的话就不会有这个限 ...
- ajax请求后弹开新页面被浏览器拦截
window.open()我想应该很多人都不陌生吧,它可以实现除用a标签以外来实现打开新窗口! 最近开发项目用到时,却遇到了麻烦,本来好好的弹出窗口,结果被浏览器无情的给拦截了! 代码如下: $.ge ...
随机推荐
- 毕达哥拉斯三元组(勾股数组)poj1305
本原毕达哥拉斯三元组是由三个正整数x,y,z组成,且gcd(x,y,z)=1,x*x+y*y=z*z 对于所有的本原毕达哥拉斯三元组(a,b,c) (a*a+b*b=c*c,a与b必定奇偶互异,且c为 ...
- http => https 升级
准备证书 阿里云安全(云盾)-> CA证书服务,购买证书,个人测试的话可以使用免费的,期限1年. 购买证书后,把域名与证书进行绑定,提交审核,大概10分钟左右,正常情况下审核就可以通过.证书准备 ...
- c#的const可以用于引用类型吗
答案是可以的.不过用const修饰的类实例只能是null. class A{ public int a=0; } class B{ const A constA=null; const object ...
- Django 之 ModelForm 组件
Django的model form组件 扩展:Django 之Form组件 首先我们要知道 Model 和 Form 分别时干什么的 Model 生成表数据 Form 对表单.字段进行校验 Dja ...
- 我的Android进阶之旅------>android中getLocationInWindow 和 getLocationOnScreen的区别
View.getLocationInWindow(int[] location) 一个控件在其父窗口中的坐标位置 View.getLocationOnScreen(int[] location) 一个 ...
- python SimpleHTTPServer 快速共享文件
简单介绍 通过一个python命令快速共享文件给他人. 操作步骤 1.打开cmd命令行,切换到需要共享文件的目录,执行命令 python -m SimpleHTTPServer . 2.打开浏览器,在 ...
- LeetCode:删除排序链表中的重复元素【83】
LeetCode:删除排序链表中的重复元素[83] 题目描述 给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次. 示例 1: 输入: 1->1->2 输出: 1->2 示 ...
- 人生要golang
第一篇 : 初识golang 第二篇 : 下载及安装 未完待续 ............................................
- 《程序员代码面试指南》第二章 链表问题 将单链表每K个节点之间逆序
样例 链表1-2-3-4-5-6-7-8-9-10 K=3 ,结果 3-2-1-6-5-4-9-8-7-10 java代码 /** * @Description:将单链表每K个节点之间逆序 * @Au ...
- 【leetcode刷题笔记】Subsets II
Given a collection of integers that might contain duplicates, S, return all possible subsets. Note: ...