我正在上传大小为URLRequest.httpBody
的 6MB png Images-> Base64-> jsonData 。我在默认配置下使用静态NSURLSession
,并在urlsession上使用dataTask上传。有时将其成功上传到服务器,有时不成功,并且出现错误,并且在服务器端没有任何输出。我没有进行并行通话。我们正在使用SSL固定并正确处理身份验证挑战,因此没有SSL身份验证错误。
我们正在使用的iOS设备11.3和XCode 10。
任务<58BF437E-7388-4AE4-B676-2485A57CB0CD>。<10>完成,错误-代码:-999
private lazy var configuration: URLSessionConfiguration = {
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = TimeInterval(120)
configuration.timeoutIntervalForResource = TimeInterval(120)
return configuration
}()
private lazy var urlSession = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil)
func invokeService(methodName : String, collectionName: String, queryDictionary: [String:AnyObject]! = nil, httpMethod: String! = nil) {
// Set up the URL request
let baseUrl: String = WebServiceConstants.hostUrl.qaUrl + "/\(collectionName)" + "/\(methodName)"
// let baseUrl: String = WebServiceConstants.hostUrl.demoUrl + "/\(collectionName)" + "/\(methodName)"
// let baseUrl: String = WebServiceConstants.hostUrl.prod_Url + "/\(collectionName)" + "/\(methodName)"
guard let url = URL(string: baseUrl) else {
return
}
var urlRequest = URLRequest(url: url)
// set up the session
// let configuration = URLSessionConfiguration.default
// configuration.timeoutIntervalForRequest = TimeInterval(120)
// configuration.timeoutIntervalForResource = TimeInterval(120)
//
// let session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
urlRequest.httpMethod = httpMethod
do {
urlRequest.httpBody = try JSONSerialization.data(withJSONObject: queryDictionary, options: []) // pass dictionary to nsdata object and set it as request body
}
catch _ {
}
urlRequest.setValue(WebServiceConstants.HTTPStrings.contentTypeJSON, forHTTPHeaderField: WebServiceConstants.HTTPStrings.contentTypeHeader)
// print(queryDictionary)
if AppController.sharedInstance.isAlreadyLogin() && (KeychainWrapper.standard.string(forKey: Constants.UserDefaultKeys.authorizationHeaderValue) != nil) {
let authorizationHeaderValue = WebServiceConstants.HTTPStrings.authorizationHeaderValue + KeychainWrapper.standard.string(forKey: Constants.UserDefaultKeys.authorizationHeaderValue)!
urlRequest.setValue(authorizationHeaderValue, forHTTPHeaderField: WebServiceConstants.HTTPStrings.authorizationHeader)
}
let _ = urlSession.dataTask(with: urlRequest, completionHandler: { [unowned self]
(data, response, error) in
//print(response)
if error != nil {
if error?._code == NSURLErrorTimedOut {
// print(error?.localizedDescription)
let userInfo = [
NSLocalizedDescriptionKey: BWLocalizer.sharedInstance.localizedStringForKey(key:"App_Timeout_Message")
]
let errorTemp = NSError(domain:"", code:-1001, userInfo:userInfo)
self.delegate?.didFailWithError(errorObject: errorTemp)
} else if error?._code == NSURLErrorNotConnectedToInternet {
let userInfo = [
NSLocalizedDescriptionKey: BWLocalizer.sharedInstance.localizedStringForKey(key:"Internet_Not_Available")
]
let errorTemp = NSError(domain:"", code:-1001, userInfo:userInfo)
self.delegate?.didFailWithError(errorObject: errorTemp)
}
else if error?._code == NSURLErrorCancelled {
// canceled
print("Request is cancelled") // Control reaches here on Finished with Error code = -999
self.delegate?.didFailWithError(errorObject: error!)
}
else {
self.delegate?.didFailWithError(errorObject: error!)
}
} else {
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any]
{
self.delegate?.didReceiveResponse(responseObject: json as AnyObject)
//Implement your logic
print(json)
}
} catch {
self.delegate?.didFailWithError(errorObject: error)
}
}
}).resume()
}
*添加了SSL证书固定代码*
extension WebserviceHandler : URLSessionDelegate {
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
guard let trust = challenge.protectionSpace.serverTrust else {
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
let credential = URLCredential(trust: trust)
let pinner = setupCertificatePinner() // adding CertificateHash
if (!pinner.validateCertificateTrustChain(trust)) {
challenge.sender?.cancel(challenge)
}
if (pinner.validateTrustPublicKeys(trust)) {
completionHandler(.useCredential, credential)
}
else {
completionHandler(.cancelAuthenticationChallenge, nil)
let popUp = UIAlertController(title: "", message: BWLocalizer.sharedInstance.localizedStringForKey(key:"Certificate_Pining_Fail_Message"), preferredStyle: UIAlertController.Style.alert)
popUp.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: {alertAction in popUp.dismiss(animated: true, completion: nil)}))
popUp.addAction(UIAlertAction(title: BWLocalizer.sharedInstance.localizedStringForKey(key:"Action_Go"), style: UIAlertAction.Style.default, handler: { action in
let urlToOpenAppStorepage = URL(string: WebServiceConstants.hostUrl.appstore_url)
let objApp = UIApplication.shared
objApp.openURL(urlToOpenAppStorepage!)
}))
UIApplication.shared.keyWindow?.rootViewController?.present(popUp, animated: true, completion: nil)
}
}
}
func setupCertificatePinner() -> CertificatePinner {
// let baseUrl: String = WebServiceConstants.hostUrl.dev_URL
let baseUrl: String = WebServiceConstants.hostUrl.qaUrl
// let baseUrl: String = WebServiceConstants.hostUrl.demoUrl
// let baseUrl: String = WebServiceConstants.hostUrl.prod_Url
let pinner = CertificatePinner(baseUrl)
/*
You will see something like this:
being challanged! for www.google.co.nz
hash order is usually most specific to least, so the first one is your domain, the last is the root CA
Production
hash: 8U/k3RvTcMVSafJeS9NGpY4KDFdTLwpQ/GUc+lmPH/s=
hash: 4vkhpuZeIkPQ+6k0lXGi7ywkVNV55LhVgU0GaWWMOdk=
hash: jZzMXbxSnIsuAiEBqDZulZ/wCrrpW9bRLMZ6QYxs0Gk=
hash: uthKDtpuYHgn+wRsokVptSysyqBzmr4RP86mOC703bg=
you might need to change the has below to be the second one in the list for the code to pass
QA
hash: LX6ZGwP3Uii+KCZxDxDWlDWijvNI6K/t2906cUzKYM4=
hash: 4vkhpuZeIkPQ+6k0lXGi7ywkVNV55LhVgU0GaWWMOdk=
hash: jZzMXbxSnIsuAiEBqDZulZ/wCrrpW9bRLMZ6QYxs0Gk=
*/
pinner.debugMode = true
pinner.addCertificateHash(WebServiceConstants.HTTPStrings.hashKey)
return pinner
}
答案 0 :(得分:1)
我在该代码中看到了几个错误。
如果您在经过身份验证的代理后面,则身份验证处理程序的第一行将导致失败。
如果服务器需要任何类型的HTTP身份验证密码或OAuth凭据,也会失败。
在其他许多情况下也会失败。
除非该请求实际上在某种程度上是不好的,否则切勿取消身份验证请求。如果可以的话,取消身份验证请求还会阻止操作系统为您透明地处理它。唯一应该取消身份验证请求的情况是,如果您检查证书或其他内容,则该证书实际上无效。否则,在请求外部处理时,通常应该信任操作系统默认情况下可以做的正确的事情。
因此,除非身份验证方法是服务器信任,否则请使用默认处理。
该代码根本不检查身份验证方法。出于上述所有原因,除非身份验证方法是服务器信任,否则您不应该进行 any 检查。否则,请使用默认处理。总是。
下一个if语句有两个问题:
它为身份验证请求提供了新的状态而没有返回。这意味着您之后可以调用完成处理程序,这可能导致崩溃和其他不良行为。
它正在质询发送方上调用旨在影响行为的方法。这就是您过去使用NSURLConnection
进行操作的方式,但是您应该从不调用挑战发件人上的任何方法(除非可能,否则请查看它是否是您自己创建的对象正在将自定义NSURLProtocol
类与NSURLSession一起使用,因为它可能导致各种问题,甚至崩溃。 (请参见NSURLAuthenticationChallenge's -sender
method文档中的大警告,或者对此警告之前的两段内容。)
我也不完全确定我是否信任固定代码。如果信任链中的任何密钥都是受信任密钥,则看起来像过去了,而通常,锁定要求信任链中的 last (叶)密钥是受信任密钥。
该固定代码中的安全建议也令人怀疑。您可能不应该固定在证书上,而应该固定在证书内的密钥上。固定叶子证书的密钥是完全合适的,并且实际上是唯一合适的选择,因为它通常是唯一真正受您直接控制的密钥。如果您重新发行证书,那没什么大不了的,因为除非您的密钥已经以某种方式被盗用,否则您应该使用与以前相同的密钥重新发行。
或者,您可以根据需要添加信任层,但这需要运行自己的自定义根目录(这需要更改validateCertificateTrustChain方法以在验证信任链的同时添加自定义根证书)或说服CA向您出售可以签署其他证书的证书,价格为$$$$。这些选项似乎都不实用。
这些问题使我有点担心整个图书馆,但是我没有时间对其进行审核。您可能应该四处询问,看看是否有人对有关库进行了彻底的审核,因为编写编写与TLS密钥一起使用的代码时很容易犯错误,而且我不希望看到您以后遇到安全问题
修复上面列出的所有错误之后,如果仍然有问题,请返回并使用更新的代码提出另一个问题。另外,还请询问有问题的代码固定项目来修复其代码片段,因为它们似乎包含相同的错误。 :-)谢谢。