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
相关产品推荐
相关产品推荐

