侧边栏壁纸
博主头像
钱学超博主等级

火星人,1万小时法则的忠实拥趸。技术宅,象棋和羽毛球爱好者,马拉松PB成绩:4小时零8分。坚持认为算法是计算机的灵魂。喜欢解决问题,喜欢手工,喜欢与朋友们聊天喝酒吹牛X。

  • 累计撰写 81 篇文章
  • 累计创建 447 个标签
  • 累计收到 88 条评论
标签搜索

目 录CONTENT

文章目录

swift处理自签名证书的url验证

钱学超
2023-03-09 / 0 评论 / 0 点赞 / 350 阅读 / 800 字 / 正在检测是否收录...
  1. swift和oc一样,都是苹果的app研发语言,苹果推荐大家都使用https,所以难以避免的遇到ssl证书使用自签名证书的情况(测试服务器等情况)

  2. 而自签名证书,在苹果看来,也是不安全的。所以默认情况下,是不能正常访问的。

  3. 刚好做了一个App,使用swift访问https自签名服务器,记录一下处理方法。

A、Alamofire框架体系下,处理自签名URL的方法

   // 生成对应Url的Session,如果这个URL对应的host已经被注册,返回已有的Session即可。
  fileprivate static func generalSession(url: String, redirect: Redirector = Redirector.follow) -> Session {
      // 查看是否包含url对应的host
      var theHost: String = "";
      do {
          let regex = try NSRegularExpression(pattern: ".*?://(.*?)/.*", options: .caseInsensitive)
          theHost = regex.stringByReplacingMatches(in: url, options: .anchored, range: NSMakeRange(0, url.count), withTemplate: "$1")
      } catch {
      }

      if (!lztHostEvaluators.keys.contains(theHost)) {
          lztHostEvaluators[theHost] = DisabledTrustEvaluator();
          let host = URL.init(string: url)?.host ?? ""
          lztHostEvaluators[host] = DisabledTrustEvaluator();
          //  lztHostEvaluators[host] = PinnedCertificatesTrustEvaluator(acceptSelfSignedCertificates: true);
      }

      // 服务器访问证书的验证模式
      let manager = ServerTrustManager(evaluators: lztHostEvaluators)
      let logger = LztNetworkLogger()
      // 创建Session
      lztUrlSession = Session(serverTrustManager: manager, redirectHandler: redirect, eventMonitors: [logger])

      return lztUrlSession
      //    return Session.default;
  }
这样,使用session.request就可以访问自签名证书的URL了。

B、 使用swift自带的URLSession的方式,访问自签名URL

extension ImageLoader: URLSessionDelegate {
    func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        // 使用自签名证书认证
        completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
    }

    func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
        // 打印错误日志
        if let err = error {
            print("Error: \(err.localizedDescription)")
        } else {
            print("Error. Giving up")
        }
    }
}

extension ImageLoader: URLSessionDataDelegate {
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didBecome streamTask: URLSessionStreamTask) {
        streamTask.resume()
    }

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didBecome downloadTask: URLSessionDownloadTask) {
        downloadTask.resume()
    }

    func urlSession(_ session: URLSession, task: URLSessionTask, didReceive challenge: URLAuthenticationChallenge, completionHandler: (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        // 这里也一样,接收自签名证书
        completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))
    }

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        // 错误日志打印
        if let err = error {
            print("Error: \(err.localizedDescription)")
        } else {
            print("Error. Giving up")
        }
    }

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: (URLSession.ResponseDisposition) -> Void) {
        // 接收请求
        completionHandler(URLSession.ResponseDisposition.allow)
    }

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        session.finishTasksAndInvalidate()
    }
}
/// 定义Session,后续使用 session.dataTask(with: rUrl) {// do some thing } 就好了。
private lazy var session: URLSession = {
        let config = URLSessionConfiguration.default
        config.urlCache = cache
        config.requestCachePolicy = .returnCacheDataElseLoad
        return URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue())
    }()
  1. 注意第一种方式,我尝试使用 PinnedCertificatesTrustEvaluator(acceptSelfSignedCertificates: true) 这个方法,不好使。看起来这应该是好使的,也许是我哪里写错了也不一定。

  2. 除了以上的代码以外,还需要做一些配置,在info.plist文件中添加代码(也可以操作界面)

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>localhost</key>
        <dict>
            <key>NSExceptionAllowsInsecureHTTPLoads</key>
            <true/>
        </dict>
    </dict>
</dict>
  1. 以上方案可以解决类似:AppName[44835:3182593] NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813)
    这样的问题。

  2. 感谢:https://gist.github.com/stinger/420107a71a02995c312036eb7919e9f9

  3. 感谢各位的阅读,如果有错误或者有什么问题,请大家留言。我的代码一般都是拿来即用的,基本不需要修改。

  4. 环境:swift5.7 , AppCode 2022.3.2,XCode Version 14.2 (14C18)


0

评论区