zoukankan      html  css  js  c++  java
  • 使用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] == 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类的方法:

    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]];

    Demo地址

  • 相关阅读:
    华为OD机试 :找终点
    华为OD机试 :磁盘容量排序
    剑指Offer-从上到下打印二叉树
    GO语言学习笔记3-int与byte类型转换
    剑指Offer-树的子结构
    LeetCode :21.合并两个有序链表
    LeetCode :206.反转链表
    剑指Offer-删除链表的结点
    剑指Offer-调整数组顺序使奇数位于偶数前面
    如何创建ts+react项目
  • 原文地址:https://www.cnblogs.com/wobuyayi/p/6283599.html
Copyright © 2011-2022 走看看