域名被封锁问题解决

当移动网络服务运营商封锁了你的域名,导致请求失败时,可以通过自定义NSURLProtocol实现域名重定向来解决。本文介绍了通过实现协议类、新建域名管理类以及在请求回调中处理重定向的方法,详细解析了解决域名被拦截问题的步骤。

域名被拦截(重定向)

移动网络服务运营商或其他网络服务机构,由于某些不知明原因,会禁掉一些域名访问,如果你的域名正好在其中,那恭喜,你的域名访问被其封锁了。你的所有请求就只能呵呵了,此时四十五度角仰望星空。心里有一百万个为什么…但是天无绝人之路,我们可以通过域名重定向去解决。是不是瞬间开心起来了。以下是我的解决方法:

一. 自定义协议类(AFURLProtocol)继承NSURLProtocol
(1)、 实现+ (BOOL)canInitWithRequest:(NSURLRequest *)request方法。此方法可以处理给定的请求,YES是需要处理,NO不需要处理。

+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
    //防止无限循环
    if ([NSURLProtocol propertyForKey:URLProtocolHandledKey inRequest:request]) {
       return NO;
    }
    if (![UCDomainManager hasNeedDomainRedirect]) {
        return NO;
    }
    NSString *scheme = [[request URL] scheme];
    if ([scheme caseInsensitiveCompare:@"http"] == NSOrderedSame || [scheme caseInsensitiveCompare:@"https"] == NSOrderedSame) {
        if ([request.URL.host isEqualToString:[UCDomainManager currentDomain]]) {
            return YES;
        }
    }
   
   return NO;
}

(2)、实现+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b方法。出于缓存目的,请求被认为是等效的当且仅当它们将由相同协议处理且协议在执行后声明它们等效, 实施特定的检查。

+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b {
    return [super requestIsCacheEquivalent:a toRequest:b];
}

(3)、实现- (instancetype)initWithRequest:(NSURLRequest *)request cachedResponse:(nullable NSCachedURLResponse *)cachedResponse client:(nullable id )client方法。初始化给定的NSURLProtocol请求, 缓存的响应和客户端。

- (instancetype)initWithRequest:(NSURLRequest *)request cachedResponse:(NSCachedURLResponse *)cachedResponse client:(id<NSURLProtocolClient>)client {
    return [super initWithRequest:request cachedResponse:cachedResponse client:client];
}

(4)、实现+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request方法。拦截request请求进行处理(修改host、添加cookie)

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
    NSMutableURLRequest *mutableRequest = [request mutableCopy];
    mutableRequest = [self redirectHostInRequest:mutableRequest];
    return [mutableRequest copy];
}
+ (NSMutableURLRequest *)redirectHostInRequest:(NSMutableURLRequest *)request {
    if (request.URL.host.length == 0) {
        return request;
    }
    NSString *originUrlString = [request.URL absoluteString];
    NSString *originHostString = [request.URL host];
    NSRange hostRange = [originUrlString rangeOfString:originHostString];
    if (hostRange.location == NSNotFound) {
        return request;
    }
    NSString *httpDnsIP = [UCDomainManager getRedirectIP];
    //替换请求头中url host
    NSString *urlString = [originUrlString stringByReplacingCharactersInRange:hostRange withString:httpDnsIP];
    NSURL *url = [NSURL URLWithString:urlString];
    request.URL = url;
    
    //注意:若包头的host(key-value中的host)本身就是一个IP,则需要将这个IP替换成域名(该域名需要从referer中获取)
    if ([self isValidIP:originHostString]) {
        //从referer中获取域名
        NSString *referString = [request valueForHTTPHeaderField:@"Referer"];
        NSArray *firstArray = [referString componentsSeparatedByString:@"://"];
        NSString *secondString;
        if (firstArray.count >= 2) {
            secondString = firstArray[1];
        }else if (firstArray.count == 1) {
            secondString = firstArray[0];
        }
        if (secondString) {
            NSRange range = [secondString rangeOfString:@"/"];
            if (range.length > 0) {
                originHostString = [[secondString componentsSeparatedByString:@"/"] firstObject];
                originHostString = [originUrlString stringByReplacingOccurrencesOfString:httpDnsIP withString:originHostString];
            }
        }
    }
    //将从referer中取出的域名,放到请求包头的Host中
    [request setValue:originHostString forHTTPHeaderField:@"Host"];
    
    //设置http的header的cookie
    NSArray *cookieArray = [NSHTTPCookieStorage sharedHTTPCookieStorage].cookies;
    NSDictionary *cookieDic = [NSHTTPCookie requestHeaderFieldsWithCookies:cookieArray];
    NSString *cookie = [cookieDic objectForKey:@"Cookie"];
    [request setValue:cookie forHTTPHeaderField:@"Cookie"];
    
    return request;
}
// 判断一个字符串是否是一个IP地址
+ (BOOL)isValidIP:(NSString *)ipStr {
    if (nil == ipStr) {
        return NO;
    }
    NSArray *ipArray = [ipStr componentsSeparatedByString:@"."];
    if (ipArray.count == 4) {
        for (NSString *ipnumberStr in ipArray) {
            int ipnumber = [ipnumberStr intValue];
            if (!(ipnumber>=0 && ipnumber<=255)) {
                return NO;
            }
        }
        return YES;
    }
    return NO;
}

(5)、实现- (void)startLoading方法。自定义协议就要协议实现,开始加载请求

- (void)startLoading {
    NSMutableURLRequest *mutableRequst = [[self request] mutableCopy];
    //标识该request已经处理过了,防止无限循环
    [NSURLProtocol setProperty:@(YES) forKey:URLProtocolHandledKey inRequest:mutableRequst];
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    configuration.protocolClasses = @[[UCAFURLProtocol class]];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[NSOperationQueue currentQueue]];
    self.sessionDataTask = [session dataTaskWithRequest:mutableRequst];
    [self.sessionDataTask resume];
}

