平时在打开某些网站时,会提示不安全需要信任证书之类的提示,这些网页要想在WKWebView上展示,还需要做一些适配
第一步
实现代理WKNavigationDelegate,找到如下方法,并实现
/*! @abstract Invoked when the web view needs to respond to an authentication challenge.
@param webView The web view that received the authentication challenge.
@param challenge The authentication challenge.
@param completionHandler The completion handler you must invoke to respond to the challenge. The
disposition argument is one of the constants of the enumerated type
NSURLSessionAuthChallengeDisposition. When disposition is NSURLSessionAuthChallengeUseCredential,
the credential argument is the credential to use, or nil to indicate continuing without a
credential.
@discussion If you do not implement this method, the web view will respond to the authentication challenge with the NSURLSessionAuthChallengeRejectProtectionSpace disposition.
*/
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler;
通过challenge.protectionSpace.authenticationMethod判断信任方式,对应的值为:
/*!
@const NSURLAuthenticationMethodClientCertificate
@abstract SSL Client certificate. Applies to any protocol.
*/
FOUNDATION_EXPORT NSString * const NSURLAuthenticationMethodClientCertificate API_AVAILABLE(macos(10.6), ios(3.0), watchos(2.0), tvos(9.0));
/*!
@const NSURLAuthenticationMethodServerTrust
@abstract SecTrustRef validation required. Applies to any protocol.
*/
FOUNDATION_EXPORT NSString * const NSURLAuthenticationMethodServerTrust API_AVAILABLE(macos(10.6), ios(3.0), watchos(2.0), tvos(9.0));
第二步,实现代理
实现如下代理便完成了服务端验证,没有特殊需求实现如下方法页面即可正常展示
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
} else {
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
}
}else {
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
}
}
#pragma mark 服务端验证
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain {
domain = nil;
/*
* 创建证书校验策略
*/
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是否可信任
SecTrustResultType result;
SecTrustEvaluate(serverTrust, &result);
if (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed) {
return YES;
}
return NO;
}
客户端验证
添加客户端验证的目的,主要还是觉得单向的服务端验证,不满足项目需求,还需要加入客户端验证,以保证项目的安全性,依项目实际需要使用即可
- (BOOL)clientCertificateTrust:(NSURLAuthenticationChallenge *)challenge{
SecTrustRef trust = challenge.protectionSpace.serverTrust;
SecTrustResultType result;
// 证书的路径,此证书存放在项目目录下
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"you" ofType:@"cer"];
NSData *certData = [NSData dataWithContentsOfFile:cerPath];
SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData);
NSMutableArray *cerArray;
[cerArray addObject:(__bridge_transfer id)(certificate)];
SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)cerArray);
SecTrustSetAnchorCertificatesOnly(trust, false);
OSStatus status = SecTrustEvaluate(trust, &result);
if(status == errSecSuccess && (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified)){
return YES;
}
return NO;
}
使用中遇到的问题
我个人在实际使用中遇到的问题,就是WKWebview默认缓存的问题,在开了代理的情况下,有时候可以打开有时候不可以打开
解决方式也很简单,先判断是否开启代理:
+ (BOOL) getIsOpenProxy{
// 以任意网址校验,是否开启代理
NSURL *url = [[NSURL alloc] initWithString:@"http://www.baidu.com"];
NSDictionary *proxySettings = CFBridgingRelease(CFNetworkCopySystemProxySettings());
NSArray *proxies = CFBridgingRelease(CFNetworkCopyProxiesForURL((__bridge CFURLRef)url,
(__bridge CFDictionaryRef)proxySettings));
if (proxies.count > 0)
{
NSDictionary *settings = [proxies objectAtIndex:0];
NSString *host = [settings objectForKey:(NSString *)kCFProxyHostNameKey];
NSString *port = [settings objectForKey:(NSString *)kCFProxyPortNumberKey];
NSString *type = [settings objectForKey:(NSString *)kCFProxyTypeKey];
if (host || port || ![type isEqualToString:@"kCFProxyTypeNone"])
{
return YES;
}
}
return NO;
}
在加载的时候,使用不同的缓存策略:
NSMutableURLRequest *request;
if([Webtools getIsOpenProxy]){
// 开启代理使用忽略本地缓存,直接从后台获取
request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.requestURL] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:15];
}else{
// 未开启代理使用默认缓存测试
request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.requestURL] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15];
}