HTTPS进行网络请求

由于苹果规定2017年1月1日以后,所有APP都要使用HTTPS进行网络请求,否则无法上架,因此研究了一下在iOS中使用HTTPS请求的实现。相信大家对HTTPS都或多或少有些了解,这里我就不再介绍了,主要功能就是将传输的报文进行加密,提高安全性。
1、证书准备

证书分为两种,一种是花钱向认证的机构购买的证书,服务端如果使用的是这类证书的话,那一般客户端不需要做什么,用HTTPS进行请求就行了,苹果内置了那些受信任的根证书的。另一种是自己制作的证书,使用这类证书的话是不受信任的(当然也不用花钱买),因此需要我们在代码中将该证书设置为信任证书。

我这边使用的是xca来制作了根证书,制作流程请参考

http://www.2cto.com/Article/201411/347512.html

,由于xca无法导出.jsk的后缀,因此我们只要制作完根证书后以.p12的格式导出就行了,之后的证书制作由命令行来完成。自制一个批处理文件,添加如下命令:

  set ip=%1%
    md %ip%
    keytool -importkeystore -srckeystore 
    ca.p12
     -srcstoretype PKCS12 -srcstorepass 
    123456
     -destkeystore ca.jks -deststoretype JKS -deststorepass 
    123456
    keytool -genkeypair -alias server-%ip% -keyalg RSA -keystore ca.jks -storepass 
    123456
     -keypass 
    123456
     -validity 3650 -dname "CN=%ip%, OU=ly, O=hik, L=hz, ST=zj, C=cn"
    keytool -certreq -alias server-%ip% -storepass 
    123456
     -file %ip%\server-%ip%.certreq -keystore ca.jks
    keytool -gencert -alias ca -storepass 
    123456
     -infile %ip%\server-%ip%.certreq -outfile %ip%\server-%ip%.cer -validity 3650 -keystore ca.jks  
    keytool -importcert -trustcacerts -storepass 
    123456
     -alias server-%ip% -file %ip%\server-%ip%.cer -keystore ca.jks
    keytool -delete -keystore ca.jks -alias ca -storepass 
    123456

将上面加粗的ca.p12改成你导出的.p12文件的名称,123456改为你创建证书的密码。

然后在文件夹空白处按住ctrl+shift点击右键,选择在此处打开命令窗口,在命令窗口中输入“start.bat ip/域名”来执行批处理文件,其中start.bat是添加了上述命令的批处理文件,ip/域名即你服务器的ip或者域名。执行成功后会生成一个.jks文件和一个以你的ip或域名命名的文件夹,文件夹中有一个.cer的证书,这边的.jks文件将在服务端使用.cer文件将在客户端使用,到这里证书的准备工作就完成了。
2、服务端配置

由于我不做服务端好多年,只会使用Tomcat,所以这边只讲下Tomcat的配置方法,使用其他服务器的同学请自行查找设置方法。

打开tomcat/conf目录下的server.xml文件将HTTPS的配置打开,并进行如下配置:

keystoreFile是你.jks文件放置的目录,keystorePass是你制作证书时设置的密码,netZone填写你的ip或域名。注意苹果要求协议要TLSv1.2以上。
3、iOS端配置

首先把前面生成的.cer文件添加到项目中,注意在添加的时候选择要添加的targets。

