You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift Combine适配自签名HTTPS证书的接口请求实现方法

Combine 框架下自签名HTTPS证书适配实现

现有代码的核心问题

你目前实现的Combine请求直接调用URLSession.shared发起请求,这个系统提供的共享单例session使用内置的代理处理逻辑,完全不会触发你自定义SessionDelegate中的认证挑战回调,自签名证书校验逻辑自然不会生效。
URLSession.dataTaskPublisher本身支持绑定自定义的session实例,不需要改动现有Combine的链式调用逻辑,只需要替换请求发起的session来源即可。

具体实现步骤

1. 优化安全的自签名校验代理

对原来的SessionDelegate做安全增强,避免强制解包崩溃、无差别信任所有证书的问题:

class SelfSignedSessionDelegate: NSObject, URLSessionDelegate {
    /// 配置允许信任的自签名服务host集合,避免任意证书被放行
    private let trustedHosts: Set<String>
    
    init(trustedHosts: Set<String>) {
        self.trustedHosts = trustedHosts
        super.init()
    }
    
    func urlSession(
        _ session: URLSession,
        didReceive challenge: URLAuthenticationChallenge,
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
    ) {
        // 仅处理HTTPS服务端信任类挑战,其余类型的认证请求走系统默认处理
        guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust,
              let serverTrust = challenge.protectionSpace.serverTrust,
              trustedHosts.contains(challenge.protectionSpace.host) else {
            completionHandler(.performDefaultHandling, nil)
            return
        }
        // 对匹配可信host的自签名服务返回信任凭证
        let credential = URLCredential(trust: serverTrust)
        completionHandler(.useCredential, credential)
    }
}

2. 构造绑定自定义代理的URLSession实例

这里有个容易踩的核心坑:URLSession对传入的delegate是弱引用持有,不会帮你保持delegate的生命周期,必须自己对delegate实例做强引用,否则delegate会被ARC回收,认证挑战回调完全不会触发。

3. 调整API类的Combine请求逻辑

把原来使用URLSession.shared的请求替换为自定义配置的session,整体改动如下:

struct API {
    /// 持有绑定了自定义代理的URLSession实例
    private let session: URLSession
    
    /// 初始化时传入需要信任的自签名服务host列表
    init(trustedHosts: Set<String>) {
        let delegate = SelfSignedSessionDelegate(trustedHosts: trustedHosts)
        // 初始化session,配置保持和系统默认一致即可,同时绑定自定义代理
        self.session = URLSession(
            configuration: .default,
            delegate: delegate,
            delegateQueue: nil
        )
    }
    
    func getJSON() -> AnyPublisher<ResultList, Error> {
        guard let url = URL(string: urlString) else {
            return Fail(error: URLError(.badURL)).eraseToAnyPublisher()
        }
        // 替换原共享session调用,使用自定义的带证书校验逻辑的session发起请求
        return session.dataTaskPublisher(for: url)
            .map({ $0.data })
            .decode(type: ResultList.self, decoder: JSONDecoder())
            .receive(on: RunLoop.main)
            .eraseToAnyPublisher()
    }
}

实现注意事项

  • 不要将可信host配置硬编码在代理类内部,通过初始化方法注入更便于维护,同时能严格限制自签名证书的信任范围,降低中间人攻击风险
  • 如果需要更高的安全性,可以在代理处理serverTrust的逻辑中,增加对自签名证书固定指纹的比对校验,避免同host下的恶意证书被信任
  • 调试阶段如果出现请求一直报证书不信任的错误,优先检查delegate实例是否被强引用持有,避免因为ARC回收导致代理方法不触发

内容的提问来源于stack exchange,提问作者CableGuy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.28 16:24:04