天天看點

iOS中自定義網絡架構NSURLSession 導入cer證書 post/get

###直接上代碼

.h檔案

//
//  HWNetworking.h
//  SpeakerNetworkConfig
//
//  Created by 陳帆 on 2019/10/23.
//  Copyright © 2019 wenming liu. All rights reserved.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN



/**
 請求資料類型

 - RequestSerializerJSON: JSON字元串("{\"a\":\"value1\",\"b\":\"value2\"}")
 - RequestSerializerNormal: 普通的&符号連結("a=value1&b=value2")
 */
typedef NS_ENUM(NSUInteger, RequestSerializerType){
    RequestSerializerJSON = 0,
    RequestSerializerNormal
};

@class HWSecurityPolicy;

/**
 請求成功傳回block

 @param response 響應對象
 @param responseObject 資料對象
 @param error 錯誤對象
 */
typedef void (^HttpSuccessCallback)(NSURLResponse * _Nullable response, id _Nullable responseObject, NSError * _Nullable error);

/**
 請求失敗傳回block

 @param error 錯誤對象
 */
typedef void (^HttpFailCallback)(NSError * _Nullable error);

@interface HWNetworking : NSObject

/**
 逾時時間 機關:s
 */
@property (assign, nonatomic) NSTimeInterval time;

/**
 請求header
 */
@property (strong, nonatomic) NSMutableDictionary *headerDict;

/**
 證書
 */
@property (strong, nonatomic) NSSet <NSData *> *cersData;

/**
 請求類型
 */
@property (assign, nonatomic) RequestSerializerType serializerType;

/**
 單例

 @return 單例對象
 */
+ (instancetype)sharedInstance;

/**
 get http請求
 
 @param url url
 @param paramsDict 參數清單
 @param successCallback 成功傳回callback
 @param failureCallback 失敗傳回callback
 */
- (void)httpGetWithUrl:(NSString *)url params:(NSDictionary *)paramsDict success:(HttpSuccessCallback)successCallback failure:(HttpFailCallback)failureCallback;

/**
 post http請求
 
 @param url url
 @param paramsDict 參數清單
 @param successCallback 成功傳回callback
 @param failureCallback 失敗傳回callback
 */
- (void)httpPostWithUrl:(NSString *)url params:(NSDictionary *)paramsDict success:(HttpSuccessCallback)successCallback failure:(HttpFailCallback)failureCallback;

/**
 iOS 原生資料請求
 @param url url
 @param dic 參數
 @param method 請求方法
 @param success 成功
 @param failure 失敗
 */
- (void)httpRequestWithUrl:(NSString *)url body:(NSDictionary *)dic header:(NSDictionary *)header method:(NSString *)method timeout:(NSTimeInterval)time success:(HttpSuccessCallback)success failure:(HttpFailCallback)failure;

# pragma static method
@end

NS_ASSUME_NONNULL_END

           

.m檔案

#import "HWNetworking.h"
#import <UIKit/UIKit.h>

@interface HWNetworking() <NSURLSessionTaskDelegate>

@end

@implementation HWNetworking

- (instancetype)init {
    self = [super init];
    if (self) {
        // init
        self.time = 15.0f;
        self.serializerType = RequestSerializerJSON;
    }
    return self;
}

+ (instancetype)sharedInstance {
    static HWNetworking *instance = nil;
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[HWNetworking alloc] init];
    });
    
    return instance;
}



/**
 get http請求

 @param url url
 @param paramsDict 參數清單
 @param successCallback 成功傳回callback
 @param failureCallback 失敗傳回callback
 */
- (void)httpGetWithUrl:(NSString *)url params:(NSDictionary *)paramsDict success:(HttpSuccessCallback)successCallback failure:(HttpFailCallback)failureCallback {
    [self httpRequestWithUrl:url body:paramsDict header:self.headerDict method:@"GET" timeout:self.time success:successCallback failure:failureCallback];
}

/**
 post http請求
 
 @param url url
 @param paramsDict 參數清單
 @param successCallback 成功傳回callback
 @param failureCallback 失敗傳回callback
 */
- (void)httpPostWithUrl:(NSString *)url params:(NSDictionary *)paramsDict success:(HttpSuccessCallback)successCallback failure:(HttpFailCallback)failureCallback {
    [self httpRequestWithUrl:url body:paramsDict header:self.headerDict method:@"POST" timeout:self.time success:successCallback failure:failureCallback];
}

/**
 iOS 原生資料請求
 @param url url
 @param dic 參數
 @param method 請求方法
 @param success 成功
 @param failure 失敗
 */
