swift - NTLM 的 URLSession 的 401 挑战
问题描述
我正在使用带有委托的自定义 URLSession 来处理从我们的 NTLM 身份验证 Web api 收到的 401 挑战。我希望只收到第一个 api 调用的挑战,但是我调用的每个唯一端点都会收到一个挑战。有没有办法在每次调用时提供存储的凭据以避免每次都出现 401?我的基本会话设置如下。
let config = URLSessionConfiguration.default
let delegate = MySessionDelegate()
let session = URLSession(configuration: config, delegate: delegate, delegateQueue: nil)
解决方案
这是我在使用 NTLM 身份验证处理 API 时发现的解决方案。本质上,您需要将URLSessionConfiguration
实例的委托设置为 ViewController 的类(或任何子类NSObject
)并实现协议的func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
功能URLSessionDelegate
。
处理身份验证的方式是,当URLSession
发出请求时,它首先协商身份验证方法(请参阅NSURLProtectionSpace 身份验证方法常量),然后根据您选择的身份验证方法(在这种情况下,我们拒绝所有不是的NSURLAuthenticationMethodNTLM
),我们通过服务器。
以下代码使用 Swift 5.1 和 Xcode 11 进行了测试:
import Cocoa
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
class NTLMAuthentication: NSObject {
private let baseURL = "<insert base URL here>"
private let username: String
private let password: String
lazy private var session: URLSession = {
let config = URLSessionConfiguration.default
return URLSession(configuration: config, delegate: self, delegateQueue: nil)
}()
init(username: String, password: String) {
self.username = username
self.password = password
}
func authenticate() {
let url = URL(string: baseURL)
let request = URLRequest(url: url!)
let task = session.dataTask(with: request) { data, response, error in
guard error == nil else {
print("An error occurred")
return
}
let htmlData = String(bytes: data!, encoding: String.Encoding.utf8)
print(htmlData)
}
task.resume()
}
}
extension NTLMAuthentication : URLSessionDelegate {
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
print("got challenge")
print(challenge.protectionSpace.authenticationMethod)
guard challenge.previousFailureCount == 0 else {
print("too many failures")
completionHandler(.cancelAuthenticationChallenge, nil)
return
}
guard challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodNTLM else {
completionHandler(.rejectProtectionSpace, nil)
return
}
let credentials = URLCredential(user: self.username, password: self.password, persistence: .forSession)
completionHandler(.useCredential, credentials)
}
}
请注意,该行PlaygroundPage.current.needsIndefiniteExecution = true
是在 Playground 中运行此代码所必需的,否则代码在 Playground 终止之前将无法完成运行。
推荐阅读
- python - DjangoModelPermissions 和 DjangoObjectPermissions 有什么区别
- android - 分析控制台突出日志“InternalRequestSender outbound”
- powershell - 更改日志目录脚本 IIS
- node.js - 如何在 mongodb 上使用 findByIdAndUpdate?
- javascript - 如何在我的抽屉回声中放入文本下方的灰色反应导航线?
- wordpress - Wordpress 速度优化不起作用
- python - 如何使用 node-red 将 json 数据发送到烧瓶后端
- tensorflow - 如何在 TensorFlow 2.0 中调试 Keras?
- python - python列表循环中的前一个元素
- firebird - Firebird 错误:尝试更新只读数据库