URLSessionTaskDelegate认证挑战函数未触发,求Digest Auth替代方案
解决URLSession与Shelly设备Digest Authentication兼容问题
一、替代认证方式:预配置凭证到Session配置
不需要等挑战代理触发,直接把用户名密码封装成凭证,提前配置给URLSession:
let username = "你的用户名" let password = "你的密码" let credential = URLCredential(user: username, password: password, persistence: .forSession) let config = URLSessionConfiguration.default // 指定目标设备的防护空间,匹配Digest认证 config.urlCredentialStorage?.set(credential, for: URLProtectionSpace( host: "192.168.1.99", port: 80, protocol: "http", realm: nil, authenticationMethod: NSURLAuthenticationMethodHTTPDigest )) let session = URLSession(configuration: config, delegate: self, delegateQueue: nil) // 正常发起请求即可
原理是提前告知URLSession对应设备的认证信息,即使设备的Digest响应格式略有偏差,Session也会尝试自动匹配使用该凭证。
二、更低层级设置Authentication头:自定义URLProtocol
如果上面的方法无效,可以通过自定义URLProtocol绕过URLSession对Auth头的限制,完全手动控制请求头:
- 实现自定义Protocol子类:
class CustomShellyAuthProtocol: URLProtocol { override class func canInit(with request: URLRequest) -> Bool { // 只处理Shelly设备的请求 return request.url?.host == "192.168.1.99" } override class func canonicalRequest(for request: URLRequest) -> URLRequest { return request } override func startLoading() { guard let url = request.url else { client?.urlProtocol(self, didFailWithError: NSError(domain: "CustomAuth", code: -1, userInfo: [NSLocalizedDescriptionKey: "无效URL"])) return } var mutableRequest = request // 填入你在Playground验证有效的Digest Auth头内容 let authHeader = "Digest username=\"你的用户名\", realm=\"shelly\", nonce=\"...\", uri=\"/relay/0?turn=on&timer=30\", response=\"...\"" mutableRequest.setValue(authHeader, forHTTPHeaderField: "Authorization") let task = URLSession.shared.dataTask(with: mutableRequest) { [weak self] data, response, error in guard let self = self else { return } if let error = error { self.client?.urlProtocol(self, didFailWithError: error) return } if let response = response { self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) } if let data = data { self.client?.urlProtocol(self, didLoad: data) } self.client?.urlProtocolDidFinishLoading(self) } task.resume() } override func stopLoading() {} }
- 在App启动时注册这个Protocol:
// 比如在AppDelegate的didFinishLaunchingWithOptions方法中 URLProtocol.registerClass(CustomShellyAuthProtocol.self)
这样所有指向Shelly设备的请求都会走自定义逻辑,你可以完全控制HTTP头的设置,不受URLSession的限制。
三、手动触发认证流程(针对设备响应不规范的情况)
既然httpbin能正常触发代理,说明Shelly设备的401响应可能不符合标准Digest格式,你可以在请求完成回调里手动检查响应,触发认证:
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { if let httpResponse = task.response as? HTTPURLResponse, httpResponse.statusCode == 401 { if let authHeader = httpResponse.allHeaderFields["WWW-Authenticate"] as? String, authHeader.starts(with: "Digest") { // 手动构造凭证并触发认证 let credential = URLCredential(user: "你的用户名", password: "你的密码", persistence: .forSession) task.authenticate(with: credential) { success, error in if success { task.resume() } } } } }
内容的提问来源于stack exchange,提问作者MindSpiker
相关产品推荐
相关产品推荐