(6)、实现- (void)stopLoading方法。协议实现应该结束加载请求。

- (void)stopLoading {
    [self.sessionDataTask cancel];
    self.sessionDataTask = nil;
}

(7)、实现NSURLSessionDataDelegate协议。

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
    [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];

    completionHandler(NSURLSessionResponseAllow);
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    [[self client] URLProtocol:self didLoadData:data];
}

(8)、实现NSURLSessionTaskDelegate协议。

 1. (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    // 请求完成,成功或者失败的处理
    if (!error) {
        //成功
        [self.client URLProtocolDidFinishLoading:self];
    } else {
        //失败
        [self.client URLProtocol:self didFailWithError:error];
    }
}
 2. (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain {
    /*
     * 创建证书校验策略
     */
    NSMutableArray *policies = [NSMutableArray array];
    if (domain) {
        [policies addObject:(__bridge_transfer id) SecPolicyCreateSSL(true, (__bridge CFStringRef) domain)];
    } else {
        [policies addObject:(__bridge_transfer id) SecPolicyCreateBasicX509()];
    }
    /*
     * 绑定校验策略到服务端的证书上
     */
    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef) policies);
    /*
     * 评估当前serverTrust是否可信任,
     * 官方建议在result = kSecTrustResultUnspecified 或 kSecTrustResultProceed
     * 的情况下serverTrust可以被验证通过,https://developer.apple.com/library/ios/technotes/tn2232/_index.html
     * 关于SecTrustResultType的详细信息请参考SecTrust.h
     */
    SecTrustResultType result;
    SecTrustEvaluate(serverTrust, &result);
    if (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed) {
        return YES;
    }
    return NO;
}

 3. (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler {
    if (!challenge) {
        return;
    }
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    NSURLCredential *credential = nil;

    /*
     * 获取原始域名信息。
     */
    NSString* host = [[self.request allHTTPHeaderFields] objectForKey:@"Host"];
    if (!host) {
        host = self.request.URL.host;
    }
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        if ([self evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:host]) {
            disposition = NSURLSessionAuthChallengeUseCredential;
            credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
        } else {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }
    } else {
        disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    }
    // 对于其他的challenges直接使用默认的验证方案
    completionHandler(disposition,credential);
}

二. 新建一个域名管理类,用来请求管理域名重定向IP

@interface DomainManager : NSObject

/// 获取当前域名
+ (NSString *)currentDomain;

/// 获取重定向IP
+ (NSString *)getRedirectIP;

/// 请求失败检测网络,若网络正常,但域名访问失败尝试域名替换IP
+ (BOOL)checkNetAvaliable;

/// 是否需要域名重定向
+ (BOOL)hasNeedDomainRedirect;
@end

static BOOL isRedirect = NO;

static NSString *redirectIP = @"";

#import "UCDomainManager.h"

@implementation UCDomainManager

+ (NSString *)currentDomain {
    NSURL *url = [NSURL URLWithString:[UCHttpManager getBaseURLWithParam:nil]];
    return url.host;
}

+ (NSString *)getRedirectIP {
    return redirectIP;
}

+ (BOOL)checkNetAvaliable {
    if (redirectIP.length > 0) {
        isRedirect = YES;
        return YES;
    }
    __block BOOL canUse = NO;
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"获取ip地址"]];
    [[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
       if(data == nil) return;
       NSDictionary *resultDic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
        if (resultDic[@"ips"] && [resultDic[@"ips"] isKindOfClass:[NSArray class]]) {
            if ([resultDic[@"ips"] count] > 0) {
                canUse = YES;
                isRedirect = YES;
                redirectIP = resultDic[@"ips"][0];
            }
        }

    dispatch_semaphore_signal(semaphore);
    }] resume];
    
    dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 5*NSEC_PER_SEC));
    return canUse;
}

+ (BOOL)hasNeedDomainRedirect {
    return isRedirect;
}
@end

三. 在请求回调中处理重定向业务
根据NSError错误code码:
-1003------不能发现Host
-1004------不能连接到Host
310-----无法与HTTPS代理建立连接
在以上三种错误码中,处理重定向,并重新发起请求。

- (nonnull UCAFTASK *)postBaseRequestWithURL:(nullable NSString *)url
                      withParams:(nullable NSDictionary *)params
                     withSuccess:(nonnull UCHttpClientRequestSuccess)success
                         failure:(nonnull UCHttpClientRequestFailure)failure {
    
    [self resetRequestLanguage];
    return [[UCBaseHttpClient sharedClient].manager POST:url parameters:params success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull object) {
        
        if ([task task_isCanceled]) return;
        
        NSDictionary *resultJSON = [NSDictionary dictionary];
        if (object) {
            if ([object isKindOfClass:[NSData class]]) {
                resultJSON = [object objectFromJSONData];
                
            } else if([object isKindOfClass:[NSDictionary class]]) {
                resultJSON = [NSDictionary dictionaryWithDictionary:object];
            }
        }
        success ? success(task,resultJSON,YES) : 1;
    } failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) {
        if ([task task_isCanceled]) return;
        if (error.code == -1003 || error.code == 310 || error.code == -1004) {
            if ([UCDomainManager checkNetAvaliable]) {
                [self postBaseRequestWithURL:url withParams:params withSuccess:success failure:failure];
            }else{
                failure ? failure(task,error) : 1;
            }
        }else{
            failure ? failure(task,error) : 1;
        }
    } presetParameterEnabled:NO];
    
}

按照以上流程操作,恭喜你域名封锁问题你已经可以解决了。不急。先去撸个串压压惊吧。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值