問題:服務器端有一個網站須要AD認證,整站都開了Basic認證,包括圖片,CSS等資源,我在HTTP請求頭裏面添加認證所需的用戶名和密碼,傳遞到服務器端能夠認證經過。我在UIWebView的shouldStartLoadWithRequest代理方法中攔截WebView的請求,而後在請求的Header中添加認證所需的用戶名和密碼,而後使用NSURLSession從新發出HTTP的請求,這種方法能夠解決大部分的網絡請求,可是沒法攔截到網頁內部的ajax請求,因此全部的ajax請求都會失敗,一旦遇到ajax請求,認證都會失敗,而且網頁會失去響應?git
解決思路:使用NSURLProtocol攔截UIWebView內部的全部請求,包括Ajax請求,在全部的請求頭中添加認證所需的用戶名和密碼。github
注意:NSURLProtocol只能攔截UIWebView、NSURLConnection、NSURLSession和基於NSURLConnenction、NSURLSession實現的第三方框架(如AFNetworking)發出的網絡請求,沒法攔截WKWebview、CFNetwork以及基於CFNetwork實現的第三方框架(如MKNetworkit)發出的網絡請求。 //update on 2017-02-28ajax
下面提供一個完整的NSURLProtocol的實現類:服務器
RichURLSessionProtocol.h網絡
#import <Foundation/Foundation.h> @interface RichURLSessionProtocol : NSURLProtocol @end
RichURLSessionProtocol.msession
#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] == 0) { 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類的方法:app
1.在單個的UIViewController中使用框架
//導入自定義NSURLProtocol類網站
#import "RichURLSessionProtocol.h"
//在ViewDidLoad中添加攔截網絡請求的代碼atom
//註冊網絡請求攔截 [NSURLProtocol registerClass:[RichURLSessionProtocol class]];
//在ViewWillDisappear中添加取消網絡攔截的代碼
//取消註冊網絡請求攔截 [NSURLProtocol unregisterClass:[RichURLSessionProtocol class]];
2.攔截整個App中全部的網絡請求
//直接在AppDelegate中的didFinishLaunchingWithOptions註冊網絡攔截代碼
//註冊Protocol [NSURLProtocol registerClass:[RichURLSessionProtocol class]];