1.使用NSURLSession进行请求

  • (void)sessionBtnClicked {
    NSString *urlString = @”https://10.20.129.25:8443/dreamVideo/restful/show“;
    NSURL *url = [NSURL URLWithString:urlString];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10.0f];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
    [task resume];
    }

  • (void)URLSession:(NSURLSession )session dataTask:(NSURLSessionDataTask )dataTask
    didReceiveResponse:(NSURLResponse *)response
    completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
    NSLog(@”接收到服务器响应”);
    //注意:这里需要使用completionHandler回调告诉系统应该如何处理服务器返回的数据
    //默认是取消
    /**
    NSURLSessionResponseCancel = 0, 默认的处理方式,取消
    NSURLSessionResponseAllow = 1, 接收服务器返回的数据
    NSURLSessionResponseBecomeDownload = 2, 变成一个下载请求
    NSURLSessionResponseBecomeStream 变成一个流
    */
    completionHandler(NSURLSessionResponseAllow);
    }

  • (void)URLSession:(NSURLSession )session dataTask:(NSURLSessionDataTask )dataTask
    didReceiveData:(NSData *)data {
    NSLog(@”获取到服务段数据”);
    NSLog(@”%@”,[self jsonToDictionary:data]);
    }

  • (void)URLSession:(NSURLSession )session task:(NSURLSessionTask )task
    didCompleteWithError:(nullable NSError *)error {
    NSLog(@”请求完成”);
    }

  • (void)URLSession:(NSURLSession )session didReceiveChallenge:(NSURLAuthenticationChallenge )challenge
    completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    NSLog(@”证书认证”);
    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),请替换掉你的证书名称
    */
    NSString *cerPath = [[NSBundle mainBundle] pathForResource:@”ca” ofType:@”cer”];//自签名证书
    NSData* caCert = [NSData dataWithContentsOfFile:cerPath];

        NSCAssert(caCert != nil, @"caCert is nil");
        if(nil == caCert)
            break; /* failed */
    
        SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCert);
        NSCAssert(caRef != nil, @"caRef is nil");
        if(nil == caRef)
            break; /* failed */
    
        //可以添加多张证书
        NSArray *caArray = @[(__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 */
        NSLog(@"stutas:%d",(int)status);
        NSLog(@"Result: %d", result);
    
        BOOL allowConnect = (result == kSecTrustResultUnspecified) || (result == kSecTrustResultProceed);
        if (allowConnect) {
            NSLog(@"success");
        }else {
            NSLog(@"error");
        }
    
        /* 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
        NSLog(@"信任该证书");

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

}

  • (NSDictionary )jsonToDictionary:(NSData )jsonData {
    NSError *jsonError;
    NSDictionary *resultDic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableLeaves error:&jsonError];
    return resultDic;
    }即可成功请求到服务端。用SecTrustSetAnchorCertificates设置可信任证书列表后就只会在设置的列表中进行验证,会屏蔽掉系统原本的信任列表,要使系统的继续起作用只要调用SecTrustSetAnchorCertificates方法,第二个参数设置成NO即可。

2.使用AFNetworking进行请求

AFNetworking首先需要配置AFSecurityPolicy类,AFSecurityPolicy类封装了证书校验的过程。

/**
 AFSecurityPolicy分三种验证模式:
 AFSSLPinningModeNone:只是验证证书是否在信任列表中
 AFSSLPinningModeCertificate:该模式会验证证书是否在信任列表中,然后再对比服务端证书和客户端证书是否一致
 AFSSLPinningModePublicKey:只验证服务端证书与客户端证书的公钥是否一致
*/

AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
    securityPolicy.allowInvalidCertificates = YES;//是否允许使用自签名证书
    securityPolicy.validatesDomainName = NO;//是否需要验证域名,默认YES

    AFHTTPSessionManager *_manager = [AFHTTPSessionManager manager];
    _manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    _manager.securityPolicy = securityPolicy;
    //设置超时
    [_manager.requestSerializer willChangeValueForKey:@"timeoutinterval"];
    _manager.requestSerializer.timeoutInterval = 20.f;
    [_manager.requestSerializer didChangeValueForKey:@"timeoutinterval"];
    _manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringCacheData;
    _manager.responseSerializer.acceptableContentTypes  = [NSSet setWithObjects:@"application/xml",@"text/xml",@"text/plain",@"application/json",nil];

    __weak typeof(self) weakSelf = self;
    [_manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing *_credential) {

        SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
        /**
         *  导入多张CA证书
         */
        NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"ca" ofType:@"cer"];//自签名证书
        NSData* caCert = [NSData dataWithContentsOfFile:cerPath];
        NSArray *cerArray = @[caCert];
        weakSelf.manager.securityPolicy.pinnedCertificates = cerArray;

        SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCert);
        NSCAssert(caRef != nil, @"caRef is nil");

        NSArray *caArray = @[(__bridge id)(caRef)];
        NSCAssert(caArray != nil, @"caArray is nil");

        OSStatus status = SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)caArray);
        SecTrustSetAnchorCertificatesOnly(serverTrust,NO);
        NSCAssert(errSecSuccess == status, @"SecTrustSetAnchorCertificates failed");

        NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        __autoreleasing NSURLCredential *credential = nil;
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            if ([weakSelf.manager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                if (credential) {
                    disposition = NSURLSessionAuthChallengeUseCredential;
                } else {
                    disposition = NSURLSessionAuthChallengePerformDefaultHandling;
                }
            } else {
                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
            }
        } else {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }

        return disposition;
    }];

AFN的封装