- (void)httpRequestWithUrl:(NSString *)url body:(NSDictionary *)dic header:(NSDictionary *)header method:(NSString *)method timeout:(NSTimeInterval)time success:(HttpSuccessCallback)success failure:(HttpFailCallback)failure {
    // 1、建立URL
    NSString *URLString = [url stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
    NSURL *urls = [NSURL URLWithString:URLString];
    
    // 2、建立請求對象
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:urls];
    [request setHTTPMethod:method];
    if (dic) {
        // 3、設定body
        NSData *bodyData = [self dealWithParam:dic];
        if (bodyData) {
            [request setHTTPBody:bodyData];
        }
    }
    
    // 4. 設定header
    if (header) {
        [request setAllHTTPHeaderFields:header];
        // 添加額外header
        [request setValue:@"zh-Hans-CN;q=1, en-CN;q=0.9" forHTTPHeaderField:@"Accept-Language"];
        [request setValue:[NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]] forHTTPHeaderField:@"User-Agent"];
    }
    
    // 5. 設定timeout value
    [request setTimeoutInterval:time];
    
    // 6. set mode
    [request setCachePolicy:NSURLRequestUseProtocolCachePolicy];
    
    // 建立會話
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    // 建立資料請求任務
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *_Nullable data,NSURLResponse * _Nullable response,NSError * _Nullable error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            if (error) {
                failure(error);
            } else {
                NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
                if (dic) {
                    success(response, dic, nil);
                } else {
                    failure(error);
                }
            }
        });
    }];
    
    // 啟動任務
    [task resume];
}

- (void)dealloc
{
    // 回收處理
}


#pragma mark - NSURLSessionDelegate
#pragma mark didReceiveChallenge
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
 completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler
{
    if ([[[challenge protectionSpace] authenticationMethod] isEqualToString: NSURLAuthenticationMethodServerTrust]) {
        do {
            SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
            NSCAssert(serverTrust != nil, @"serverTrust is nil");
            if(nil == serverTrust)
                break; /* failed */
            /**
             *  導入多張CA憑證(Certification Authority,支援SSL證書以及自簽名的CA),請替換掉你的證書名稱
             */
            if (!self.cersData) {
                break;
            }
            NSMutableArray *caArray = [NSMutableArray array];
            for (NSData *cerData in self.cersData) {
                SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)cerData);
                NSCAssert(caRef != nil, @"caRef is nil");
                if(nil == caRef)
                    break; /* failed */
                
                //可以添加多張證書
                [caArray addObject:(__bridge id)(caRef)];
            }
            NSCAssert(caArray != nil, @"caArray is nil");
            if(nil == caArray)
                break; /* failed */
            
            //将讀取的證書設定為服務端幀數的根證書
            OSStatus status = SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)caArray);
            NSCAssert(errSecSuccess == status, @"SecTrustSetAnchorCertificates failed");
            if(!(errSecSuccess == status))
                break; /* failed */
            
            SecTrustResultType result = -1;
            //通過本地導入的證書來驗證伺服器的證書是否可信
            status = SecTrustEvaluate(serverTrust, &result);
            if(!(errSecSuccess == status))
                break; /* failed */
            
            BOOL allowConnect = (result == kSecTrustResultUnspecified) || (result == kSecTrustResultProceed);
            
            /* kSecTrustResultUnspecified and kSecTrustResultProceed are success */
            if(! allowConnect)
            {
                break; /* failed */
            }

#if 0
            /* Treat kSecTrustResultConfirm and kSecTrustResultRecoverableTrustFailure as success */
            /*   since the user will likely tap-through to see the dancing bunnies */
            if(result == kSecTrustResultDeny || result == kSecTrustResultFatalTrustFailure || result == kSecTrustResultOtherError)
                break; /* failed to trust cert (good in this case) */
#endif
            
            // The only good exit point
            NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
            return [[challenge sender] useCredential: credential forAuthenticationChallenge: challenge];
            
        }
        while(0);
    }
    
    // Bad dog
    NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
    completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge,credential);
    return [[challenge sender] cancelAuthenticationChallenge: challenge];

}

#pragma mark static method
// 處理字典參數
- (NSData *)dealWithParam:(NSDictionary *)param {
    if (self.serializerType == RequestSerializerJSON) {
         return [NSJSONSerialization dataWithJSONObject:param options:NSJSONWritingPrettyPrinted error:nil];
    } else {
        NSArray *allkeys = [param allKeys];
        NSMutableString *result = [NSMutableString string];
        //"a=b&c=d"
        for (NSString *key in allkeys) {
            id value = param[key];
            NSString *str = @"";
            if ([value isKindOfClass:[NSString class]]) {
                str = [NSString stringWithFormat:@"%@=%@&",key,[HWNetworking urlEncodeString:param[key]]];
            } else {
                str = [NSString stringWithFormat:@"%@=%@&",key, param[key]];
            }
            [result appendString:str];
        }
        [result substringWithRange:NSMakeRange(0, result.length-1)];
        return [result dataUsingEncoding:NSUTF8StringEncoding];
    }
}

// url encoding
+ (NSString *)urlEncodeString:(NSString *)string {
    static NSString * const kAFCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4
    static NSString * const kAFCharactersSubDelimitersToEncode = @"!$&'()*+,;=";
    
    NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
    [allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]];
    
    static NSUInteger const batchSize = 50;
    
    NSUInteger index = 0;
    NSMutableString *escaped = @"".mutableCopy;
    
    while (index < string.length) {
        NSUInteger length = MIN(string.length - index, batchSize);
        NSRange range = NSMakeRange(index, length);
        
        // To avoid breaking up character sequences such as 👴🏻👮🏽
        range = [string rangeOfComposedCharacterSequencesForRange:range];
        
        NSString *substring = [string substringWithRange:range];
        NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
        [escaped appendString:encoded];
        
        index += range.length;
    }
    
    return escaped;
}




@end

           

繼續閱讀