Swift开发:如何在不开启NSAllowArbitraryLoads的情况下处理自签名证书?
处理Swift中自签名证书的服务器认证挑战
嘿,作为Swift新手碰到自签名证书的认证问题太正常了——毕竟测试服务器用自签名证书是常事,但系统默认会拒绝这种“不受信任”的证书,所以咱们得在URLSession的认证挑战方法里手动处理。
先给你补全并优化你的didReceive challenge方法,同时拆解每一步的逻辑:
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { // 只处理服务器信任类型的认证挑战 guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust else { // 其他类型的挑战(比如HTTP Basic认证),直接用默认处理逻辑 completionHandler(.performDefaultHandling, nil) return } // 仅在开发测试环境下使用!生产环境绝对不能这么做! #if DEBUG // 获取服务器的信任对象 guard let serverTrust = challenge.protectionSpace.serverTrust else { completionHandler(.cancelAuthenticationChallenge, nil) return } // 创建信任该服务器的凭据 let credential = URLCredential(trust: serverTrust) // 告诉会话接受这个凭据,继续请求 completionHandler(.useCredential, credential) #else // 生产环境直接用默认逻辑,拒绝自签名证书 completionHandler(.performDefaultHandling, nil) #endif }
关键注意事项:
- 绝对不要在生产环境用这段代码:上面用了
#if DEBUG宏,确保只有在开发测试时才跳过证书验证,生产环境会走系统默认的安全逻辑,避免安全风险。 - 如果想让测试环境更安全(比如防止连错测试服务器),可以额外验证证书的指纹:
- 导出测试服务器的自签名证书,计算它的SHA256指纹
- 在代码里把预设的指纹和服务器返回证书的指纹对比,只有匹配才信任
举个指纹验证的简化示例:
// 预设测试服务器证书的SHA256指纹(示例值,替换成你自己的) let allowedFingerprint = "ABC123DEF456GHI789JKL012MNO345PQR678STU901VWX234YZ567" func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust, let serverTrust = challenge.protectionSpace.serverTrust, let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0) else { completionHandler(.performDefaultHandling, nil) return } #if DEBUG // 获取证书的data let certificateData = SecCertificateCopyData(certificate) as Data // 计算SHA256指纹 let sha256 = certificateData.sha256() let fingerprint = sha256.map { String(format: "%02x", $0) }.joined().uppercased() if fingerprint == allowedFingerprint { let credential = URLCredential(trust: serverTrust) completionHandler(.useCredential, credential) } else { // 指纹不匹配,拒绝连接 completionHandler(.cancelAuthenticationChallenge, nil) } #else completionHandler(.performDefaultHandling, nil) #endif } // 给Data加个计算SHA256的扩展 extension Data { func sha256() -> Data { var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) self.withUnsafeBytes { _ = CC_SHA256($0.baseAddress, CC_LONG(self.count), &hash) } return Data(hash) } }
这样既解决了测试环境的认证问题,又比直接信任所有服务器更安全。
内容的提问来源于stack exchange,提问作者Kevin
相关产品推荐
相关产品推荐