/* 在使用的时候的调用方式/

NSString *urlString = @"https://10.20.129.25:8443/dreamVideo/restful/show";
HttpManager *httpManager = [HttpManager shareHttpManager];
[httpManager post:urlString withParameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
    NSLog(@"success");
} failure:^(NSURLSessionDataTask *task, NSError *error) {
    NSLog(@"failure");
}];

import “HttpManager.h”

@interface HttpManager : NSObject
@property(nonatomic,copy)NSString *etag;
+(instancetype)shareHttpManager;
//https访问

-(void)post:(NSString )url withParameters:(id)parameters success:(void (^)(NSURLSessionDataTask _Nonnull task, id _Nullable responseObject))success failure:(void (^)(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error))failure;

import “HttpManager.h”

#import "HttpManager.h"
#import "AFHTTPSessionManager.h"

@interface HttpManager()

@property(nonatomic,retain)AFHTTPSessionManager *manager;

@end

@implementation HttpManager

+(instancetype)shareHttpManager{
    static dispatch_once_t onece = 0;
    static HttpManager *httpManager = nil;
    dispatch_once(&onece, ^(void){
        httpManager = [[self alloc]init];
    });
    return httpManager;
}

//https访问
-(void)post:(NSString *)url withParameters:(id)parameters success:(void (^)(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject))success failure:(void (^)(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error))failure {
    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];

//    securityPolicy = [AFSecurityPolicy defaultPolicy];
    securityPolicy.allowInvalidCertificates = YES;
    securityPolicy.validatesDomainName = NO;

    _manager = [AFHTTPSessionManager manager];
    _manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    _manager.securityPolicy = securityPolicy;
    //设置超时时间
    [_manager.requestSerializer willChangeValueForKey:@"timeoutinterval"];
    _manager.requestSerializer.timeoutInterval = 20.f;
    [_manager.requestSerializer didChangeValueForKey:@"timeoutinterval"];
    _manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringCacheData;
//    if (_etag) {
//        [_manager.requestSerializer setValue:_etag forHTTPHeaderField:@"If-None-Match"];
//    } else {
//        [_manager.requestSerializer setValue:@"bb" forHTTPHeaderField:@"If-None-Match"];
//    }
    _manager.responseSerializer.acceptableContentTypes  = [NSSet setWithObjects:@"application/xml",@"text/xml",@"text/plain",@"application/json",nil];

    __weak typeof(self) weakSelf = self;
    [_manager setSessionDidReceiveAuthenticationChallengeBlock:^NSURLSessionAuthChallengeDisposition(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential *__autoreleasing *_credential) {

        SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
        /**
         *  导入多张CA证书(Certification Authority,支持SSL证书以及自签名的CA),请替换掉你的证书名称
         */
        NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"ca" ofType:@"cer"];//自签名证书
        NSData* caCert = [NSData dataWithContentsOfFile:cerPath];
        NSArray *cerArray = @[caCert];
        weakSelf.manager.securityPolicy.pinnedCertificates = cerArray;

        SecCertificateRef caRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)caCert);
        NSCAssert(caRef != nil, @"caRef is nil");

        NSArray *caArray = @[(__bridge id)(caRef)];
        NSCAssert(caArray != nil, @"caArray is nil");

        OSStatus status = SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)caArray);
        SecTrustSetAnchorCertificatesOnly(serverTrust,NO);
        NSCAssert(errSecSuccess == status, @"SecTrustSetAnchorCertificates failed");

        NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        __autoreleasing NSURLCredential *credential = nil;
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            if ([weakSelf.manager.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
                credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
                if (credential) {
                    disposition = NSURLSessionAuthChallengeUseCredential;
                } else {
                    disposition = NSURLSessionAuthChallengePerformDefaultHandling;
                }
            } else {
                disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
            }
        } else {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }

        return disposition;
    }];


    [_manager POST:url parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
        NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
        NSDictionary *headDic = response.allHeaderFields;
        NSInteger code = response.statusCode;
        NSLog(@"response statusCode is %zd",code);
//        NSString *etag = headDic[@"Etag"];
//        if (etag) {
//            _etag = etag;
//        }
        NSLog(@"%@",[[NSString alloc]initWithData:responseObject encoding:NSUTF8StringEncoding]);
        NSDictionary *responseDic = [self jsonToDictionary:[[NSString alloc]initWithData:responseObject encoding:NSUTF8StringEncoding]];
        success(task,responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
        NSDictionary *headDic = response.allHeaderFields;
        NSInteger code = response.statusCode;
        NSLog(@"response statusCode is %zd",code);
        failure(task,error);
    }];
}

- (NSDictionary *)jsonToDictionary:(NSString *)jsonString {
    NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
    NSError *jsonError;
    NSDictionary *resultDic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableLeaves error:&jsonError];
    return resultDic;
}

@end
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值