如何在 Swift 中制作经过身份验证的 HTTPS 帖子?

Posted

技术标签:

【中文标题】如何在 Swift 中制作经过身份验证的 HTTPS 帖子?【英文标题】:How To Make Authenticated HTTPS Post In Swift? 【发布时间】:2015-12-31 10:42:45 【问题描述】:

在 iPhone 应用程序中,我想从经过身份验证的 HTTPS 图像数据发布中收集 JSON 输出。也就是说,相当于:

curl -u "username":"password" \
     -X POST \
     -F "file=@image.png" \
     "https://server.com/blah?param=value"

此命令已验证可与真实 URL 一起使用,而不是为提问而给出的虚拟 URL。服务器需要基本身份验证。

按照Alamofire readme 和this *** example 中的“上传MultipartFormData”示例,我第一次尝试使用Alamofire,如下所示:

// 'username' and 'password' are string literals. 'img' is a valid UIImage.
let url =  NSURL("https://server.com/blah?param=value")
let dat = UIImagePNGRepresentation(img)
let credentialData = "\(username):\(password)".dataUsingEncoding(NSUTF8StringEncoding)!
let base64Credentials = credentialData.base64EncodedStringWithOptions([])
let headers = ["Authorization": "Basic \(base64Credentials)"]
Alamofire.upload(
        Alamofire.Method.POST,
        url!,
        headers: headers,
        multipartFormData:  multipartFormData in
            multipartFormData.appendBodyPart(data:dat!, name:"file")
        ,
        encodingCompletion:  encodingResult in
            switch encodingResult 

            case .Success(let upload, _, _):
                upload.responseString  response in
                    //JSON = response.result.value! as String
                    debugPrint(response)
                
            case .Failure(let encodingError):
                print(encodingError)
            
        )

同样,此代码 sn-p 使用虚拟 url,但我使用真实 url 运行代码。这会导致 SSL 错误:

2015-12-31 05:28:34.733 AutoAlbum[13518:249313] _BSMachError: (os/kern) invalid capability (20)
2015-12-31 05:28:34.733 AutoAlbum[13518:249682] _BSMachError: (os/kern) invalid name (15)
2015-12-31 05:28:35.413 AutoAlbum[13518:249708] CFNetwork SSLHandshake failed (-9824)
2015-12-31 05:28:35.415 AutoAlbum[13518:249708] NSURLSession/NSURLConnection  HTTP load failed (kCFStreamErrorDomainSSL, -9824)
[Request]: <NSMutableURLRequest: 0x7fdeda835040>  URL: https://server.com/blah?param=value 
[Response]: nil
[Data]: 0 bytes
[Result]: FAILURE: Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo=_kCFStreamErrorCodeKey=-9824, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, NSUnderlyingError=0x7fdeda84bd90 Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo=_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9824, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9824, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://server.com/blah?param=value, NSErrorFailingURLStringKey=https://server.com/blah?param=value, _kCFStreamErrorDomainKey=3

这个错误发生在 xcode 的 iPhone 5s 模拟器上,当有网络连接时。

我的第二种方法是:

let PasswordString = "username:password"
let PasswordData = PasswordString.dataUsingEncoding(NSUTF8StringEncoding)
let base64EncodedCredential = PasswordData!.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
let urlPath: String = "https://server.com/blah?param=value"
let url: NSURL = NSURL(string: urlPath)!
var request: NSMutableURLRequest = NSMutableURLRequest(URL: url)
request.setValue("Basic \(base64EncodedCredential)", forHTTPHeaderField: "Authorization")
request.HTTPMethod = "POST"
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let authString = "Basic \(base64EncodedCredential)"
config.HTTPAdditionalHeaders = ["Authorization" : authString]
let session = NSURLSession(configuration: config)
let img:UIImage = getAsset(asset, size: PHImageManagerMaximumSize)
let dat = String(UIImagePNGRepresentation(img))
let qstr = "file=\(dat)"
request.HTTPBody = qstr.dataUsingEncoding(NSUTF8StringEncoding)
var dataString = ""
session.dataTaskWithRequest(request) 
    (let data, let response, let error) in
    if let httpResponse = response as? NSHTTPURLResponse 
        dataString = String(data: data!, encoding: NSUTF8StringEncoding)!
        print(dataString)
    
    else 
        print(error)
    
.resume()

这导致了同样的 ssl 错误:

AutoAlbum[15614:316266] CFNetwork SSLHandshake failed (-9824)
2015-12-31 15:47:51.142 AutoAlbum[15614:316266] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9824)
Optional(Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." 

在this example 之后,我尝试将最低 tls 版本设置为 1.0,如下所示:

但遇到了同样的错误。

有人能在我的代码中发现问题吗,或者有人对如何使用 ios 技术来完成 curl 命令所做的事情有建议吗?

【问题讨论】:

我不知道 CURL。但是如果你想使用 POST 网络服务,那么你可以使用NSURLSession.dataTaskWithRequest() 我以前没有见过 Alamofire(也看不出它比 NSURLSession 有什么优势),但服务器可能有一个自签名证书,并且系统 TLS 库正在阻止连接。您可以为 Alamofire 库提供一个回调函数,允许您接受/拒绝证书。 【参考方案1】:

这与身份验证无关,iOS 由于某些原因无法建立与服务器的 https 连接(Code -1200 means“当尝试建立安全连接失败时返回,原因无法更具体地表达”)。

Curl 可以执行此请求,因为它不像 ATS 那样执行严格的检查,因此您应该正确配置它,并且您尝试这样做。 但是您在 ATS 配置中有一些错误,这就是请求仍然失败的原因。

    NSAllowsArbitraryLoads 应该是布尔类型,而不是字符串

    NSExceptionMinimumTLSVersion 应该嵌入到带有域名的字典中(仔细看你的例子,当你应该将yourserver.com 替换为实际域名时你错过了部分)。 This is a link 到官方文档如何正确配置 ATS 异常,它提供了有关其工作原理的更多详细信息。

    此外,为特定域指定 NSAllowsArbitraryLoadsNSExceptionMinimumTLSVersion 是多余的,因为 NSAllowsArbitraryLoads 将覆盖此设置。

【讨论】:

【参考方案2】:

我唯一能看出错误的是“允许任意加载”键应该是布尔类型。

您引用的 SO 线程建议将值为“NO”的“NSTemporaryExceptionRequiresForwardSecrecy”添加到“应用程序传输安全设置”中,您尝试过吗?

你能用更简单的 Web 请求(例如 GET 请求)测试 SSL 连接问题吗?

【讨论】:

感谢参与,我现在正在尝试这些东西。

以上是关于如何在 Swift 中制作经过身份验证的 HTTPS 帖子?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 中发出 HTTP 请求 + 基本身份验证

在 Swift 中使用 WKWebView 进行身份验证

如何安排经过身份验证的云功能的 http 调用?

如何从 android 客户端进行经过身份验证的 django rest api 调用?

如何使用经过身份验证的 id 令牌和数据库规则保护 Firebase Cloud Function HTTP 端点?

ioS Swift新用户无法登录,直到应用程序终止并启动